docs: rebuild the documentation site on MkDocs Material - #516
Conversation
The site read as a repository rather than a product. Its homepage was the GitHub README included verbatim — eight CI badges above a hand-written table of contents — and everything behind it was reference material, with no path for somebody meeting the tool for the first time. Move to MkDocs Material, which is what ruff, uv, Pydantic and Typer use, and which sphinx-immaterial was a partial port of. The toolchain stays Python, so contributors still need nothing but pip, and the docs extras shrink from five Sphinx packages to one. Replace the homepage with an actual landing page: a value proposition, the same policy shown running as a CLI, a pre-commit hook, a GitHub Action and an MCP server, and cards linking each rule family to its reference. Badges belong on the README and stay there. Add the pages the site never had. Installation, a quick start that ends with a working local policy, and the reasoning behind the tool; then task-oriented guides for pre-commit, GitHub Actions, organization-wide config, DCO signoff, and AI attribution. The reference pages carry over converted to Markdown. Replace the JPEG logo with an SVG mark and add a favicon, neither of which the site previously had. Rule anchors move from RST explicit targets to heading attributes, so rule URLs become /rules/#cc003 rather than /rules.html#cc003. Every URL the Sphinx site served now redirects to its replacement, since links to them exist in the README, on PyPI and elsewhere. The publish step pins the CNAME explicitly so the custom domain cannot be lost when the branch is replaced. The anti-drift tests follow the content: they parse Markdown tables and heading anchors now, and still verify every documented default against get_default_config().
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Material's emoji extension is configured with `!!python/name:` tags, which yaml.safe_load cannot construct, so the hook fails on a file that is perfectly valid for the tool that consumes it. MkDocs parses and validates the same file on every build, and CI runs that build with --strict, so excluding it here loses no coverage.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## docs/left-sidebar-and-rules-reference #516 +/- ##
======================================================================
Coverage 97.43% 97.43%
======================================================================
Files 12 12
Lines 1207 1207
======================================================================
Hits 1176 1176
Misses 31 31 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Deploy previews stopped appearing on documentation pull requests. The build settings lived in Netlify's web UI and still pointed at Sphinx's _build/html, so the move to MkDocs left them publishing a directory that no longer exists — a break that nothing in the repository could show in review. Move the build into netlify.toml, so the preview build changes in the same commit as the toolchain it builds. Deploy previews also now set SITE_URL to their own address, and mkdocs.yml reads it. Otherwise the canonical links and the generated redirect stubs on a preview point back at the production site, which sends a reviewer away from the very change they are reviewing.
|
d684332
into
docs/left-sidebar-and-rules-reference
* docs: move navigation to the sidebar and expand the rules reference Replace the top tab bar with a left sidebar, and group the pages into Getting started / Configuring / Reference / About so the whole documentation set is visible at a glance instead of one tab at a time. On a rule page the sidebar also nests that page's own table of contents, putting every rule one click away. Rewrite the rules reference along the lines of a linter's rule documentation: an index table per category giving each rule's code, name, message, the CLI flag that runs it, and whether it is on by default, followed by a section per rule answering what it does, why it matters, and how to fix it, with a before/after example and the options that control it. Correct several defaults in the configuration reference that had drifted from the source: subject_capitalized and subject_imperative are off by default (documented as on), allow_empty_commits and allow_wip_commits are on (documented as off), and allow_commit_types was missing perf, build, and ci. The "Default Behavior" tip repeated the same mistake. Guard all of it with tests, so the documentation cannot drift again: documented boolean and list defaults are compared against the values in commit_check/__init__.py, and every rule must have a section heading that answers what it does, why it is bad, and which options apply. Also fix four reStructuredText title underlines that were shorter than their titles, letting the docs build cleanly under -W. * docs: correct the subject-length defaults and verify docs against the runtime The subject-length rules are on by default, not off. `get_default_config()` sets subject_max_length to 80 and subject_min_length to 5, and `ConfigMerger.from_all_sources()` starts from it, so with no config file at all a subject over 80 characters fails CC004. Both the rules reference and the configuration reference claimed there was no limit. The same review turned up four more inaccuracies: - author_email_pattern defaults to `^.+@.+$`, not an empty string. - require_rebase_target defaults to `""`, not None. - CC201's option is branch.conventional_branch; it was documented under the commit section, where configuration merging would ignore it. - required_signoff_name and required_signoff_email were documented in the example config and in CC012's options, but no such options exist anywhere in the code. Two rule examples also demonstrated values that pass the very check they illustrate: `root` satisfies the built-in author name pattern, and `root@localhost` satisfies `^.+@.+$`. Both now use values that actually fail, with a note explaining how permissive the built-in patterns are and when to replace them. Rework the drift guard so this class of error is caught mechanically. Rather than comparing against a couple of constants, the options table is now parsed and every row checked against `get_default_config()` — the same dict the CLI builds its configuration from. Two further tests assert the table and the runtime describe the same set of options, in both directions, which is what catches an invented option like required_signoff_name or a newly added one that nobody documented. Also scope the rule-index table styling to those tables so long regexes in the configuration table stay wrappable, and check each rule's heading inside its own section instead of anywhere on the page. * test: narrow the documented-default parser to a type mypy accepts The regex match may be None, so reading .group() from it before the None check failed mypy and took the lint session down with it. * test: stop benchmarking the documentation consistency checks CodSpeed reported test_every_rule_is_documented as a 27% regression. It is not one: that test reads docs/rules.rst and scans it for anchors, and this branch grows that file from 7.4 KB to 22.2 KB. A 2.99x larger file taking 1.37x longer to read is the expected, sub-linear result, and no runtime code changed on this branch at all. The marker is documented as meaning "performance-related tests", which these are not. Left in place, the performance gate would report a regression every time somebody expands the rules reference — penalising the act of writing documentation. The catalog and rule-builder tests in this file keep their marker; those do exercise the package. * docs: rebuild the documentation site on MkDocs Material (#516) * ci: expand DEPLOY_PRIME_URL where the shell can see it The deploy preview failed to build: ERROR - Config value 'site_url': The URL isn't valid, it should include the http:// (scheme) Values in a [context.*.environment] block are literals — Netlify does not interpolate them — so SITE_URL reached MkDocs as the unexpanded string "${DEPLOY_PRIME_URL}", which is indeed not a URL. Set it in the context's build command instead, which runs in a shell where the variable expands. The redirect hook now also tolerates a site_url without a trailing slash, since that is the form DEPLOY_PRIME_URL takes. * docs: fix the redirect loop and eleven review findings The deploy preview could not open /rules/. The build emits a redirect stub at rules.html for the old Sphinx URL, and Netlify normalises "/rules" and "/rules/" to the same resource, so that stub was served in place of the page it points at and redirected to itself forever. Turn the normalisation off and declare the legacy URLs as Netlify redirects, which resolve before file lookup and so cannot collide with the page they target. The emitted stubs stay for GitHub Pages, and now refuse to redirect a page to itself, so the same collision cannot reappear on another host. The stubs also carry the fragment across. The links most worth keeping alive are the per-rule ones — rules.html#cc003 — and a bare redirect dropped the anchor, landing the reader at the top of a 700-line page. From review, all verified against the source before fixing: - The RST converter missed four constructs: `.. Attention::` (it only matched lowercase), `.. tip ::` (a space before the colons), and two links whose text wrapped across a line. They were rendering as literal text. - The changelog documented `forbid_ai_attribution`, which does not exist; the option is `ai_attribution`, defaulting to "ignore". - The options table mapped `allow_force_push = true` to `CCHK_ALLOW_FORCE_PUSH=false`, describing opposite behaviour for equivalent settings. - Troubleshooting had its two bypass headings the wrong way round: --no-verify skips every hook, SKIP= skips one. - Link colour was #1e85a8 on white, 4.2:1, under the 4.5:1 needed for body text. Now #176b89 at 6.0:1. - The landing page claimed attestation "verified at install time"; only a manual verification step exists, so it now says what is true. - The organization guide said "three lines" above a one-line example. - pr-comments cannot post on fork pull requests, because a fork's `pull_request` run gets a read-only token that `pull-requests: write` does not override. Documented, along with why `pull_request_target` is not a casual substitute. * docs: add the original logo as a vectorized SVG The previous logo.svg was a new line-art icon that did not match the project's actual logo. Vectorize docs/_static/logo.jpg (a 900x504 PNG in the old Sphinx site) with potrace instead, so the SVG is pixel-identical to the original: the 'commit' wordmark, the check mark and the branch shape in the brand color #2C9CCD. The old logo.jpg could not be used as-is on the new site: Sphinx's conf.py had it commented out ('can not display well in blue background'), it carried 10 KB of transparent padding around a 300x293 mark, and the new header renders the logo via <img>, where currentColor would resolve to black. The traced SVG keeps the exact shapes with a hardcoded fill, so it renders identically in the light and dark palettes. * docs: put the logo on a white tile so it shows on the blue header The header background is the brand color #2c9ccd (--md-primary-fg-color), the same blue the logo is filled with, so the logo was invisible. This is the same reason the old Sphinx site had html_logo commented out ('can not display well in blue background'). Keep the original blue logo unchanged and give it a white rounded tile. On the blue header the tile makes the logo visible; in the drawer, which sits on a light or dark gray background, the blue logo shows directly.



Note
Stacked on #515 — that is the base branch, so this diff shows only the migration. Merge #515 first and this retargets to
maincleanly.Why
The site read as a repository, not a product. The homepage of
docs.commit-check.comwas the GitHub README included verbatim —docs/index.mdwas a one-lineincludeof../README.md.So the front door was eight CI badges above a hand-written
## Table of Contents. Badges are a trust signal for GitHub visitors and a ToC is a workaround for GitHub's lack of navigation — neither belongs on a product's documentation site. Behind it, every page was reference material: no installation page, no tutorial, no task-oriented guides.Framework
MkDocs Material, for reasons specific to this project:
pyproject.toml, CI stays a singlepip install, and contributors need no Node — which matters while we are actively recruiting them through good-first-issues.sphinx-immaterialwas a partial port of it, so the palette, admonitions and feature flags carried over almost unchanged. Docs extras drop from five Sphinx packages to one.What changed
A real landing page. Value proposition, the same policy shown running four ways (CLI / pre-commit / GitHub Actions / MCP) in linked tabs, and cards routing each rule family to its reference.
The pages the site never had:
The quick start is a genuine tutorial: install, watch a real failure, read what each part of the diagnostic is for, fix it, then write the policy down.
Brand. The 10 KB JPEG logo becomes a 483-byte SVG, and there is a favicon for the first time — the Sphinx config had it commented out.
Content. All RST converted to Markdown. Cross-document
:ref:targets were rewritten to real cross-page links; converting them naively would have produced same-page anchors that silently go nowhere.Three things that could have broken the live site
Rule URLs. MkDocs serves directory URLs, so anchors move from
/rules.html#cc003to/rules/#cc003.RULES_DOCS_URLis updated to match. This is safe to change now only because the rule-ID feature has not shipped to PyPI yet — no released version emits the old form.Everything else that was published. Links to
configuration.html,rules.htmland friends exist in the README, on PyPI and outside this repository. A build hook emits a redirect stub for every URL the Sphinx site served — each one acanonicallink plus arefreshmeta pointing at the new location, e.g.rules.html→https://docs.commit-check.com/rules/.The custom domain.
gh-pagescarries aCNAMEofdocs.commit-check.com, and the publish step force-replaces that branch.cname:is now pinned explicitly on the action rather than relying on the file surviving.Tests
The anti-drift guards follow the content rather than being dropped. They now parse Markdown pipe tables and
{ #ccNNN }heading attributes, and still check every documented default againstget_default_config().18/18 rule anchors present in the rendered
rules/index.html; the CLI page is generated fromcommit-check --helpat build time, replacing the oldconf.pysetup()hook.The one failing test,
test_load_config_file_permission_error, reproduces onmainand is unrelated — it usesos.chmod(0o000), which does not restrict root.Not in this PR
example.mdandwhat-is-new.mdare carried over converted but not rewritten. They overlap the new guides and changelog respectively, and folding them in is a content decision worth making separately.