Skip to content
This repository was archived by the owner on Aug 4, 2026. It is now read-only.

Commit 4291bc7

Browse files
committed
docs: publish redirects to commit-check.com ahead of archiving
The site moved to commit-check.com, but this domain still serves a full copy of the old one. Archiving alone would not change that: it makes the repository read-only and stops Actions, and does not unpublish Pages — whatever was deployed last keeps being served. Archiving as the first step would freeze a duplicate of the pre-v2.13.0 documentation online permanently, competing with the real site and showing rule names the tool no longer prints. So the redirects go up first, and the archive switch is flipped afterwards. There is no second chance: Actions do not run on an archived repository. Replaces the mkdocs build with scripts/build_redirects.py, which emits one stub per URL the old site served. The map came from that site's own build output rather than from reading the config, and all but one entry is the same path — the pages, the blog and its archive, author and category indexes carried over unchanged, and the posts kept their filenames and created dates, so the slugs match exactly. /projects/ is the exception, folded into the Ecosystem section of the new landing page. GitHub Pages has no redirect table, so each stub is a rel=canonical plus a meta refresh, and the script carries the fragment across so deep links keep their place. A 404.html catches anything the map missed. tests/ checks the map against a real mkdocs build in both directions, and CI runs it before the deploy: a URL served with no redirect is a link that breaks for good, and a redirect for a URL that was never served means the map drifted. The docs/ directory stays as the historical source. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U9zFxq8V4qxG4aMzJhGBFn
1 parent b405bde commit 4291bc7

4 files changed

Lines changed: 239 additions & 7 deletions

File tree

.github/workflows/deploy.yml

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
1-
name: Deploy Documentation
1+
name: Deploy redirects
2+
3+
# This site moved to https://commit-check.com. What this repository publishes
4+
# is a set of redirect stubs, built by scripts/build_redirects.py rather than
5+
# by mkdocs.
6+
#
7+
# The deploy matters more than usual: once this repository is archived, Actions
8+
# stop running and Pages keeps serving the last artifact indefinitely. The
9+
# redirect map is therefore checked on every run — a URL left out is a link
10+
# that breaks permanently.
11+
212
on:
313
push:
414
branches: [main]
@@ -19,10 +29,20 @@ jobs:
1929
with:
2030
python-version: '3.x'
2131

22-
- name: Install Dependencies
23-
run: pipx run nox -s docs
32+
# mkdocs is still needed: the check builds the old site and compares the
33+
# URLs it produces against the redirect map.
34+
- name: Install dependencies
35+
run: |
36+
python -m pip install --upgrade pip
37+
python -m pip install pytest -r docs/requirements.txt
38+
39+
- name: Check every old URL still has a forwarding address
40+
run: pytest tests/ -q
41+
42+
- name: Build redirects
43+
run: python scripts/build_redirects.py
2444

25-
- name: Upload docs build as artifact
45+
- name: Upload redirects as artifact
2646
uses: actions/upload-pages-artifact@v5
2747
with:
2848
name: ${{ github.event.repository.name }}_docs

README.md

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,43 @@
1-
# commit-check.github.io
1+
# commit-check.github.io — moved to commit-check.com
22

