A Quarto extension that generates "Reproducible Environment" buttons for launching one-click, pre-configured JupyterLab environments on Onyxia platforms (such as the UN Global Platform).
Version: 0.1.4 Requirements: Quarto >= 1.3.0
This extension enables handbook authors to add reproducibility to their chapters by including a single YAML configuration. When enabled, a button is automatically generated that readers can click to launch a fully-configured computing environment with all code, data, and dependencies ready to use.
For Readers: One click launches a JupyterLab session in 5-15 seconds
For Authors: Add reproducible: enabled: true to your YAML frontmatter
Step 1: Install the extension
Install in your Quarto project:
quarto add cslovell/reproducibleOr install from a local directory:
quarto add path/to/reproducibleStep 2: Activate the filter
Add the filter to your project's _quarto.yml:
project:
type: book # or website, default, etc.
filters:
- cslovell/reproducible
format:
html:
theme: cosmoStep 3: Enable in your chapters
Add this to your chapter's YAML frontmatter:
---
title: "Your Chapter Title"
reproducible:
enabled: true
---
# Your content here...When rendered to HTML, a notice will appear at the top of the chapter with a button to launch the reproducible environment.
The extension supports three levels of configuration with a clear precedence hierarchy:
Precedence: Document > Project > Extension defaults
The extension provides sensible defaults for the UN Global Platform deployment. No configuration is required for basic usage.
Default settings include:
- Onyxia URL:
https://datalab.officialstatistics.org - Helm catalog:
capacity - Helm chart:
eostat-rstudio - Button text: "Launch Environment"
- Notice style: Full (title + button + metadata)
- Branding: Onyxia colors (orange/black)
Configure deployment-wide settings in your project's _quarto.yml file using the reproducible-config namespace. This is useful for:
- Deploying to a different Onyxia instance
- Customizing branding and UI text
- Setting different default values
Example: Custom Onyxia Instance
# _quarto.yml
project:
type: book
filters:
- cslovell/reproducible
reproducible-config:
onyxia:
base-url: "https://onyxia.example.org"
catalog: "custom-catalog"
chart: "analysis-environment"
ui:
button-text: "Launch Analysis"
notice-style: "minimal"Example: Custom Branding
reproducible-config:
branding:
primary-color: "#0066cc"
text-color: "#333333"
background-color: "#f5f5f5"
ui:
notice-title: "Interactive Environment Available"
session-duration: "4h"Configure chapter-specific settings in individual .qmd files using the reproducible namespace.
Minimal Configuration:
---
title: "Chapter Title"
reproducible:
enabled: true
---Full Configuration:
---
title: "Chapter Title"
reproducible:
enabled: true
# Resource configuration
tier: heavy # light | medium | heavy | gpu
image-flavor: gpu # base | gpu
data-snapshot: v1.2.3 # Version tag
storage-size: "50Gi" # Storage request
estimated-runtime: "45 minutes" # Display text
# UI overrides (optional)
button-text: "Launch Tutorial" # Override button text
notice-style: "button-only" # full | minimal | button-only
---Configure in _quarto.yml under reproducible-config.onyxia:
| Setting | Type | Default | Description |
|---|---|---|---|
base-url |
string | https://datalab.officialstatistics.org |
Onyxia instance URL (no trailing slash) |
catalog |
string | handbook |
Helm chart catalog name |
chart |
string | chapter-session |
Helm chart name |
auto-launch |
boolean | true |
Pre-fill autoLaunch parameter |
Configure in _quarto.yml under reproducible-config.ui:
| Setting | Type | Default | Description |
|---|---|---|---|
button-text |
string | Launch Environment |
Text displayed on button |
notice-title |
string | Reproducible Environment Available |
Title of notice wrapper |
notice-style |
enum | full |
Display style (see below) |
session-duration |
string | 2h |
Session expiration display text |
show-runtime |
boolean | true |
Show/hide estimated runtime |
Notice Styles:
full- Title + button + metadata (default)minimal- Button + metadata, no titlebutton-only- Just the button, no wrapper or metadata
Configure in _quarto.yml under reproducible-config.branding:
| Setting | Type | Default | Description |
|---|---|---|---|
primary-color |
string | rgb(255, 86, 44) |
Button and border color |
text-color |
string | rgb(44, 50, 63) |
Text color in notice |
background-color |
string | #fafafa |
Notice background color |
Configure in _quarto.yml under reproducible-config.tier-labels:
These are display-only labels. Actual resource allocations are defined in the Helm chart.
| Tier | Default Label |
|---|---|
light |
Light (2 CPU, 8GB RAM) |
medium |
Medium (6 CPU, 24GB RAM) |
heavy |
Heavy (10 CPU, 48GB RAM) |
gpu |
GPU (8 CPU, 32GB RAM, 1 GPU) |
Configure in _quarto.yml under reproducible-config.defaults:
| Setting | Default | Description |
|---|---|---|
tier |
medium |
Default resource tier |
image-flavor |
base |
Default container image |
data-snapshot |
latest |
Default version tag |
storage-size |
20Gi |
Default storage request |
estimated-runtime |
Unknown |
Default runtime estimate |
Configure in .qmd frontmatter under reproducible:
| Setting | Type | Required | Description |
|---|---|---|---|
enabled |
boolean | Yes | Show button or not |
tier |
enum | No | Resource tier (light, medium, heavy, gpu) |
image-flavor |
string | No | Container image variant |
data-snapshot |
string | No | Chapter version/snapshot |
storage-size |
string | No | Storage request (e.g., "50Gi") |
estimated-runtime |
string | No | Runtime estimate display |
chapter-name |
string | No | Override filename-based name |
button-text |
string | No | Override button text for this chapter |
notice-style |
enum | No | Override notice style for this chapter |
Goal: Add reproducibility to chapters using default UN Global Platform settings.
Setup (_quarto.yml):
project:
type: book
filters:
- cslovell/reproducibleChapter (chapter.qmd):
---
title: "My Chapter"
reproducible:
enabled: true
---Result: Full notice with default button and Onyxia orange branding.
Goal: Deploy handbook to a different Onyxia instance with custom branding.
Setup (_quarto.yml):
project:
type: book
filters:
- cslovell/reproducible
reproducible-config:
onyxia:
base-url: "https://onyxia.stats-france.org"
catalog: "formation"
chart: "jupyter-session"
branding:
primary-color: "#003366"
ui:
button-text: "Lancer l'environnement"
notice-title: "Environnement reproductible disponible"Chapter:
---
title: "Chapitre 1"
reproducible:
enabled: true
---Result: Button points to French instance with French text and custom blue color.
Goal: Use full notices for main chapters, minimal style for appendices.
Setup (_quarto.yml):
reproducible-config:
ui:
notice-style: "full" # DefaultMain Chapter:
---
title: "Core Analysis"
reproducible:
enabled: true
# Uses default "full" style
---Appendix:
---
title: "Appendix: Code Examples"
reproducible:
enabled: true
notice-style: "button-only" # Override for minimal appearance
---Goal: Assign appropriate compute resources to different chapters.
Theory Chapter:
---
title: "Introduction to Remote Sensing"
reproducible:
enabled: true
tier: light
estimated-runtime: "5 minutes"
---Analysis Chapter:
---
title: "Crop Classification with Random Forest"
reproducible:
enabled: true
tier: heavy
estimated-runtime: "45 minutes"
storage-size: "50Gi"
---Deep Learning Chapter:
---
title: "Yield Prediction with Neural Networks"
reproducible:
enabled: true
tier: gpu
image-flavor: gpu
estimated-runtime: "2 hours"
storage-size: "100Gi"
---The extension generates Onyxia deep-link URLs that pre-fill all launch parameters:
https://datalab.officialstatistics.org/launcher/handbook/chapter-session
?autoLaunch=true
&tier=«medium»
&imageFlavor=«base»
&chapter.name=«chapter-name»
&chapter.version=«v1-0-0»
&chapter.storageSize=«20Gi»
URL Encoding Rules:
- Numbers: Pass as-is (e.g.,
5) - Booleans: Pass as-is (e.g.,
true) - Strings: URL-encoded and wrapped in
«»(e.g.,«medium»)
The extension determines the chapter name using this priority:
- Explicit override:
chapter-namein metadata - Filename: Extract from
.qmdfilename (e.g.,ct_chile.qmd→ct_chile) - Title: Sanitized version of document title
- Fallback:
unknown-chapter
Version strings are normalized for URL compatibility:
- Dots converted to hyphens:
v1.2.3→v1-2-3 - Special characters URL-encoded
Possible causes:
-
Feature not enabled
- Solution: Ensure
reproducible: enabled: truein frontmatter
- Solution: Ensure
-
Rendering to non-HTML format
- Solution: Button only appears in HTML output (not PDF/DOCX)
- Check:
quarto render --to html
-
Extension not installed
- Solution: Run
quarto add cslovell/reproducible - Verify: Check
_extensions/reproducible/directory exists
- Solution: Run
-
Syntax error in YAML
- Solution: Validate YAML formatting (proper indentation, colons, etc.)
- Check: Quarto render output for error messages
Cause: Quarto is trying to execute R code, but R packages aren't installed.
Solution: Add engine: markdown to your YAML frontmatter:
---
title: "Chapter Title"
engine: markdown
reproducible:
enabled: true
---This tells Quarto to treat code blocks as literal markdown, not executable code.
Symptom: Console shows "Invalid tier: X, using 'medium'"
Cause: Tier value is not one of: light, medium, heavy, gpu
Solution: Check spelling and use valid tier names:
reproducible:
enabled: true
tier: heavy # Correct
# tier: super-heavy # InvalidSymptom: Button shows < instead of <
Cause: Special characters in button text
Solution: Quarto automatically escapes HTML. This is expected and safe. If you need literal HTML, consider using a different approach or simplified text.
This is correct behavior. Onyxia requires string parameters to be wrapped in «» delimiters. The URL is properly formatted.
Example correct URL:
...?tier=«medium»&chapter.name=«ct_chile»
Possible causes:
-
Incorrect namespace
- Project-level: Use
reproducible-configin_quarto.yml - Document-level: Use
reproduciblein.qmdfrontmatter
- Project-level: Use
-
Indentation error
- YAML requires exact 2-space indentation
- Check: No tabs, proper nesting
-
Quarto not finding _quarto.yml
- Ensure
_quarto.ymlis in project root - Check: Render from correct directory
- Ensure
Debug steps:
-
Add debug output to check what Quarto sees:
quarto render chapter.qmd --log-level debug
-
Check generated HTML:
grep "reproducible" chapter.html -
Verify extension version:
cat _extensions/reproducible/_extension.yml | grep version
Cause: Quarto caches extension metadata
Solution:
-
Remove extension cache:
rm -rf .quarto/
-
Re-render:
quarto render
-
For persistent issues, reinstall extension:
quarto remove reproducible quarto add cslovell/reproducible
The extension includes a comprehensive Playwright E2E test suite with 200+ tests across multiple browsers:
# Run full test suite (Chromium, Firefox, WebKit)
npm test
# Run tests in UI mode (interactive)
npm run test:ui
# Run tests in debug mode
npm run test:debugTest Coverage:
- ✅ Filter Registration (11 tests) - Verifies filter loads and executes
- ✅ URL Generation (8 tests) - Deep-link parameter encoding
- ✅ Configuration (11 tests) - Multi-level config precedence
- ✅ Notice Styles (9 tests) - Full, minimal, button-only variants
- ✅ Metadata Extraction (15 tests) - Tier, flavor, snapshot handling
- ✅ Error Handling (9 tests) - Graceful degradation
- ✅ Accessibility (6 tests) - ARIA, keyboard nav, contrast
- ✅ Visual Regression (4 tests) - Screenshot comparison
For rapid feedback during development:
cd reproducible/
bash tests/bash/test.shThis runs 10 quick smoke tests covering basic scenarios.
Render the example document:
quarto render example.qmd
open example.htmlVerify:
- Button appears at top of page
- Button link is correct
- Styling looks appropriate
- Text is correct
-
View generated HTML:
quarto render chapter.qmd cat chapter.html | grep -A 10 "reproducible"
-
Check metadata merging:
quarto inspect chapter.qmd
-
Enable Lua debugging: Add to your Lua filter:
quarto.log.output(config) -- Dump config to console
-
Test with minimal example: Create a minimal test file to isolate issues
This extension is Component #4 of a 5-component reproducible analysis system:
Build-Time:
- Portable CI Pipeline (Dagger) - Builds images and packages data
- Curated Compute Images - Pre-built Docker images
- OCI Data Artifacts - Content-hashed data snapshots
Run-Time: 4. "Reproduce" Button (This Extension) - User's entrypoint 5. "Chapter Session" Helm Chart - Kubernetes deployment
- Reads
reproducible:andreproducible-config:metadata - Generates Onyxia deep-link URL with encoded parameters
- Injects HTML button/notice into rendered document
- Validates configuration with warnings for invalid values
- Build Docker images (done by CI pipeline)
- Package data artifacts (done by CI pipeline)
- Deploy Kubernetes sessions (done by Helm chart)
- Hard-code infrastructure details (uses semantic tier names)
Decoupled Architecture:
- Frontend (Quarto) knows only semantic names (
tier: "heavy") - Backend (Helm chart) translates to actual resources (10 CPU, 48GB RAM)
- Infrastructure changes don't require re-rendering the handbook
Backward Compatibility:
- Simple
enabled: trueconfigs continue to work - New configuration features are opt-in
- Sensible defaults for all values
Fail Gracefully:
- Invalid config → warning + fallback to default
- Missing config → use extension defaults
- Never fail rendering due to config issues
Bug Fixes:
- Fixed critical bug: Missing
return {{Meta = Meta}}in Lua filter that prevented filter execution - Updated documentation filter references from
- reproducibleto- cslovell/reproducible - Fixed README installation instructions
Testing:
- Added 11 new regression tests for filter registration and validation
- Added source code validation tests to prevent filter export bugs
- Total test coverage: 210+ tests across 3 browsers (Chromium, Firefox, WebKit)
Initial Release:
- Comprehensive configuration system
- Three notice styles (full, minimal, button-only)
- Configurable Onyxia deployment (URL, catalog, chart)
- Customizable button text and branding
- Project-level configuration via
reproducible-config - Document-level UI overrides
- Complete Playwright E2E test suite
- Accessibility and visual regression tests
This extension is part of the UN Handbook reproducible analysis system. For issues or contributions:
- Report issues on GitHub
- Follow the test-driven development approach
- Ensure all tests pass before submitting
- Update documentation for new features
[License TBD]
- UN Handbook Team
- Based on research of onyxia-quarto and quarto-open-social-comments extensions
For questions about this extension, consult the documentation files:
CLAUDE.md- Project notes and architectureUSER_JOURNEY.md- User experience specificationsIMPLEMENTATION_APPROACH.md- Technical design detailsEXTENSION_LEARNINGS.md- Best practicesTEST_STRATEGY.md- Testing approach