Skip to content

Add claude init component for audience-tagged claude.md sections #5245

Add claude init component for audience-tagged claude.md sections

Add claude init component for audience-tagged claude.md sections #5245

Workflow file for this run

---
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.
# `readme.*.md` catches the translated readmes kept beside the English
# one. `check-broken-links` crawls every file `metadata` reports under
# `doc_files`, translations included, so listing `readme.md` alone left a
# translation-only push with no link check at all: on an awesome list,
# where the translation carries the same few hundred links, that is most
# of what the crawl exists to cover.
paths:
- .github/workflows/docs.yaml
- changelog.md
- citation.cff
- docs/**
- pyproject.toml
- readme.*.md
- 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
timeout-minutes: 15
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.3"
- 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 sphinx_builder
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
# Wider than the default: this job provisions a toolchain, so its runtime tracks how much it has to install.
timeout-minutes: 30
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.3"
- 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 cover all features of the project, including the optional ones.
# The builder is `[tool.repomatic] sphinx.builder`, defaulting to `html`:
# a project serving extension-less URLs sets it to `dirhtml`.
# It travels through the environment rather than being interpolated
# into the command, like DOC_FILES below: the value is whatever the
# repository's own `pyproject.toml` holds, so expanding it inline would
# let a `pull_request`-triggered caller run a fork's string as shell
# code.
env:
SPHINX_BUILDER: ${{ fromJSON(needs.metadata.outputs.metadata).sphinx_builder }}
run: >
uv --no-progress run --frozen --all-extras --group docs --
sphinx-build -b "$SPHINX_BUILDER"
./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
# Widest cap in the repository, for the reason just above: this crawl is the
# one job whose runtime is set by other people's servers. Triple the 15
# minutes that already killed it once, against a measured 10.4-minute worst
# case, so the growth has somewhere to go.
timeout-minutes: 45
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
with:
version: "0.12.3"
- 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}"}