3-
[![Website](https://img.shields.io/static/v1?label=Website&message=commit-check.github.io&color=2c9ccd&logo=git&logoColor=white)](https://commit-check.github.io)
3+
> [!IMPORTANT]
4+
> **This repository is no longer the source of the Commit Check website.**
5+
>
6+
> The documentation, the landing page and the blog now live at
7+
> **[commit-check.com](https://commit-check.com)**, built from
8+
> **[commit-check/commit-check.com](https://github.com/commit-check/commit-check.com)**.
9+
>
10+
> Open issues and pull requests about the website there, not here.
411
5-
This repository contains the source code for the [commit-check.github.io](https://commit-check.github.io) website.
12+
[![Website](https://img.shields.io/static/v1?label=Website&message=commit-check.com&color=2c9ccd&logo=git&logoColor=white)](https://commit-check.com)
13+
14+
## What this repository is now
15+
16+
It is kept as the historical source of the old site. The `docs/` directory is
17+
still here and still readable, but it is no longer published.
18+
19+
What `commit-check.github.io` serves is a set of redirect stubs — one for every
20+
URL the old site had — pointing at the page that replaced it. The pages, the
21+
blog and its archive, author and category indexes all kept their paths, so the
22+
redirects are one-to-one; `/projects/` is the exception, having been folded into
23+
the Ecosystem section of the new landing page.
24+
25+
| Old URL | Now |
26+
|---|---|
27+
| `commit-check.github.io/` | [commit-check.com/](https://commit-check.com/) |
28+
| `commit-check.github.io/getting-started/` | [commit-check.com/getting-started/](https://commit-check.com/getting-started/) |
29+
| `commit-check.github.io/blog/…` | [commit-check.com/blog/…](https://commit-check.com/blog/) — same paths |
30+
| `commit-check.github.io/projects/` | [commit-check.com/](https://commit-check.com/) |
31+
32+
The redirects are generated by `scripts/build_redirects.py`, and `tests/`
33+
verifies the map still covers every URL the old site served. GitHub Pages has
34+
no server-side redirect table, so each stub is a `rel=canonical` plus a
35+
`<meta refresh>` — the canonical is what moves search ranking to the new URL.
36+
37+
## Why the redirects come before the archive
38+
39+
Archiving a repository makes it read-only and stops its Actions from running,
40+
but it does **not** unpublish its GitHub Pages site: whatever was deployed last
41+
keeps being served. So the redirects have to be deployed *before* the archive
42+
switch is flipped. Afterwards there is no way to change what this domain serves
43+
without unarchiving first.

scripts/build_redirects.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
"""Build the redirect-only site this repository now publishes.
2+
3+
The site moved to https://commit-check.com. This repository is kept as the
4+
historical source — ``docs/`` is still here and still readable — but what it
5+
publishes is a set of redirect stubs, one per URL the old site served, so links
6+
already out in the world land on the page that replaced them.
7+
8+
Why stubs rather than a server redirect: GitHub Pages serves static files and
9+
has no redirect table, so a ``<meta refresh>`` plus a ``rel=canonical`` is the
10+
only mechanism available. The canonical link is what transfers search ranking
11+
to the new URL; the meta refresh and the script are what move a reader.
12+
13+
Why this runs instead of ``mkdocs build``: once this repository is archived,
14+
Actions stop running and the last deployed artifact is what Pages serves
15+
forever. That artifact needs to be the redirects, so the redirects have to be
16+
deployed *before* the archive switch is flipped, not after.
17+
18+
Run with ``python scripts/build_redirects.py`` — output goes to ``site/``.
19+
"""
20+
21+
from __future__ import annotations
22+
23+
import sys
24+
from pathlib import Path
25+
26+
NEW_SITE = "https://commit-check.com"
27+
28+
#: Every URL the mkdocs site served, taken from its own build output, mapped to
29+
#: the path that replaced it on the new site.
30+
#
31+
# All but one are the same path: the pages, the blog, its archive, author and
32+
# category indexes were carried over unchanged, and the posts kept their
33+
# filenames and ``created`` dates, so the generated slugs match byte for byte.
34+
#
35+
# ``/projects/`` is the exception. It was folded into the Ecosystem section of
36+
# the new landing page, so it redirects to the root rather than to a page that
37+
# does not exist.
38+
SAME_PATH = [
39+
"/",
40+
"/getting-started/",
41+
"/blog/",
42+
"/blog/2026/06/21/ai-native-json-output-and-a-python-api/",
43+
"/blog/2026/06/21/from-zero-config-to-org-wide-policy/",
44+
"/blog/2026/06/21/one-policy-file-for-your-git-history/",
45+
"/blog/2026/07/06/ai-attribution-governance-enforcing-ai-disclosure-policies-at-the-ci-level/",
46+
"/blog/archive/2026/",
47+
"/blog/author/team/",
48+
"/blog/category/announcements/",
49+
"/blog/category/updates/",
50+
]
51+
52+
REDIRECTS = {path: path for path in SAME_PATH} | {"/projects/": "/"}
53+
54+
# The fragment is carried across by the script: a reader following a deep link
55+
# into a page should keep their place. ``location.replace`` rather than
56+
# ``location.href`` so the stub does not land in the back-button history and
57+
# trap them in a loop between the two sites.
58+
TEMPLATE = """<!doctype html>
59+
<html lang="en">
60+
<head>
61+
<meta charset="utf-8">
62+
<meta name="viewport" content="width=device-width, initial-scale=1">
63+
<title>Moved to commit-check.com</title>
64+
<link rel="canonical" href="{target}">
65+
<meta name="robots" content="noindex, follow">
66+
<meta http-equiv="refresh" content="0; url={target}">
67+
<script>location.replace("{target}" + location.hash);</script>
68+
<style>
69+
body {{ font-family: system-ui, sans-serif; margin: 4rem auto; max-width: 34rem;
70+
padding: 0 1rem; line-height: 1.6; }}
71+
a {{ color: #2c9ccd; }}
72+
</style>
73+
</head>
74+
<body>
75+
<h1>This site has moved</h1>
76+
<p>The Commit Check documentation, landing page and blog are now published at
77+
<a href="{target}">{target}</a>.</p>
78+
<p>If you are not redirected automatically, follow the link above.</p>
79+
</body>
80+
</html>
81+
"""
82+
83+
84+
def main() -> int:
85+
site = Path(__file__).resolve().parent.parent / "site"
86+
for old, new in REDIRECTS.items():
87+
target = NEW_SITE + new
88+
page = site / old.strip("/") / "index.html"
89+
page.parent.mkdir(parents=True, exist_ok=True)
90+
page.write_text(TEMPLATE.format(target=target), encoding="utf-8")
91+
92+
# Pages serves this for any path with no file of its own, which covers the
93+
# URLs this list missed — a stray deep link, a page from an older layout.
94+
# It points at the new site's root because there is nothing better to guess.
95+
(site / "404.html").write_text(
96+
TEMPLATE.format(target=NEW_SITE + "/"), encoding="utf-8"
97+
)
98+
99+
# Without this, Pages runs the output through Jekyll, which skips files and
100+
# directories whose names begin with an underscore.
101+
(site / ".nojekyll").write_text("", encoding="utf-8")
102+
103+
print(f"wrote {len(REDIRECTS)} redirects + 404 fallback to {site}")
104+
return 0
105+
106+
107+
if __name__ == "__main__":
108+
sys.exit(main())

tests/redirects_test.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
"""Check the redirect map against the site it is replacing.
2+
3+
This repository publishes redirects now, and once it is archived the artifact
4+
from the last successful run is what GitHub Pages serves for good — Actions do
5+
not run on an archived repository, so there is no second chance to fix a URL
6+
that was left out. That makes the map worth checking while it can still change.
7+
8+
The check builds the mkdocs site the old way and compares the URLs it produces
9+
against the redirect map, in both directions: a URL the old site served with no
10+
redirect is a link that will break, and a redirect for a URL the old site never
11+
served is a sign the map was edited by hand and drifted.
12+
"""
13+
14+
from __future__ import annotations
15+
16+
import subprocess
17+
import sys
18+
import tempfile
19+
from pathlib import Path
20+
21+
import pytest
22+
23+
ROOT = Path(__file__).resolve().parent.parent
24+
sys.path.insert(0, str(ROOT / "scripts"))
25+
26+
from build_redirects import REDIRECTS # noqa: E402
27+
28+
29+
def _urls_the_old_site_served() -> set[str]:
30+
"""Build the mkdocs site and return every URL it publishes."""
31+
with tempfile.TemporaryDirectory() as tmp:
32+
result = subprocess.run(
33+
[sys.executable, "-m", "mkdocs", "build", "--site-dir", tmp],
34+
cwd=ROOT,
35+
capture_output=True,
36+
text=True,
37+
)
38+
if result.returncode != 0:
39+
pytest.skip(f"mkdocs build unavailable: {result.stderr.strip()[:200]}")
40+
site = Path(tmp)
41+
urls = set()
42+
for page in site.rglob("index.html"):
43+
rel = page.parent.relative_to(site).as_posix()
44+
# ``relative_to`` gives "." for the site root, which is "/".
45+
urls.add("/" if rel == "." else f"/{rel}/")
46+
return urls
47+
48+
49+
def test_every_published_url_has_a_redirect():
50+
"""Nothing the old site served may be left without a forwarding address."""
51+
missing = sorted(_urls_the_old_site_served() - set(REDIRECTS))
52+
assert not missing, (
53+
"these URLs are served by the current site but have no redirect, so "
54+
"they will break when this repository is archived:\n "
55+
+ "\n ".join(missing)
56+
)
57+
58+
59+
def test_no_redirect_points_at_a_url_that_never_existed():
60+
"""A redirect for a URL the site never served means the map drifted."""
61+
served = _urls_the_old_site_served()
62+
invented = sorted(set(REDIRECTS) - served)
63+
assert not invented, (
64+
"these redirects are for URLs the site does not serve:\n "
65+
+ "\n ".join(invented)
66+
)

0 commit comments

Comments
 (0)