Repository navigation
ci: check the docs in CI and before every push - #9
Merged
Merged
Conversation
tools/docs/check_docs.py checks the tracked Markdown files: relative links and #anchors resolve (GitHub slugs), no links to maintainer-local files, setup file names match web/lib/setup-downloads.json, "open source" only next to not/never. tools/docs/push_hook.py is a PreToolUse hook (.claude/settings.json) that holds Claude's git push until the check is clean and the docs were compared with the push (marker in .git, set with --mark). New CI job "docs". SECURITY.md: the advisory link is now absolute, the relative form only worked on github.com. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TEvvb1DM3ZA5FqELYSs4v8
- hook command starts the script only if it exists (python exits 2 on a missing file and would block every Bash/PowerShell call); onFailure: continue for Windows without Git Bash - is_push tokenizes the command (shlex) instead of searching the raw string; a push chained with another git command is blocked, the stale-marker message says to run --mark alone - the docs check reads the file list and contents at HEAD (git ls-tree / git show), so a link to a gitignored file is a finding locally as in CI; the maintainer-local rule is gone - the hook acts only in a repo that has tools/docs/push_hook.py - code spans are blanked per paragraph (they cross line breaks) - slug(): html.unescape, Nd/Nl/Pc only, paired emphasis, github-slugger duplicate loop - AGENTS.md and ci.yml name the same coverage; docstrings no longer claim exit 1 always passes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TEvvb1DM3ZA5FqELYSs4v8
Guessing "is this a git push?" from Bash/PowerShell text kept missing cases. git's own pre-push hook (https://git-scm.com/docs/githooks#_pre_push) gets the pushed commits from git itself: tools/githooks/pre-push runs check_docs on every pushed commit and, in Claude Code (CLAUDECODE set), holds the push until `python tools/docs/push_hook.py --mark`. - remove .claude/settings.json and the command parser of push_hook.py - push_hook.py is the pre-push implementation (pure decide(), IO in main(), fails open) - .gitattributes: tools/githooks/* is LF, a CRLF sh script breaks - check_docs: the "open source" wording rule works per paragraph, the docs are hard-wrapped - tests with real git: a bare remote and a clone with the hook on Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TEvvb1DM3ZA5FqELYSs4v8
- wrapper probes python and python3 for a working 3.10+ (the Microsoft Store alias answers
command -v and exits 49), still fails open
- parse the stdin lines with rsplit (a local ref like HEAD^{/some words} holds spaces)
- import check_docs inside the fail-open try; the marker under CLAUDECODE no longer depends on it
- only the tip of each pushed ref is checked, and a tip already on a remote-tracking ref is skipped
- the hint range uses the remote being pushed to; messages name the checked ref and short sha
- the marker lives in the git common dir, shared by all worktrees
- wording rule cuts blocks at list items; an invalid setup-downloads.json is a finding
- drop the redundant .gitattributes rule (* text=auto eol=lf already gives the hook LF)
- AGENTS.md, CONTRIBUTING.md, ci.yml: say what is checked and that core.hooksPath turns it on
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TEvvb1DM3ZA5FqELYSs4v8
python exits 1 on a SyntaxError, so a broken push_hook.py used to block every push. The hook now aborts with 3 and the wrapper lets every other code through. Also rewraps the SECURITY.md line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TEvvb1DM3ZA5FqELYSs4v8
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.
Checks that the docs follow the code, in CI and before every push.
tools/docs/check_docs.py(stdlib only) reads the Markdown files tracked by git at a revision and reports:#anchorsthat do not resolve to a file in git (anchors computed like GitHub's heading slugs; all 137 existing anchors were compared with github.com);web/lib/setup-downloads.json;It found one real problem:
SECURITY.mdlinked../../security/advisories/new, which only works on github.com; now an absolute link.tools/githooks/pre-push(turn on once per clone withgit config core.hooksPath tools/githooks): git calls it at the real push (githooks) and it checks the tip commit of each pushed ref withcheck_docs. In Claude Code (CLAUDECODE=1) it also holds the push until the docs were compared with the change andpython tools/docs/push_hook.py --markwas run. Already-pushed commits are skipped, and every internal error fails open: only the hook's own exit code 3 aborts a push.A Claude Code
PreToolUsehook was the first version; three adversarial review rounds kept finding pushes that command-text parsing missed or commands it wrongly blocked, so it was replaced by git's own hook, which needs no parsing.CI: a new
docsjob runs the tests (102) and the check. It becomes a required check in themainruleset once this run has shown its name.Tested end to end: this branch's own push was held by the hook (not marked yet), then accepted after
--mark.🤖 Generated with Claude Code
https://claude.ai/code/session_01TEvvb1DM3ZA5FqELYSs4v8