ci: follow-up workflow hardening (action updates, hashed deps lock, fork guard, perms default)#300
Merged
Merged
Conversation
Bump the actions/* actions used across the workflows to their current major releases (node runtime upgrades; no breaking input changes for how they are used here): - actions/checkout v6 -> v7 - actions/cache v5 -> v6 - actions/setup-go v5 -> v6 - actions/configure-pages v5 -> v6 - actions/upload-pages-artifact v4 -> v5 - actions/deploy-pages v4 -> v5 - actions/github-script v8 -> v9 - actions/upload-artifact v4 -> v7 (docs-preview; link-checker was already v7) setup-python (v6) and download-artifact (v8) are already on the latest major. First-party actions stay pinned by major tag (not SHA); the SHA-pinned third-party actions (lychee, markdownlint) are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the loosely-ranged requirements.txt with a compiled, fully pinned and hashed lock so documentation builds (including the production Pages deploy) are reproducible and tamper-evident: - requirements.in holds the human-edited direct dependencies (the former ranges) and is the source of truth. - requirements.txt is now generated from it with `pip-compile --generate-hashes --strip-extras`, pinning every direct and transitive package to an exact version with sha256 hashes. Installing it puts pip in --require-hashes mode automatically, so a compromised or unexpected artifact fails the install instead of silently entering a build. lexer-tests installs the lock on its own and then pytest separately, since pip refuses to mix hashed and unhashed requirements in one command; pytest is pinned (9.1.1) but kept out of the docs lock as a test-only tool. Verified: a clean venv installs from the lock, `mkdocs build --strict` succeeds, and the 57 lexer tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Every other pull-request-triggered job in link-checker.yml (check-links, comment-links) already guards on the PR head being a branch in this repository, not a fork. check-markdown was the lone exception. It is low-risk (markdownlint has no custom rules, so it runs no repository JS, with a read-only token and no secrets), but add the same guard so all PR-triggered jobs are consistent and fork PRs run nothing here. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
deploy-pages.yml set permissions only at the job level (build: contents:read; deploy: pages:write + id-token:write) with no workflow-level default, so a future job added without its own block would inherit the repository default token scope. Add a top-level `permissions: contents: read` so the safe default applies workflow-wide, matching every other workflow in the repo. The existing job-level blocks are unchanged and still take precedence. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…steps Add a Local Development section covering how to install the build dependencies and preview the site, and document that requirements.txt is a generated, pinned + hashed lock: edit requirements.in and regenerate with `pip-compile --generate-hashes --strip-extras` rather than hand-editing the lock. Commit both files. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
maximelb
approved these changes
Jul 10, 2026
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Details
Follow-up CI/security hardening pass on the GitHub Actions workflows, continuing the review after #294. It updates the first-party actions to their current majors, pins the Python documentation dependencies with a hashed lock file so builds (including the production Pages deploy) are reproducible and tamper-evident, and closes two small consistency gaps: a missing fork guard and a missing workflow-level permissions default.
There are no documentation content changes; the resolved dependency versions are unchanged, so the built site output is unchanged.
What's included
actions/checkoutv6 to v7,actions/cachev5 to v6,actions/setup-gov5 to v6,actions/configure-pagesv5 to v6,actions/upload-pages-artifactv4 to v5,actions/deploy-pagesv4 to v5,actions/github-scriptv8 to v9, andactions/upload-artifactv4 to v7 in docs-preview (link-checker was already v7).setup-python(v6) anddownload-artifact(v8) were already latest. The SHA-pinned third-party actions (lychee, markdownlint) are unchanged. All bumps are node-runtime upgrades with no breaking input changes for how they are used here.requirements.innow holds the human-edited direct dependencies (the former ranges) andrequirements.txtis generated from it withpip-compile --generate-hashes --strip-extras, pinning every direct and transitive package to an exact version with sha256 hashes. Installing it puts pip in--require-hashesmode automatically.lexer-testsinstalls the lock on its own and then pytest (pinned 9.1.1) separately, since pip refuses to mix hashed and unhashed requirements in one command.check-markdown: the one PR-triggered job inlink-checker.ymlthat lacked the fork guard the other jobs have now guards against fork PRs (low-risk, since markdownlint runs no repository JS, but added for consistency).deploy-pages.yml: added a top-levelpermissions: contents: readso any future job added without its own block defaults to read-only; the existing job-level blocks (buildcontents: read; deploypages: write+id-token: write) are unchanged.Blast radius / isolation
Changes are limited to
.github/workflows/*,requirements.in, andrequirements.txt. No documentation content, no MkDocs config, no site behavior change. What is affected: the action versions across all six workflows, the dependency-install step of the build/deploy workflows (now from the lock), the link-checkercheck-markdownjob (fork guard), and the deploy-pages permission default. Not affected: documentation content, the rendered site output, and the lexer/snippet/numbering logic.Notable contracts
requirements.txtis now a generated lock, not hand-edited. To change dependencies, editrequirements.inand regenerate with thepip-compilecommand in the file header. Installs enforce hashes, so an unexpected or tampered artifact fails the install instead of entering a build.Performance characteristics
Negligible. Hashed installs are the same speed as before; the action bumps are node-runtime updates. No change to build time or site size.
How this was verified
actions/*reference confirmed at its current latest major.mkdocs build --strictsucceeds, and the 57 lexer tests pass with the pinned pytest.contents: read, deploy job stillpages: write+id-token: write.Things to watch after merge
deploy-pages.ymlaction bumps run only on push-to-master (that workflow does not run on pull requests), so they are not exercised by this PR's CI; they first execute on the production Pages deploy after merge. Each bump was checked against its release notes and carries no breaking input change, and a failed Pages deploy leaves the currently live site in place (recoverable). Worth watching the first post-merge deploy, or manually running the workflow viaworkflow_dispatch.link-checker.ymlbumps (cachev6,github-scriptv9) and the newcheck-markdownfork guard are not exercised by this PR, because that workflow is path-filtered todocs/**/mkdocs.yml(untouched here); they first run on the next docs-touching PR or the weekly schedule. They are YAML-validated and use the same latest majors already exercised by the other jobs. This PR's CI (docs-preview, lexer-tests, snippet-tests) does exercise checkout v7, setup-go v6, upload-artifact v7, and the hashed-lock install.Rollback / revert
Workflow-only change: revert the PR to restore the previous action versions and dependency ranges. If a single action bump misbehaves, revert just that commit or pin the affected action back one major.
Not included
Dependabot (a
github-actions/pip/gomodupdate config) was intentionally left out of this PR; without it these action and dependency pins are bumped manually. Can be added separately if desired.Related PRs
🤖 Generated with Claude Code