diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..1a84455 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,28 @@ +name: Deploy Docs + +on: + push: + branches: [main] + paths: + - "docs/**" + - "mkdocs.yml" + - ".github/workflows/docs.yml" + workflow_dispatch: + +concurrency: + group: docs-deploy + cancel-in-progress: true + +permissions: + contents: write + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + - uses: extractions/setup-just@v4 + - uses: astral-sh/setup-uv@v8.2.0 + - run: just docs-deploy diff --git a/.readthedocs.yaml b/.readthedocs.yaml deleted file mode 100644 index 571df87..0000000 --- a/.readthedocs.yaml +++ /dev/null @@ -1,13 +0,0 @@ -version: 2 - -build: - os: "ubuntu-22.04" - tools: - python: "3.10" - -python: - install: - - requirements: docs/requirements.txt - -mkdocs: - configuration: mkdocs.yml diff --git a/Justfile b/Justfile index 0853917..6783ea3 100644 --- a/Justfile +++ b/Justfile @@ -27,3 +27,8 @@ publish: uv version $GITHUB_REF_NAME uv build uv publish --token $PYPI_TOKEN + +# Force-pushes built site to gh-pages; CI runs this on push to main. +# Manual invocation from a stale checkout will roll the live site back. +docs-deploy: + uvx --with-requirements docs/requirements.txt mkdocs gh-deploy --force diff --git a/README.md b/README.md index e248639..e8ddcad 100644 --- a/README.md +++ b/README.md @@ -19,11 +19,11 @@ With `lite-bootstrap`, you receive an application with lightweight built-in supp Those instruments can be bootstrapped for: -- [LiteStar](https://lite-bootstrap.readthedocs.io/integrations/litestar) -- [FastStream](https://lite-bootstrap.readthedocs.io/integrations/faststream) -- [FastAPI](https://lite-bootstrap.readthedocs.io/integrations/fastapi) -- [FastMCP](https://lite-bootstrap.readthedocs.io/integrations/fastmcp) -- [services and scripts without frameworks](https://lite-bootstrap.readthedocs.io/integrations/free) +- [LiteStar](https://lite-bootstrap.modern-python.org/integrations/litestar) +- [FastStream](https://lite-bootstrap.modern-python.org/integrations/faststream) +- [FastAPI](https://lite-bootstrap.modern-python.org/integrations/fastapi) +- [FastMCP](https://lite-bootstrap.modern-python.org/integrations/fastmcp) +- [services and scripts without frameworks](https://lite-bootstrap.modern-python.org/integrations/free) ## Lifecycle constraints @@ -47,7 +47,7 @@ Browse the full list of templates and libraries in [`modern-python`](https://github.com/modern-python) — see the org profile for the categorized index. -## 📚 [Documentation](https://lite-bootstrap.readthedocs.io) +## 📚 [Documentation](https://lite-bootstrap.modern-python.org) ## 📦 [PyPi](https://pypi.org/project/lite-bootstrap) diff --git a/docs/CNAME b/docs/CNAME new file mode 100644 index 0000000..767f17d --- /dev/null +++ b/docs/CNAME @@ -0,0 +1 @@ +lite-bootstrap.modern-python.org diff --git a/mkdocs.yml b/mkdocs.yml index 78b970f..bdb634c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,4 +1,5 @@ site_name: lite-bootstrap +site_url: https://lite-bootstrap.modern-python.org repo_url: https://github.com/modern-python/lite-bootstrap docs_dir: docs edit_uri: edit/main/docs/ diff --git a/planning/plans/2026-06-09-mkdocs-github-actions-plan.md b/planning/plans/2026-06-09-mkdocs-github-actions-plan.md new file mode 100644 index 0000000..04aa2ff --- /dev/null +++ b/planning/plans/2026-06-09-mkdocs-github-actions-plan.md @@ -0,0 +1,455 @@ +# MkDocs GitHub Actions Migration Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Replace Read the Docs with GitHub Actions → GitHub Pages docs deployment, mirroring the modern-di sibling project's setup. + +**Architecture:** A push to `main` that touches docs files triggers `.github/workflows/docs.yml`, which runs `just docs-deploy`. The recipe invokes `uvx --with-requirements docs/requirements.txt mkdocs gh-deploy --force`, force-pushing the built site to the `gh-pages` branch. GitHub Pages serves `gh-pages` under the custom subdomain `lite-bootstrap.modern-python.org` (set via `docs/CNAME`). + +**Tech Stack:** MkDocs + mkdocs-material, GitHub Actions, GitHub Pages, `uv`/`uvx`, `just`. + +**Spec:** `planning/specs/2026-06-09-mkdocs-github-actions-design.md` + +--- + +## File-level overview + +| File | Action | Responsibility | +|---|---|---| +| `.github/workflows/docs.yml` | Create | CI workflow that deploys docs on push to `main` | +| `Justfile` | Modify (append recipe) | `docs-deploy` recipe invoked by CI | +| `mkdocs.yml` | Modify (add `site_url`) | Canonical site URL for sitemap and Material theme | +| `docs/CNAME` | Create | Custom domain tag read by GitHub Pages from `gh-pages` | +| `pyproject.toml` | Modify (add `docs` URL) | PyPI "Documentation" link | +| `.readthedocs.yaml` | Delete | Remove RTD trigger in the same PR | + +Order of tasks: lowest-risk first (`Justfile`, `mkdocs.yml`, `CNAME`, `pyproject.toml`), then the workflow, then RTD removal, then integration verification. + +--- + +## Pre-flight + +- [ ] **Step 1: Confirm working tree clean and on a fresh branch** + +```bash +git status +git checkout -b docs/migrate-to-github-actions +``` + +Expected: clean working tree, new branch checked out. + +- [ ] **Step 2: Confirm `uvx` is available and docs deps install cleanly (no changes yet)** + +```bash +uvx --with-requirements docs/requirements.txt mkdocs --version +``` + +Expected: prints an mkdocs version string (e.g. `mkdocs, version 1.6.x`) without errors. This proves the docs toolchain works before we wire it into CI. + +--- + +## Task 1: Add `docs-deploy` recipe to `Justfile` + +**Files:** +- Modify: `Justfile` (append to end) + +**Context:** Modern-di's `Justfile` ends with this exact recipe. The two-line comment is intentional — it documents the foot-gun that running this locally from a stale checkout will roll the live site backward. CI is the only intended caller. + +- [ ] **Step 1: Append the recipe** + +Append to `Justfile`: + +``` +# Force-pushes built site to gh-pages; CI runs this on push to main. +# Manual invocation from a stale checkout will roll the live site back. +docs-deploy: + uvx --with-requirements docs/requirements.txt mkdocs gh-deploy --force +``` + +- [ ] **Step 2: Confirm `just` recognises the recipe** + +Run: `just --list` +Expected output includes a line containing `docs-deploy`. + +- [ ] **Step 3: Verify the recipe text by inspecting `just`'s view of it** + +Run: `just --show docs-deploy` +Expected: prints the recipe body exactly as written above. + +**Do NOT run `just docs-deploy` locally** — it would force-push the current checkout's build to `gh-pages`, which only exists once GitHub Pages is set up. CI will be the first caller. + +- [ ] **Step 4: Commit** + +```bash +git add Justfile +git commit -m "build: add docs-deploy Justfile recipe + +Mirrors the modern-di pattern; intended to be invoked from CI on push to +main. Local invocation force-pushes from the current checkout, so the +recipe carries an inline warning about stale checkouts." +``` + +--- + +## Task 2: Add `site_url` to `mkdocs.yml` + +**Files:** +- Modify: `mkdocs.yml:2` (insert new line under `site_name`) + +**Context:** Without `site_url`, the generated `sitemap.xml` contains relative URLs, the canonical `` tag is missing from ``, and Material theme features that need an absolute base URL silently degrade. Adding it now keeps the first deploy clean. + +- [ ] **Step 1: Edit `mkdocs.yml`** + +Find: +```yaml +site_name: lite-bootstrap +repo_url: https://github.com/modern-python/lite-bootstrap +``` + +Replace with: +```yaml +site_name: lite-bootstrap +site_url: https://lite-bootstrap.modern-python.org +repo_url: https://github.com/modern-python/lite-bootstrap +``` + +- [ ] **Step 2: Verify the docs still build with `--strict`** + +Run: +```bash +uvx --with-requirements docs/requirements.txt mkdocs build --strict --site-dir /tmp/lite-bootstrap-docs-build +``` + +Expected: exits 0. `--strict` upgrades warnings (broken links, dangling nav entries) to errors; this is the strongest local check available short of deploying. Clean up after: `rm -rf /tmp/lite-bootstrap-docs-build`. + +- [ ] **Step 3: Confirm `site_url` reached the rendered HTML** + +```bash +uvx --with-requirements docs/requirements.txt mkdocs build --site-dir /tmp/lite-bootstrap-docs-build +grep -c 'lite-bootstrap.modern-python.org' /tmp/lite-bootstrap-docs-build/sitemap.xml +rm -rf /tmp/lite-bootstrap-docs-build +``` + +Expected: `grep -c` prints a positive integer (≥1) — the canonical hostname appears in the sitemap. + +- [ ] **Step 4: Commit** + +```bash +git add mkdocs.yml +git commit -m "docs: set site_url for github-pages deployment + +site_url is required for canonical URLs in , sitemap.xml, and any +Material theme feature that needs an absolute base URL." +``` + +--- + +## Task 3: Create `docs/CNAME` + +**Files:** +- Create: `docs/CNAME` + +**Context:** `mkdocs gh-deploy` copies files from `docs/` (and other configured directories) into the built site. GitHub Pages then reads `CNAME` from the `gh-pages` branch root to bind the custom domain. Single line, no extension, no trailing whitespace, exactly one newline at end-of-file (the repo uses `eof-fixer` in `lint`; an empty final line is required). + +- [ ] **Step 1: Create the file** + +Create `docs/CNAME` with exactly this content (one line plus terminating newline): + +``` +lite-bootstrap.modern-python.org +``` + +- [ ] **Step 2: Verify file shape** + +```bash +wc -l docs/CNAME +xxd docs/CNAME | tail -1 +``` + +Expected: `wc -l` reports `1` (one line). `xxd` tail shows the final byte is `0a` (newline), no carriage returns, no trailing spaces. + +- [ ] **Step 3: Verify `eof-fixer` is happy** + +```bash +uv run eof-fixer docs/CNAME --check +``` + +Expected: exits 0 with no output. + +- [ ] **Step 4: Confirm `mkdocs build` copies `CNAME` into the site** + +```bash +uvx --with-requirements docs/requirements.txt mkdocs build --site-dir /tmp/lite-bootstrap-docs-build +cat /tmp/lite-bootstrap-docs-build/CNAME +rm -rf /tmp/lite-bootstrap-docs-build +``` + +Expected: prints `lite-bootstrap.modern-python.org`. If the file is absent from the built site, mkdocs may need an `extra_files` directive — this is not the case for default mkdocs because it copies all non-markdown files from `docs/` automatically, but verify here so we catch any version-specific surprise before merging. + +- [ ] **Step 5: Commit** + +```bash +git add docs/CNAME +git commit -m "docs: add CNAME for lite-bootstrap.modern-python.org + +GitHub Pages reads this from the gh-pages branch root to bind the +custom subdomain. mkdocs copies it into the built site automatically." +``` + +--- + +## Task 4: Update `[project.urls]` in `pyproject.toml` + +**Files:** +- Modify: `pyproject.toml:37-39` + +**Context:** The PyPI page renders `[project.urls]` entries as named links. Currently only `repository` is set; adding `docs` makes the docs link appear on PyPI on the next release. Modern-di's order is `repository` then `docs` — match. + +- [ ] **Step 1: Edit `pyproject.toml`** + +Find: +```toml +[project.urls] +repository = "https://github.com/modern-python/lite-bootstrap" +``` + +Replace with: +```toml +[project.urls] +repository = "https://github.com/modern-python/lite-bootstrap" +docs = "https://lite-bootstrap.modern-python.org" +``` + +- [ ] **Step 2: Verify TOML still parses and lockfile is unchanged** + +```bash +uv lock --check +``` + +Expected: exits 0 (lockfile up-to-date; the URLs section does not affect the dependency graph, so no relock needed). + +- [ ] **Step 3: Run lint to ensure no formatting drift** + +```bash +just lint-ci +``` + +Expected: exits 0 (eof-fixer, ruff format check, ruff check, ty check all pass). + +- [ ] **Step 4: Commit** + +```bash +git add pyproject.toml +git commit -m "build: add docs URL to project.urls + +PyPI renders this as the 'Documentation' link on the project page." +``` + +--- + +## Task 5: Create `.github/workflows/docs.yml` + +**Files:** +- Create: `.github/workflows/docs.yml` + +**Context:** Verbatim from `../modern-di/.github/workflows/docs.yml`. Each design choice is load-bearing — read the inline notes below before editing. + +- [ ] **Step 1: Create the workflow file** + +Create `.github/workflows/docs.yml`: + +```yaml +name: Deploy Docs + +on: + push: + branches: [main] + paths: + - "docs/**" + - "mkdocs.yml" + - ".github/workflows/docs.yml" + workflow_dispatch: + +concurrency: + group: docs-deploy + cancel-in-progress: true + +permissions: + contents: write + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + - uses: extractions/setup-just@v4 + - uses: astral-sh/setup-uv@v8.2.0 + - run: just docs-deploy +``` + +Design notes (for the engineer reviewing this — do **not** weaken any of these): + +- `paths:` filter — prevents redeploys on commits that only change `src/` or tests. The list intentionally includes `.github/workflows/docs.yml` so workflow edits trigger a redeploy too. +- `concurrency: docs-deploy` + `cancel-in-progress: true` — if two pushes land near-simultaneously, the older one is cancelled before it can force-push a stale build over the newer one. +- `permissions: contents: write` — required for `mkdocs gh-deploy --force` to push to `gh-pages`. The default `GITHUB_TOKEN` would be read-only without this. +- `fetch-depth: 0` — `mkdocs gh-deploy` updates the `gh-pages` branch via git, which fails on a shallow clone. Do not remove. +- Action versions match modern-di. The lite-bootstrap `ci.yml` is older (`checkout@v4`, `setup-just@v2`, `setup-uv@v3`); leave those alone — out of scope. + +- [ ] **Step 2: Validate YAML syntax** + +```bash +uvx --from yamllint yamllint -d '{extends: relaxed, rules: {line-length: disable}}' .github/workflows/docs.yml +``` + +Expected: exits 0 with no output. (yamllint catches typos, indentation errors, duplicate keys.) + +- [ ] **Step 3: Optional — schema-validate with `actionlint`** + +```bash +# only if actionlint is installed locally; skip otherwise +command -v actionlint && actionlint .github/workflows/docs.yml || echo "actionlint not installed — relying on GitHub-side validation post-push" +``` + +Expected: if `actionlint` is installed, exits 0 with no output. If not installed, prints the fallback message; GitHub will validate on push. + +- [ ] **Step 4: Commit** + +```bash +git add .github/workflows/docs.yml +git commit -m "ci: add docs deployment workflow + +Builds the mkdocs site and force-pushes to gh-pages on push to main when +docs files change. Mirrors modern-di's pattern: paths filter, deploy +concurrency group, contents: write permission, fetch-depth: 0." +``` + +--- + +## Task 6: Delete `.readthedocs.yaml` + +**Files:** +- Delete: `.readthedocs.yaml` + +**Context:** Once this file is removed, Read the Docs stops triggering builds on push (RTD still serves the *last* successful build, but the project is dormant from a build-pipeline perspective). The RTD project record on readthedocs.io is not affected and should be archived manually after the new site is verified live (operator follow-up, post-merge). + +- [ ] **Step 1: Remove the file** + +```bash +git rm .readthedocs.yaml +``` + +Expected: file deleted from working tree and staged for commit. + +- [ ] **Step 2: Confirm no other files reference it** + +```bash +grep -rn '\.readthedocs\.yaml\|readthedocs\.io' . --include='*.md' --include='*.yml' --include='*.yaml' --include='*.toml' --exclude-dir=.git --exclude-dir=.venv --exclude-dir=planning +``` + +Expected: no matches. (`planning/` is excluded because the design spec legitimately references the RTD URL and the file being deleted.) If any match appears outside of `planning/`, update it to point at the new domain. + +- [ ] **Step 3: Commit** + +```bash +git commit -m "ci: remove read-the-docs config + +Docs now deploy via GitHub Actions to gh-pages. RTD project will be +archived on readthedocs.io after the new site is verified live." +``` + +--- + +## Task 7: Final integration verification + +**Files:** none (verification only) + +- [ ] **Step 1: Re-run lint and tests** + +```bash +just lint-ci +just test +``` + +Expected: both exit 0. Confirms none of the config/URL edits broke anything checked by the existing CI. + +- [ ] **Step 2: One more `--strict` docs build with all changes in place** + +```bash +uvx --with-requirements docs/requirements.txt mkdocs build --strict --site-dir /tmp/lite-bootstrap-docs-final +test -f /tmp/lite-bootstrap-docs-final/CNAME && echo "CNAME present" +grep -q 'lite-bootstrap.modern-python.org' /tmp/lite-bootstrap-docs-final/sitemap.xml && echo "site_url present in sitemap" +rm -rf /tmp/lite-bootstrap-docs-final +``` + +Expected: build exits 0; both echo lines print. + +- [ ] **Step 3: Review the staged commits** + +```bash +git log --oneline origin/main..HEAD +``` + +Expected: 6 commits (Justfile, mkdocs.yml, CNAME, pyproject.toml, workflow, RTD removal). Order is fine for review either way. + +--- + +## Task 8: Push branch and open PR + +**Files:** none (git/gh operations only) + +- [ ] **Step 1: Push branch** + +```bash +git push -u origin docs/migrate-to-github-actions +``` + +- [ ] **Step 2: Open PR with operator follow-up checklist** + +The operator follow-ups from the spec ("Out of repo scope") must appear in the PR body so the reviewer / merger does not miss them. + +```bash +gh pr create --title "docs: migrate from Read the Docs to GitHub Actions + Pages" --body "$(cat <<'EOF' +## Summary + +- Replaces Read the Docs with a GitHub Actions workflow that force-pushes the built site to `gh-pages` on push to `main`. +- GitHub Pages serves `gh-pages` under `lite-bootstrap.modern-python.org` (`docs/CNAME`). +- Mirrors the sibling [`modern-di`](https://github.com/modern-python/modern-di) project's docs deployment pattern verbatim. + +Spec: `planning/specs/2026-06-09-mkdocs-github-actions-design.md` +Plan: `planning/plans/2026-06-09-mkdocs-github-actions-plan.md` + +## Operator follow-ups (post-merge) + +These actions happen outside the repo and must be performed in order after merging: + +1. **DNS** — on the `modern-python.org` zone, add a `CNAME` record: `lite-bootstrap` → `modern-python.github.io`. (Same as the existing `modern-di.modern-python.org` record.) +2. **Trigger first deploy** — either let the merge commit trigger it (if it touches docs files) or run the workflow manually via `Actions → Deploy Docs → Run workflow`. First run creates the `gh-pages` branch. +3. **Enable GitHub Pages** — repo Settings → Pages → Source: 'Deploy from a branch' → `gh-pages` / `(root)`. Custom domain auto-populates from `CNAME`. +4. **Update repo homepage** — `gh repo edit modern-python/lite-bootstrap --homepage https://lite-bootstrap.modern-python.org` +5. **Archive RTD project** — on readthedocs.io, mark the project archived once the new site is verified live. + +## Test plan + +- [ ] CI green on PR (`main` workflow runs lint + pytest as today; docs workflow does not run on PRs by design). +- [ ] After merge: `Deploy Docs` workflow runs green on the merge commit. +- [ ] `gh-pages` branch is created and contains the built site + `CNAME`. +- [ ] Once DNS and Pages are configured: https://lite-bootstrap.modern-python.org serves the site. +- [ ] PyPI 'Documentation' link points to the new domain on the next release. + +EOF +)" +``` + +Expected: PR URL printed. + +--- + +## Done + +After merging: + +1. Watch the first run of `Deploy Docs` (it will only trigger if the merge commit touches docs files — otherwise dispatch manually via `Actions → Deploy Docs → Run workflow`). +2. Walk the operator-follow-up checklist from the PR body in order. +3. Verify the live site, then archive the RTD project. diff --git a/planning/specs/2026-06-09-mkdocs-github-actions-design.md b/planning/specs/2026-06-09-mkdocs-github-actions-design.md new file mode 100644 index 0000000..f4e5f7b --- /dev/null +++ b/planning/specs/2026-06-09-mkdocs-github-actions-design.md @@ -0,0 +1,151 @@ +# Migrate docs from Read the Docs to GitHub Actions + Pages + +Date: 2026-06-09 +Status: Approved (pending written-spec review) + +## Goal + +Replace the current Read the Docs build pipeline with a GitHub Actions workflow that builds the MkDocs site and deploys it to `gh-pages`, served by GitHub Pages under the custom subdomain `lite-bootstrap.modern-python.org`. Mirror the setup already in use in the sibling `modern-di` project so both repos in the `modern-python` org share one deployment pattern. + +## Current state + +- `.readthedocs.yaml` at repo root drives RTD builds. RTD installs `docs/requirements.txt` and runs `mkdocs build` against `mkdocs.yml`. +- Site is served at `lite-bootstrap.readthedocs.io` (set as the GitHub repo `homepageUrl`). +- `mkdocs.yml` has no `site_url`. No `CNAME` file. No `[project.urls] docs` entry. +- No GitHub Actions workflow for docs. `ci.yml` only runs lint + pytest. + +## Reference + +`modern-di` (sibling repo at `../modern-di/`) already runs this pattern: + +- `.github/workflows/docs.yml` — push-to-main + `workflow_dispatch`, paths-filtered to docs files. +- `Justfile` recipe `docs-deploy` — `uvx --with-requirements docs/requirements.txt mkdocs gh-deploy --force`. +- `mkdocs.yml` — `site_url: https://modern-di.modern-python.org`. +- `docs/CNAME` — single line `modern-di.modern-python.org`. +- `pyproject.toml` — `[project.urls] docs = "https://modern-di.modern-python.org"`. + +This spec copies that pattern with names changed for lite-bootstrap. + +## Changes + +### 1. `.github/workflows/docs.yml` (new) + +Verbatim from `modern-di/.github/workflows/docs.yml`: + +```yaml +name: Deploy Docs + +on: + push: + branches: [main] + paths: + - "docs/**" + - "mkdocs.yml" + - ".github/workflows/docs.yml" + workflow_dispatch: + +concurrency: + group: docs-deploy + cancel-in-progress: true + +permissions: + contents: write + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + - uses: extractions/setup-just@v4 + - uses: astral-sh/setup-uv@v8.2.0 + - run: just docs-deploy +``` + +Notes: + +- `paths` filter prevents redeploys on unrelated commits. +- `concurrency: docs-deploy` + `cancel-in-progress: true` prevents stale-checkout overwrites if two pushes land near-simultaneously. +- `permissions: contents: write` is required for `mkdocs gh-deploy --force` to push to `gh-pages`. +- `fetch-depth: 0` is required by `mkdocs gh-deploy` to update the `gh-pages` branch (shallow clone would fail). +- Action versions (`checkout@v6`, `setup-just@v4`, `setup-uv@v8.2.0`) match modern-di. The lite-bootstrap `ci.yml` uses older versions; that stays unchanged here. + +### 2. `Justfile` — append `docs-deploy` recipe + +``` +# Force-pushes built site to gh-pages; CI runs this on push to main. +# Manual invocation from a stale checkout will roll the live site back. +docs-deploy: + uvx --with-requirements docs/requirements.txt mkdocs gh-deploy --force +``` + +Identical to modern-di. `uvx --with-requirements docs/requirements.txt` keeps docs dependencies isolated from the main project lockfile (mkdocs is not declared in `pyproject.toml`). + +### 3. `mkdocs.yml` — add `site_url` + +Insert immediately under `site_name`: + +```yaml +site_url: https://lite-bootstrap.modern-python.org +``` + +Needed for canonical URLs in ``, the generated `sitemap.xml`, and Material theme features that depend on absolute URLs. + +### 4. `docs/CNAME` (new) + +Single line: + +``` +lite-bootstrap.modern-python.org +``` + +`mkdocs gh-deploy` copies the file into the built site root; GitHub Pages reads it from the `gh-pages` branch to serve the custom domain. + +### 5. `pyproject.toml` — add `docs` URL + +Update `[project.urls]`: + +```toml +[project.urls] +repository = "https://github.com/modern-python/lite-bootstrap" +docs = "https://lite-bootstrap.modern-python.org" +``` + +This becomes the PyPI "Documentation" link on the next release. + +### 6. `.readthedocs.yaml` — delete + +Once removed, RTD will stop triggering builds on push. The RTD project record on readthedocs.io is unchanged by this PR and should be archived manually post-cutover (see "Out of repo scope" below). + +## Out of repo scope (operator follow-ups) + +These actions happen outside the repo and are not part of the PR diff. They are listed here so the operator does not miss them: + +1. **DNS** — add a `CNAME` record on the `modern-python.org` zone: `lite-bootstrap` → `modern-python.github.io`. Same setup as the existing `modern-di.modern-python.org` record. +2. **First workflow run** — first push to `main` after merge will create the `gh-pages` branch. The workflow uses `gh-deploy --force`, so the initial create is automatic. +3. **GitHub Pages enablement** — repo admin: Settings → Pages → Source: "Deploy from a branch" → `gh-pages` / `(root)`. The custom-domain field auto-populates from the `CNAME` file in the branch. +4. **GitHub repo homepage** — `gh repo edit --homepage https://lite-bootstrap.modern-python.org` (currently set to the RTD URL). +5. **Read the Docs project archival** — on the readthedocs.io dashboard, mark the project as archived after the new site is verified live. Not done in this PR so that RTD remains a fallback during cutover. + +## Out of design scope (deliberate non-goals) + +- **Action-version bumps in `ci.yml`** — modern-di uses `checkout@v6` / `setup-just@v4` / `setup-uv@v8.2.0` and split its workflow into a reusable `_checks.yml`. The lite-bootstrap `ci.yml` still uses `checkout@v4` / `setup-just@v2` / `setup-uv@v3` and is monolithic. Not touched here; the new `docs.yml` uses current versions because it is new code. +- **Build-on-PR check** — modern-di does not run `mkdocs build --strict` on PRs and neither will lite-bootstrap. Cheap to add later if broken docs slip through. +- **Doc content changes** — `nav`, theme, extensions are unchanged. + +## Risk and rollback + +- **First deploy failure** is harmless — the new domain has no users yet and RTD still works until `.readthedocs.yaml` is deleted (this happens in the same PR, so a failure on the first run leaves the site temporarily unbuilt until fixed; acceptable for a low-traffic library docs site). +- **Stale-checkout rollback** — `gh-deploy --force` overwrites `gh-pages` from whatever the checkout sees. The Justfile comment documents this. Manual invocation from an out-of-date local clone will roll the live site backward. CI is the only intended caller. +- **Reverting** — revert the PR; RTD config returns; RTD resumes building automatically. The RTD project remains linked to the repo until manually archived, so the fallback is always available. + +## Acceptance criteria + +- [ ] `docs.yml` workflow exists and runs on push to `main` when docs files change. +- [ ] `just docs-deploy` runs locally without error (Justfile recipe wired up; `uvx` reachable). +- [ ] `mkdocs.yml` declares `site_url: https://lite-bootstrap.modern-python.org`. +- [ ] `docs/CNAME` contains the custom domain on a single line, no trailing newline issues. +- [ ] `pyproject.toml` `[project.urls]` includes a `docs` key pointing at the custom domain. +- [ ] `.readthedocs.yaml` is removed. +- [ ] After merge: workflow runs green, `gh-pages` branch is created, GH Pages serves the site under the custom domain, repo homepage URL updated. diff --git a/pyproject.toml b/pyproject.toml index 5a87fee..6f03f31 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,6 +36,7 @@ dependencies = [ [project.urls] repository = "https://github.com/modern-python/lite-bootstrap" +docs = "https://lite-bootstrap.modern-python.org" [project.optional-dependencies] sentry = [