feat(cms): U4 one-command site build + local preview

Add tools/build_site.py: runs the full pipeline in dependency order
(build_content -> build_listings -> seo_inject --all --sitemap ->
build_botlinks -> build_search_index), each as a subprocess so a failing
step aborts the build before anything downstream runs (no partial
deploy). --check verifies non-destructively; --images also runs
optimize_images. This is what CI will run in U5.

Fix a build_content idempotence bug found via U4: the hero and
article-header regions were emitted with leading indentation while the
regex left the template's pre-tag whitespace in place, so indentation
compounded (+16 spaces) on every rebuild. First line is now unindented;
the full build is verified idempotent (identical git tree across two
runs). Added a fixpoint test (re-rendering a page's own output is
byte-identical) so this can't regress.

docs/local-preview.md: build + serve on :8000 in all three languages.
tools/README.md: document build_site + build_content.

26 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
popovskikandClaude Opus 4.8 committed 2026-08-08 17:04:00 +02:00
1 parent 8ff968739a
commit 2d535db7d5
5 files changed
+175 -2

No files matched your search

+7
View File
@@ -8,6 +8,13 @@ so **adding a post no longer needs a manual `ARTICLE_DATES` edit** — the date
lives in the post's front-matter (`date` / `date_type`). See
`docs/content-model.md`.
## build_site.py
One command to build the whole site from `content/`, in dependency order
(content pages -> listings -> SEO/sitemap -> bot links -> search index). This is
what CI runs before deploying. `--check` verifies without writing; `--images`
also runs image optimisation. Idempotent. See `docs/local-preview.md`.
## build_content.py
Renders the `content/` source files into the blog/news/event detail pages