|
| 1 | +# ADR 0009 — Integrate scientific-python-myst-theme via copier (committed files) |
| 2 | + |
| 3 | +Date: 2026-05-28 |
| 4 | +Status: Proposed |
| 5 | +Branch: lb/myst-migration |
| 6 | + |
| 7 | +## Context |
| 8 | + |
| 9 | +`scientific-python/scientific-python-myst-theme` provides shared MyST styling, |
| 10 | +config, plugins (`team-grid.mjs`), assets (logo, favicon, CSS), and a footer |
| 11 | +template. It is distributed as a **copier template**, not a pip/npm package: the |
| 12 | +intended consumption pattern is for a site's `myst.yml` to `extends:` a |
| 13 | +`config/scientific-python.yml` shipped in the template. |
| 14 | + |
| 15 | +The theme is brand-new (created 2026-05-18, 0 tagged releases), so upstream |
| 16 | +`main` HEAD is the only pinning target. We need a strategy for pulling theme |
| 17 | +files into `content/` that is reproducible on Netlify CI (`make html-all`), on a |
| 18 | +fresh `git clone` + `myst start`, and across maintainer machines. |
| 19 | + |
| 20 | +Constraint: MyST resolves `myst.yml` paths (`extends:`, `style:`, `logo:`, |
| 21 | +plugins) relative to the `myst.yml` location, which is `content/`. Theme files |
| 22 | +must land at predictable paths under `content/`. |
| 23 | + |
| 24 | +## Decision |
| 25 | + |
| 26 | +Adopt **committed theme files, vendored selectively from a copier render** (a |
| 27 | +refinement of Option 2). |
| 28 | + |
| 29 | +Upstream is a _scaffold-a-new-site_ template: a full `copier copy` renders |
| 30 | +`index.md`, `myst.yml`, `about.md`, `news.md`, `Makefile`, `.gitignore` |
| 31 | +alongside the theme infrastructure. Our `content/` already holds a populated |
| 32 | +site, so a direct copy would clobber `index.md`/`myst.yml` and add unwanted |
| 33 | +pages. We therefore render to a throwaway dir and copy in only the theme files. |
| 34 | + |
| 35 | +Implementation (as performed): |
| 36 | + |
| 37 | +- Render once to a temp dir via `pixi exec --spec copier copier copy --trust |
| 38 | +--defaults gh:scientific-python/scientific-python-myst-theme <tmp>` (copier is |
| 39 | + run ephemerally; it is not a project dependency). |
| 40 | +- Vendor only these into `content/`, committed: `config/scientific-python.yml`, |
| 41 | + `assets/css/scientific-python.css`, `assets/images/logo.svg`, |
| 42 | + `assets/images/favicon.ico`, `team-grid.mjs`, `footer.md`, and |
| 43 | + `.copier-answers.yml` (provenance: records upstream `_commit`). |
| 44 | +- Add `extends: config/scientific-python.yml` to `content/myst.yml`; `style` |
| 45 | + (`assets/css/scientific-python.css`) is inherited from it. No local style |
| 46 | + override: mystmd >=1.10 requires `style` to be a single string, and MyST does |
| 47 | + not bundle a stylesheet's CSS `@import`s, so theme CSS plus a separate local |
| 48 | + override file cannot coexist. The former local `custom.css` (a navbar Lato |
| 49 | + font tweak) was dropped. |
| 50 | +- Remove the previously duplicated top-level `assets/` (logo/favicon byte |
| 51 | + identical to theme copies); `content/assets/` is now the single source. |
| 52 | +- Do not add copier to the Netlify build or `make prepare`; CI consumes the |
| 53 | + committed files as-is. |
| 54 | + |
| 55 | +Update path: `copier update` cannot run cleanly (it conflicts on the diverged |
| 56 | +scaffold files), so updates are done by re-rendering to a temp dir and diffing |
| 57 | +the vendored files by hand. Revisit Option 3 once upstream separates "theme |
| 58 | +core" from "site scaffold" and cuts tagged releases. |
| 59 | + |
| 60 | +Known upstream gap: `config/scientific-python.yml` sets |
| 61 | +`primary_sidebar_footer: sidebar-footer.md`, which the template does not ship; the |
| 62 | +build emits a non-fatal warning. To be raised upstream, not stubbed. |
| 63 | + |
| 64 | +`external-content/cookie` is unaffected (ADR 0004): it is a separately-built |
| 65 | +Jekyll site overlaid on `public/development/`, whereas the theme is config plus |
| 66 | +assets consumed by the MyST build itself. |
| 67 | + |
| 68 | +## Options considered |
| 69 | + |
| 70 | +1. **Git submodule** (mirror cookie) — introduces transient copied files, dual |
| 71 | + source of truth, and a copy step on every `myst start`; a copier template is |
| 72 | + not a runnable artifact, so a source submodule is a structural mismatch. |
| 73 | +2. **Copier copy, committed files** (chosen) — zero new build-time deps, |
| 74 | + `myst start` works on fresh clone. Tradeoff: manual update cadence, drift risk. |
| 75 | +3. **Scheduled GitHub Action + copier update** — cron PR on diff; automatic but |
| 76 | + adds a workflow and auto-PR conflict maintenance. Premature with no releases. |
| 77 | +4. **Pre-commit hook running copier update** — slows every commit, doesn't help |
| 78 | + CI on fresh checkout, surprises contributors without copier. Rejected. |
| 79 | +5. **Rethink cookie integration** — cookie's Ruby toolchain and release tags keep |
| 80 | + submodule + Makefile right for it; this decision does not alter ADR 0004. |
| 81 | + |
| 82 | +## Consequences |
| 83 | + |
| 84 | +- `netlify.toml` and `make prepare` are unchanged; `make html-all` (the |
| 85 | + Netlify build path) works because all theme files are on disk after `git |
| 86 | +clone`. Fresh clone `git clone && cd content && myst start` needs no setup. |
| 87 | +- `pixi.toml` `build`/`serve` tasks (local-only, git-excluded) were fixed to run |
| 88 | + with `cwd = content`. |
| 89 | +- Manual updates risk upstream drift; pin `.copier-answers.yml` to the first |
| 90 | + tagged release when available, and revisit Option 3 then. |
0 commit comments