Check the website field against the declared documentation URL #5208
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| name: 馃摎 Docs | |
| "on": | |
| workflow_call: | |
| workflow_dispatch: | |
| push: | |
| branches: | |
| - main | |
| # Only rebuild docs when something the docs actually depend on changes: | |
| # Sphinx sources, Python (autodoc), pyproject (project metadata + docs | |
| # group deps), `uv.lock` (resolved env), the docs workflow itself, and | |
| # the top-level Markdown / data files that the docs include or link to. | |
| paths: | |
| - .github/workflows/docs.yaml | |
| - changelog.md | |
| - citation.cff | |
| - docs/** | |
| - pyproject.toml | |
| - readme.md | |
| - uv.lock | |
| - "**/*.py" | |
| permissions: {} | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} | |
| cancel-in-progress: ${{ !startsWith(github.event.head_commit.message, '[changelog] Release') }} | |
| # Supply-chain cooldown: no package published within the window can be resolved by | |
| # any command in this workflow. Set here, not per command, so it also covers the | |
| # `metadata` bootstrap and any step added later; a workflow-level `env:` cannot | |
| # reference `needs`, so the window is a literal kept equal to `[tool.repomatic] | |
| # minimum-release-age`. repomatic's own test suite enforces that upstream; a | |
| # synced copy is kept in step by hand. Deliberate bypasses are per-package CLI | |
| # flags (`--exclude-newer-package`, `--min-release-age-exclude`). | |
| # See claude.md for the rationale. | |
| env: | |
| NPM_CONFIG_MIN_RELEASE_AGE: 7 | |
| UV_EXCLUDE_NEWER: "1 week" | |
| jobs: | |
| metadata: | |
| name: 馃К Project metadata | |
| runs-on: ubuntu-26.04 | |
| outputs: | |
| metadata: ${{ steps.metadata.outputs.metadata }} | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 | |
| with: | |
| version: "0.12.2" | |
| - name: Run repomatic metadata | |
| id: metadata | |
| run: > | |
| uv --no-progress run --frozen -- repomatic metadata | |
| --format github-json --output "$GITHUB_OUTPUT" | |
| is_python_project is_sphinx doc_files | |
| deploy-docs: | |
| name: 馃摉 Deploy Sphinx doc | |
| needs: | |
| - metadata | |
| if: >- | |
| fromJSON(needs.metadata.outputs.metadata).is_python_project | |
| && fromJSON(needs.metadata.outputs.metadata).is_sphinx | |
| runs-on: ubuntu-26.04 | |
| permissions: | |
| id-token: write | |
| pages: write | |
| environment: | |
| name: github-pages | |
| url: ${{ steps.deploy.outputs.page_url }} | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 | |
| with: | |
| version: "0.12.2" | |
| - name: Install Graphviz and mandoc | |
| # Graphviz: backs the sphinx.ext.graphviz directive. | |
| # See: https://www.sphinx-doc.org/en/master/usage/extensions/graphviz.html | |
| # mandoc: renders the roff `.1` files written by the | |
| # click_extra.sphinx.manpages hook into the browser-viewable | |
| # `.html` siblings the docs site links to. Skipped silently when | |
| # the hook is not opted in. | |
| # fonts-liberation is graphviz's only Recommends (via the transitional | |
| # fonts-liberation2), and an image that ships no fonts of its own would | |
| # otherwise lose it: naming it here keeps diagram labels rendering | |
| # whatever the base image carries, while --no-install-recommends stops | |
| # apt pulling anything else unasked. | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install --yes --no-install-recommends \ | |
| fonts-liberation graphviz mandoc | |
| - name: Build documentation | |
| # Install --all-extras so documentation can covers all features of the project, including the optional ones. | |
| run: uv --no-progress run --frozen --all-extras --group docs -- sphinx-build -b html ./docs ./docs/_build | |
| - name: Upload artifact | |
| uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0 | |
| with: | |
| path: ./docs/_build | |
| - name: Deploy to GitHub Pages | |
| id: deploy | |
| uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0 | |
| check-broken-links: | |
| name: 馃挃 Check broken links | |
| needs: | |
| - metadata | |
| permissions: | |
| issues: write | |
| # Skip all PRs as we won't have permissions to create issues. | |
| # Skip the prepare-release branch as it contains URLs pointing to tags that don't exist yet. | |
| # Skip any push containing a post-release bump commit as a precautionary measure. | |
| if: > | |
| github.event_name != 'pull_request' | |
| && github.ref != 'refs/heads/prepare-release' | |
| && (! contains(toJSON(github.event.commits.*.message), '[changelog] Post-release bump')) | |
| && (fromJSON(needs.metadata.outputs.metadata).doc_files | |
| || (fromJSON(needs.metadata.outputs.metadata).is_python_project | |
| && fromJSON(needs.metadata.outputs.metadata).is_sphinx)) | |
| # ARM rather than the x86 axis: this crawl's runtime is dominated by remote | |
| # servers' throttling and grows with every release (each adds a dozen URLs | |
| # to the binaries page), so it wants the fastest Linux image available. It | |
| # once ran on a lean image with a 15-minute job cap, which killed the whole | |
| # crawl outright rather than failing a step. | |
| runs-on: ubuntu-26.04-arm | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 | |
| with: | |
| version: "0.12.2" | |
| - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: ~/.cache/repomatic/bin | |
| key: >- | |
| repomatic-${{ runner.os }}-${{ runner.arch }}-lychee-${{ | |
| github.job_workflow_sha || hashFiles('repomatic/tool_registry.py') }} | |
| restore-keys: | | |
| repomatic-${{ runner.os }}-${{ runner.arch }}-lychee- | |
| - name: Install Graphviz | |
| if: > | |
| fromJSON(needs.metadata.outputs.metadata).is_python_project | |
| && fromJSON(needs.metadata.outputs.metadata).is_sphinx | |
| # So we can use the sphinx.ext.graphviz plugin. | |
| # See: https://www.sphinx-doc.org/en/master/usage/extensions/graphviz.html | |
| # fonts-liberation named explicitly: see the deploy-docs job above. | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install --yes --no-install-recommends fonts-liberation graphviz | |
| - name: Run Sphinx linkcheck | |
| if: > | |
| fromJSON(needs.metadata.outputs.metadata).is_python_project | |
| && fromJSON(needs.metadata.outputs.metadata).is_sphinx | |
| # Do not use -W: we parse output.json directly instead of relying on exit codes. | |
| # GitHub throttles anonymous crawls to ~1 request/minute, enough to blow a | |
| # capped runner's job budget on link-heavy repos (the runner kills the whole | |
| # job, bypassing continue-on-error). Exposing the token lets a repo's conf.py | |
| # authenticate its github.com checks via linkcheck_request_headers. | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: > | |
| uv --no-progress run --frozen --all-extras --group docs -- | |
| sphinx-build -b linkcheck ./docs ./docs/_linkcheck | |
| continue-on-error: true | |
| - name: Run lychee | |
| if: fromJSON(needs.metadata.outputs.metadata).doc_files | |
| id: lychee_run | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| DOC_FILES: ${{ fromJSON(needs.metadata.outputs.metadata).doc_files }} | |
| # xargs splits the list and does nothing else. It must not be what launches | |
| # lychee: xargs answers any child status in 1..125 with 123 of its own, so | |
| # lychee's 2 ("broken links found") reaches `repomatic broken-links` looking | |
| # exactly like a crash, and a real report gets filed as a tool failure and | |
| # dropped. Batching is the second casualty, since each batch writes the same | |
| # --output path and only the last one survives. Handing the split to `printf | |
| # '%s\0'` keeps xargs on an operation that cannot fail, and the single | |
| # invocation below then carries back both the status and the whole report. | |
| # | |
| # The split cannot be `eval "set -- ${DOC_FILES}"`: metadata wraps each path | |
| # in bare double quotes with no escaping, so a filename containing $(...) | |
| # would execute. xargs expands neither that nor backticks. | |
| run: | | |
| exit_code=0 | |
| files=() | |
| while IFS= read -r -d '' f; do files+=("$f"); done \ | |
| < <(echo "${DOC_FILES}" | xargs printf '%s\0') | |
| uv --no-progress run --frozen -- repomatic run lychee -- \ | |
| --format markdown --output ./lychee/out.md \ | |
| --hidden --suggest --no-progress --include-fragments --exclude-all-private \ | |
| "${files[@]}" \ | |
| || exit_code=$? | |
| echo "exit_code=$exit_code" >> "$GITHUB_OUTPUT" | |
| - name: Manage broken links issue | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| LYCHEE_EXIT_CODE: ${{ steps.lychee_run.outputs.exit_code }} | |
| run: | | |
| uv --no-progress run --frozen -- repomatic broken-links \ | |
| ${LYCHEE_EXIT_CODE:+--lychee-exit-code "${LYCHEE_EXIT_CODE}"} |