Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 40 additions & 12 deletions .github/workflows/documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -127,28 +127,29 @@ jobs:
with:
name: documentation-html
path: docs/_build/html/
include-hidden-files: true
if-no-files-found: error
retention-days: 14

publish:
needs: documentation
if: github.repository == 'sequince-dev/sequince-examples' && github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
# All PR previews are published by the trusted workflow_run workflow.
if: >-
github.repository == 'sequince-dev/sequince-examples' &&
github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
runs-on: ubuntu-latest
timeout-minutes: 55
concurrency:
group: examples-rtd-latest
cancel-in-progress: false
permissions:
contents: write
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.sha }}
ref: ${{ github.event.pull_request.head.sha || github.sha }}
persist-credentials: false
- uses: actions/download-artifact@v4
- uses: actions/setup-python@v5
with:
name: execution-lock
path: .
python-version: "3.13"
- uses: actions/download-artifact@v4
with:
name: validated-bundle
Expand All @@ -165,9 +166,36 @@ jobs:
assert Path(str(path) + '.sha256').read_text().split() == [digest(path), path.name]
unpack(path, sha, OUTPUT)
PY
- name: Publish durable assets and wait for RTD
run: python3 scripts/publish_docs.py
- uses: actions/download-artifact@v4
with:
name: documentation-html
path: docs/_build/html
- name: Check publication is current
id: publication
env:
GH_TOKEN: ${{ github.token }}
RTD_TOKEN: ${{ secrets.RTD_TOKEN }}
RTD_PROJECT: ${{ vars.RTD_PROJECT }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
python3 - <<'PY'
import os
import sys
sys.path.insert(0, 'scripts')
from docs_pipeline import git
number = os.environ['PR_NUMBER']
ref = f'refs/pull/{number}/head' if number else 'refs/heads/main'
remote = git('ls-remote', 'origin', ref).split()
current = bool(remote) and remote[0] == git('rev-parse', 'HEAD')
with open(os.environ['GITHUB_OUTPUT'], 'a') as output:
output.write(f'current={str(current).lower()}\n')
if not current:
print('Source advanced; skipping stale publication')
PY
- name: Upload HTML to Read the Docs
if: steps.publication.outputs.current == 'true'
# Pinned beta revision; upstream has not published the documented v1 tag.
uses: readthedocs/upload-action@f1f5bac5fc97073927100367d8a5bb22587e1e16
with:
token: ${{ secrets.READTHEDOCS_TOKEN }}
project-slug: ${{ vars.RTD_PROJECT }}
html: docs/_build/html
# Keep the existing latest URL.
version-name: latest
27 changes: 21 additions & 6 deletions .github/workflows/pr-documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:

permissions:
actions: read
contents: write
contents: read
pull-requests: read

jobs:
Expand All @@ -21,15 +21,30 @@ jobs:
group: examples-rtd-pr-${{ github.event.workflow_run.head_repository.full_name }}-${{ github.event.workflow_run.head_branch }}
cancel-in-progress: false
steps:
# workflow_run uses trusted default-branch code. Never check out the PR
# or execute files from its artifacts in this credential-bearing job.
# workflow_run checks out trusted default-branch code, never PR code.
# Treat the downloaded artifacts as data and never execute their files.
- uses: actions/checkout@v4
with:
ref: ${{ github.sha }}
persist-credentials: false
- name: Publish bundle and rebuild PR preview
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Validate artifacts and prepare PR preview
id: preview
run: python3 scripts/publish_pr_docs.py
env:
GH_TOKEN: ${{ github.token }}
RTD_TOKEN: ${{ secrets.RTD_TOKEN }}
RTD_PROJECT: ${{ vars.RTD_PROJECT }}
- name: Upload PR preview to Read the Docs
if: steps.preview.outputs.ready == 'true'
# Same pinned beta revision as the main documentation workflow.
uses: readthedocs/upload-action@f1f5bac5fc97073927100367d8a5bb22587e1e16
with:
token: ${{ secrets.READTHEDOCS_TOKEN }}
project-slug: ${{ vars.RTD_PROJECT }}
html: ${{ steps.preview.outputs.html }}
# workflow_run metadata describes the default branch, so override it
# with the validated PR metadata rather than relying on inference.
version-name: ${{ steps.preview.outputs.version }}
version-type: external
commit: ${{ steps.preview.outputs.commit }}
28 changes: 5 additions & 23 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,29 +177,11 @@ plots and staging directories.

### CI and hosting

The documentation workflow resolves one execution lock and shares it across
parallel example jobs. It collects outputs from that same workflow run, runs
checks, builds Sphinx, and uploads HTML and a validated output bundle.

Trusted runs on `main` can attach the bundle to a dedicated GitHub release keyed
by the full source commit and trigger a Read the Docs build. Read the Docs
installs `docs/requirements.txt`, fetches the exact matching bundle, and verifies
it before building; it does not run sampling. Freshness checks guard `latest`
builds, and there is no fallback to results from another commit.

Pull requests run examples and build Sphinx at the PR head commit, matching
Read the Docs previews. After a successful run, the separate
`Publish PR documentation` workflow runs trusted code from the default branch.
It reads the completed run's artifact as data, checks its archive and commit,
publishes it under `docs-pr-<PR-number>-<head-SHA>`, and requests the PR preview.
PR code and artifacts are never executed in that publishing job. Read the Docs
then performs the same output provenance validation used for `latest`.

Maintainers must configure the Read the Docs project, the repository variable
`RTD_PROJECT`, and the secret `RTD_TOKEN` before enabling this hosting workflow.
Keep credentials out of source files and pull request jobs. The configuration
uses standard Read the Docs hosting; private access requires a hosting service
and account configuration that supports it.
CI shares one execution lock across example jobs, validates their outputs, and
builds Sphinx. The official RTD upload action publishes main as `latest`.
PR previews, including forks, use a separate trusted workflow that validates
artifacts without executing PR code. Stale publications are skipped; no Git
tags or GitHub releases are created.

## Commit messages

Expand Down
138 changes: 0 additions & 138 deletions scripts/publish_docs.py

This file was deleted.

Loading
Loading