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> |
||
|---|---|---|
| .. | ||
| __pycache__ | ||
| git-hooks | ||
| tests | ||
| README.md | ||
| build_blog.py | ||
| build_botlinks.py | ||
| build_content.py | ||
| build_listings.py | ||
| build_search_index.py | ||
| build_site.py | ||
| content_index.py | ||
| migrate_pages_to_content.py | ||
| seo_inject.py | ||
README.md
tools/
content_index.py
Reads the content/ source tree (front-matter) as structured data. Its
load_dates() builds the article date registry consumed by seo_inject.py,
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
(--check verifies the output matches the current site; --write emits pages).
See docs/content-model.md and the no-code CMS plan in docs/plans/.
seo_inject.py
Injects a technical-SEO block into every language page (en|mk|si/**/index.html)
and regenerates sitemap.xml + robots.txt. Safe to re-run — the block lives
between <!-- SEO:START --> and <!-- SEO:END --> and is replaced in place.
The block adds: meta description (auto-extracted from the page's lead/subtitle
where available), canonical, hreflang alternates (en / mk / sl / x-default),
Open Graph, Twitter Card, and an Organization JSON-LD.
All absolute URLs point at the final domain https://msosorg.com (not the
temporary host), so nothing needs rewriting at launch. Note: the Slovene folder
is /si/ but the ISO code is sl — the script maps si → sl for hreflang/locale.
Usage
python tools/seo_inject.py # dry run: print the block for pilot pages
python tools/seo_inject.py --all # write the block into every page
python tools/seo_inject.py --all --sitemap # also rebuild sitemap.xml + robots.txt
Run --all --sitemap after adding, renaming, or deleting any page so canonical
tags, hreflang alternates, and the sitemap stay in sync.