Upgrade the Starter Pack¶
This is a tested, exhaustive prompt for upgrading the Docs Starter Pack installation in a repo that employs standard docs-build logic. I.e., running make html and co. must work from the docs/ directory.
General notes:
Format (rST/MyST) agnostic.
Strict instructions to never lose any repo-specific config.
Presents a report of upstream changes and local customizations.
Provides a plan of action before changing anything, allowing for adjustments.
Linting and checks:
Includes instructions for working around current
valerules bugs and limitations (to be updated when fixed rule set available).Similarly, workarounds are included for
pymarkdownlint, which has no MyST support.Final checklist requires passing all checks and lints; remove any (except
make html) that you don’t want to deal with during the upgrade.Requires the use of
ruffandblack(Python linter and formatter) onconf.py; this is stricter than what upstream Starter Pack uses. Remove if not desired.
# AI Instructions: Upgrade the Sphinx Docs Starter Pack
## Task
Use these instructions to upgrade a Canonical documentation repository from one
version of the [Sphinx Docs Starter Pack](https://github.com/canonical/sphinx-docs-starter-pack)
(SP) to the latest available version. The instructions are repo-agnostic; adapt
specifics to the repo at hand.
Study the instructions and linked Starter Pack resources carefully, and proceed when ready.
## Markup format
A docs set may use MyST Markdown, reStructuredText (rST), or a combination of
both. These instructions are format-agnostic. Where markup syntax appears in
examples or grep patterns, MyST notation is used, but the same guidance applies
to rST equivalents:
| Element | MyST | rST |
|---|---|---|
| Role | `` {role}`content` `` | `` :role:`content` `` |
| Directive | `` ```{name} `` (fenced block) | `.. name::` |
**When editing any source file, always use the markup format that file already
uses.** When grepping for directive or role usage, search for both patterns.
---
## Pre-flight
1. Identify the current SP version (`docs/.sphinx/version`).
2. Identify the latest SP version from the upstream repo or its changelog.
3. Run `docs/.sphinx/update_sp.py` and capture its output — it provides a
diff-based starting point but does not cover everything.
4. Read the upstream upgrade guide:
https://canonical-starter-pack.readthedocs-hosted.com/stable/how-to/update-starter-packs/new-starter-pack/
5. Study the upstream `conf.py`, `Makefile`, `requirements.txt`, and
`.github/workflows/` to understand what changed between versions.
6. Catalogue all custom content in the repo: custom extensions, custom roles,
`extlinks`, `intersphinx_mapping`, any custom Python in `conf.py`, custom
Makefile targets, etc. Document these explicitly before proceeding — they
must all survive the upgrade.
7. **IMPORTANT:** Before starting with implementation -- before touching
anything -- provide a complete plan of action, and ask for explicit
confirmation. List all planned modifications, additions, removals, and any
other identified steps. Structure your plan as follows:
- New things in upstream SP
- Custom local settings to be preserved
- Questions that need user decision
---
## Analysis: what to check in each file
### `requirements.txt`
- SP v1.4+ uses **atomic extensions** instead of `canonical-sphinx[full]`.
The full list of upstream-managed packages lives in the upstream
`requirements.txt`; replicate structure exactly.
- **Audit which extensions are actually used in the docs set before deciding
what to keep.** Do not assume an extension is used just because it is in
`requirements.txt`. Do not assume it is unused just because grep finds no
directive usage — some extensions provide roles (`:pkg:`, `:lpsrc:`,
`:spellexception:`, etc.) that may be used widely but are easy to miss.
- Grep for: directive names (e.g. `{terminal}` / `.. terminal::`, `{mermaid}` /
`.. mermaid::`, `{tabs}` / `.. tabs::`), roles (`` {pkg}` `` / `:pkg:`,
`` {lpsrc}` `` / `:lpsrc:`, `` {spellexception}` `` / `:spellexception:`,
etc.), and intersphinx references (`{external+...}` / `:external+...:`).
- Check for SVG images — they require `sphinxcontrib.cairosvgconverter`.
- Keep project-specific packages in a clearly commented section at the bottom.
- **`sphinx-markdown-builder`**: this package is a *transitive* dependency of
`sphinx-llm` (used internally for `llms.txt` generation). It does **not**
need to appear in `requirements.txt` explicitly, but its presence in the venv
means `_build/` will contain generated `.md` files. Exclude `_build` from
`CHECK_PATH` (see Makefile section) to prevent Vale false positives.
### `conf.py`
- **Copyright**: SP v1.4+ uses `f"{datetime.date.today().year}, Canonical Ltd"`.
- **Extensions list**: Sort upstream extensions first; project-specific below
with a comment separator. See "Known gotchas" below for `canonical_sphinx`
loading behaviour — certain extensions must be listed explicitly even if
`canonical_sphinx` nominally loads them.
- **`exclude_patterns`**: Must include `".venv"` (see "Known gotchas").
- **`rst_prolog`**: Upstream may add new substitution roles (`h2`, `woke-ignore`,
`vale-ignore`); add them without removing existing custom ones.
- **`ogp_image`**: Upstream default is no custom image. Remove if the repo has
no specific need; ask the user if unsure.
- **`sitemap_url_scheme`**: Upstream default is no custom scheme. Remove unless
the repo has a specific reason for it.
- **`redirects` / `rediraffe_redirects`**: `sphinx_reredirects` used `redirects = {}`;
`sphinx_rerediraffe` uses `rediraffe_redirects`. See "Known gotchas".
- **`extlinks`**, **`intersphinx_mapping`**, **custom roles/functions**: These are
always repo-specific. Never remove them without explicit confirmation.
Restore immediately if a build reveals they are needed.
- **Python formatting**: Run `ruff` and `black` on `conf.py` after any edits.
Many repos enforce stricter formatting than upstream. If not available
globally, run from the venv: `.venv/bin/ruff check docs/conf.py` and
`.venv/bin/black docs/conf.py`.
### `Makefile`
- SP v1.4+ renames variables (`VENVDIR→DOCS_VENVDIR`, `TARGET→CHECK_PATH`,
`SPHINXDIR→SPHINX_DIR`, etc.) and removes the `allmetrics` target.
- `html` target: `-W` becomes `--fail-on-warning --keep-going`.
- If the repo's `Makefile` has no custom targets, replace it wholesale with
the upstream version. If it does have custom targets, merge carefully.
- **`CHECK_PATH`**: The upstream default `$(filter-out $(DOCS_VENVDIR),$(wildcard *))`
includes `_build/`. If the repo uses `sphinx-markdown-builder` or any other
builder that outputs `.md` files under `_build/`, Vale will flag errors in
those generated files. Exclude `_build` explicitly:
```make
CHECK_PATH ?= $(filter-out $(DOCS_VENVDIR) _build,$(wildcard *))
```
### `.gitignore`
- Venv path changed from `.sphinx/venv/` to `.venv/`; update accordingly.
### GitHub Actions (`.github/workflows/`)
- **Audit all workflow files**, not just `automatic-doc-checks.yml`. Repos
commonly also have `check-removed-urls.yml` and `markdown-style-checks.yml`.
- **`automatic-doc-checks.yml`**: add `fetch-depth: 0` to the checkout step
(required by `sphinx_last_updated_by_git`); scope the `push`/`pull_request`
trigger to `docs/**` if not already done.
- **`check-removed-urls.yml`**: the "Build docs" step may hard-code the old
venv activation path (`. .sphinx/venv/bin/activate`). With SP v1.5+, the
venv moves to `.venv/` and `make html` activates it automatically; remove
any explicit `make install` + `. .sphinx/venv/bin/activate` lines and
replace the entire step with just `make html`.
- **`markdown-style-checks.yml`**: update the runner version to match upstream
(e.g. `ubuntu-22.04` → `ubuntu-24.04`).
- Diff every workflow against its upstream counterpart carefully; preserve any
repo-specific environment variables, secrets, or deployment steps.
### `.readthedocs.yaml`
- Compare `os:` and `python:` versions against upstream — the repo may
intentionally run a newer OS/Python than upstream specifies; confirm before
changing.
- The `post_checkout` cancel-on-no-diff block and `sphinx:` / `formats:` /
`python:` sections are otherwise stable across SP versions; verify they
match upstream structure.
- Do not overlook this file — it is easy to miss because it lives at the repo
root rather than under `docs/`.
### `.sphinx/` update files
After running `update_sp.py`, copy the resulting updated files:
- `update_sp.py` → `docs/.sphinx/update_sp.py`
- `get_vale_conf.py` → `docs/.sphinx/get_vale_conf.py`
- Update `docs/.sphinx/version` to the new SP version string.
---
## Known gotchas (version-specific)
### `canonical_sphinx` v0.6: explicit extension loading required
`canonical_sphinx` v0.6 auto-loads several extensions using **old module names**
that no longer match the installed package names. This means extensions that
appear to be loaded by `canonical_sphinx` may silently fail to load.
**Always list these explicitly in `extensions`**, regardless of what
`canonical_sphinx` claims to load:
- `sphinx_roles` — provides `:spellexception:` and other roles
- `sphinx_terminal` — provides the `{terminal}` directive
Extensions that `canonical_sphinx` v0.6 does load correctly:
`myst_parser`, `sphinx_design`, `sphinx_tabs.tabs`, `sphinx_copybutton`,
`sphinxext.opengraph`, `sphinxcontrib.jquery`, `notfound.extension`.
If the build emits "Unknown directive type" or "Unknown role" for anything
that was supposedly provided by `canonical_sphinx`, add it explicitly.
### `sphinx-terminal` v1.0.x: breaking directive syntax change
The `terminal` directive syntax changed in `sphinx-terminal` v1.0 (applies to
both MyST and rST usage):
| | Old (pre-1.0) | New (1.0+) |
|---|---|---|
| Command | `:input: COMMAND` option | First non-empty line of body |
| Output | Entire body | Body after blank line following command |
| Separator | None needed | Blank line between command and output |
Example migration:
```
# Old
{terminal}
:input: sudo apt install foo
Package installed.
# New
{terminal}
sudo apt install foo
Package installed.
```
Valid options in v1.0+: `class`, `copy`, `dir`, `host`, `output-only`,
`scroll`, `user`. The `input` option is gone.
Scan all source files for `terminal` directives (`` {terminal} `` in MyST,
`.. terminal::` in rST) that use the old `:input:` option and migrate them.
A Python script is the most reliable approach for bulk migration.
### `sphinx_rerediraffe`: no-redirects warning breaks build
`sphinx_rerediraffe` (which replaced `sphinx_reredirects`) emits a `WARNING`
if loaded when no `rediraffe_redirects` are configured. With
`--fail-on-warning`, this breaks the build.
**Rule**: Do not add `sphinx_rerediraffe` to `extensions` unless the repo
actually has redirects to configure. Instead, leave it commented out:
```python
# Uncomment and populate when redirects are needed:
# rediraffe_redirects = {}
```
**If the repo has existing redirects** defined in `redirects = {}` (the
`sphinx_reredirects` format), they must be converted — the two extensions use
different mapping conventions:
| | `sphinx_reredirects` | `sphinx_rerediraffe` |
|---|---|---|
| Variable | `redirects` | `rediraffe_redirects` |
| Key | source path (no extension) | source file (with extension, e.g. `.md` or `.rst`) |
| Value | target URL or HTML path | target source file (with extension) |
Convert every entry, add `sphinx_rerediraffe` to `extensions`, and verify the
redirects still resolve correctly in a build. Do not silently drop existing
redirects.
### `.venv` inside `docs/`: Sphinx scans venv templates
When the venv lives at `docs/.venv/` (SP v1.4+ default), Sphinx's source
scanner will descend into it and attempt to parse Jinja2/autosummary template
files as RST, producing ~90 warnings that fail the build under `--fail-on-warning`.
Fix: add `".venv"` to `exclude_patterns` in `conf.py`:
```python
exclude_patterns = ["_build", "**.ipynb_checkpoints", ".venv"]
```
---
## Vale: known false positives
After the upgrade, run `make vale` and expect to address pre-existing content
errors (unrelated to the SP upgrade itself). Several Canonical Vale rules produce
non-obvious false positives:
- **`Canonical.003`** (`Ubuntu \d?[02468]\.04(?! LTS)`): any even-year Ubuntu
LTS release number must be followed by ` LTS`. Add the suffix wherever missing.
Common oversights: Ubuntu 22.04, Ubuntu 24.04, Ubuntu 26.04 without "LTS".
Known limitation: the rule does not handle point releases like "Ubuntu 24.04.4
LTS" (the lookahead sees `.4` not ` LTS`). Use vale suppress comments for
point-release paragraphs: `<!-- vale Canonical.003-Ubuntu-names-versions = NO -->`.
- **`Canonical.004`** (`(?<=Ubuntu )Pro`, nonword, ignorecase): matches "pro"
anywhere in a word after "Ubuntu". False-positive triggers include:
"Ubuntu **pro**ject", "Ubuntu **pro**posed", "Ubuntu **pro**vides", etc.
Fix by rephrasing so the word following "Ubuntu" does not start with "pro".
Cannot be suppressed with `TokenIgnores` when the adjacency is created by
Markdown link-stripping (e.g. `[...ubuntu](#anchor) process`); rephrase instead.
- **`Canonical.004` for `launchpad`**: "launchpad" in URLs, inline code spans
(`` `https://bugs.launchpad.net/...` ``), and `` [`text`](url) `` backtick-link
combos triggers this rule even with `TokenIgnores = (launchpad\.net)`. Vale only
applies `TokenIgnores` once per token match, so when two `launchpad.net` links
appear in the same parsed sentence the second one still fires. These are
unresolvable false positives with current Vale tokenization; accept or suppress.
- **`_build/` false positives**: if `sphinx-markdown-builder` is in use,
`_build/` contains generated `.md` files that may include content from
`reuse/` directories not directly scanned by Vale. These errors are
misleading. The fix is to exclude `_build` from `CHECK_PATH` (see Makefile
section above). Verify errors are build artifacts before editing source.
### Recommended vale.ini TokenIgnores (for `[*.md]` section)
```ini
TokenIgnores = (https?://\S+)
TokenIgnores = (launchpad\.net)
TokenIgnores = (`[^`\n]+`)
TokenIgnores = (launchpadlib)
TokenIgnores = (?i:Ubuntu pro\w+)
```
### Recommended vale.ini TokenIgnores (for `[*.rst]` section)
```ini
TokenIgnores = (\`[^\`\n]+\`)
TokenIgnores = (launchpad\.net)
TokenIgnores = (launchpadlib)
TokenIgnores = (?i:Ubuntu pro\w+)
```
## pymarkdownlnt: known rule conflicts
- **MD022 (`blanks-around-headings`)**: the project style can use 2 blank lines above
headings (not 1), and MyST anchors `(name)=\n# Heading` produce 0 blank lines
above the heading. Both patterns conflict with `lines_above_heading: 1`. Disable
the rule entirely: set `"blanks-around-headings": {"enabled": false}` in
`.pymarkdown.json`.
- **MD014 (`commands-show-output`)**: fires on any `$ command` shell block that
doesn't also show output. Projects that conventionally use `$` to indicate shell
prompts should disable this rule: `"commands-show-output": {"enabled": false}`.
## Post-upgrade spelling wordlist
After upgrading, run `make spelling` and add project-specific technical terms to
`docs/.custom_wordlist.txt`. Common terms in ubuntu-project-docs that are not in
the Canonical accept list include:
`armhf`, `Dreamwidth`, `ics`, `motu`, `openstack`, `openssh`, `pbuilder`,
`reportbug`, `rmadison`, `rustc`, `Subteam`, `wnpp`
(The Canonical list already includes `MOTUs`, `Snapcraft`, etc.)
---
## Post-upgrade checklist
Do **not** commit anything before this checklist is complete!
- [ ] `make html` — clean build, zero warnings
- [ ] `make vale` — zero errors (see false-positive notes above)
- [ ] `make spelling` — zero errors (add new terms to `.custom_wordlist.txt`)
- [ ] `make woke` — zero errors
- [ ] `make lint-md` — zero errors
- [ ] `ruff` and `black` pass on `conf.py` (run from `.venv/bin/` if not available globally)
- [ ] All custom extensions, roles, and settings verified intact
- [ ] `docs/.sphinx/version` reflects the new SP version
- [ ] `python docs/.sphinx/update_sp.py` reports no pending updates
---
## Commit guidance
Squash all upgrade work into a single atomic commit. The commit message should:
- State the version transition in the subject line:
`Upgrade Sphinx Docs Starter Pack from vX.Y to vZ.W`
- Use bullet groups in the body: one group for SP structural changes, one for
content/Vale fixes if any.
- Be specific but not exhaustive — name the files and changes, not the
reasoning.
- Include the `Co-authored-by` trailer if applicable.
---
## When to ask the user
Before implementing, ask for a decision on any of the following:
- Settings present in this repo but absent in upstream (e.g. `ogp_image`,
`sitemap_url_scheme`, custom `extlinks` keys): keep, remove, or modify?
- Whether to adopt upstream defaults for things that diverge
(e.g. copyright format, venv location, redirect extension).
- Any case where upstream and repo custom content are in direct conflict.
Do not remove **any** custom setting without explicit confirmation.