Generative Engine Optimization pass so AI answer engines (ChatGPT, Gemini, Perplexity, AI Overviews) can confidently identify and cite MSOS. Schema (tools/seo_inject.py, propagated to all 231 pages): - FAQPage JSON-LD parsed from the /faq/ Q&A blocks (33 Q&A x EN/MK/SI) - Organization upgraded to NGO with entity-disambiguation fields: alternateName (all spellings), description, areaServed Slovenia, knowsLanguage, and foundingDate 2023-03-05 - Event JSON-LD on all 9 /projects/ recap pages (date + real location) Autonomous for future content (no code edits to add a project): - Event/Article dates auto-read from the English page's publish date; event city auto-detected from the body (Slovenian-city whitelist). ARTICLE_DATES/PROJECT_LOCATIONS are now optional overrides only. (Auto-detect corrected Paint & Wine to Nova Gorica.) - build_listings.py: auto-date fallback so undated new content still lists - pre-commit hook now runs seo_inject.py --all --sitemap first, then listings/botlinks, and re-stages changed pages (idempotent) Docs: docs/geo-offsite-worklist.md - copy-paste-ready off-site plan (Wikidata, Wikipedia fixes, directories, Google Business, AI-visibility tests). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
48 lines
2.3 KiB
Bash
Executable File
48 lines
2.3 KiB
Bash
Executable File
#!/bin/sh
|
|
# ---------------------------------------------------------------------------
|
|
# MSOS pre-commit hook — keeps derived files in sync automatically.
|
|
#
|
|
# On every commit it regenerates and stages:
|
|
# - the technical-SEO <head> block on every page + sitemap.xml/robots.txt
|
|
# (tools/seo_inject.py) — canonical, hreflang, meta, and the JSON-LD schema
|
|
# (Organization/NGO, Article, Event, FAQPage). Idempotent: unchanged pages
|
|
# produce no diff, so it only ever stages pages whose content actually moved.
|
|
# - the News hub grid + homepage "Latest news" (tools/build_listings.py)
|
|
# - /llms.txt and /links-for-bots/ (tools/build_botlinks.py)
|
|
# so adding a blog post or an event/project flows through to its schema, the
|
|
# sitemap, News, the homepage and the bot pages without any manual command.
|
|
#
|
|
# It never blocks a commit: if Python or a generator fails, it warns and lets
|
|
# the commit proceed.
|
|
#
|
|
# Activate once per clone: git config core.hooksPath tools/git-hooks
|
|
# ---------------------------------------------------------------------------
|
|
set -u
|
|
|
|
PY="$(command -v python || command -v python3 || true)"
|
|
if [ -z "$PY" ]; then
|
|
echo "pre-commit: Python not found — skipping News/bot regeneration." >&2
|
|
exit 0
|
|
fi
|
|
|
|
# Run from the repo root regardless of where git invoked the hook.
|
|
ROOT="$(git rev-parse --show-toplevel 2>/dev/null || echo .)"
|
|
cd "$ROOT" || exit 0
|
|
|
|
# SEO/schema first, so build_listings sees fresh dates and new pages are in the
|
|
# sitemap. --all rewrites every page's SEO block idempotently; --sitemap also
|
|
# refreshes sitemap.xml + robots.txt.
|
|
"$PY" tools/seo_inject.py --all --sitemap >/dev/null 2>&1 \
|
|
|| echo "pre-commit: seo_inject.py failed — SEO/schema not refreshed." >&2
|
|
"$PY" tools/build_listings.py >/dev/null 2>&1 \
|
|
|| echo "pre-commit: build_listings.py failed — News/homepage not refreshed." >&2
|
|
"$PY" tools/build_botlinks.py >/dev/null 2>&1 \
|
|
|| echo "pre-commit: build_botlinks.py failed — bot pages not refreshed." >&2
|
|
|
|
# Re-stage generated/derived outputs (no-op when unchanged). `git add -u` only
|
|
# touches already-tracked files, so an unrelated commit stages nothing new.
|
|
git add -u en mk si >/dev/null 2>&1 || true
|
|
git add -- sitemap.xml robots.txt llms.txt links-for-bots >/dev/null 2>&1 || true
|
|
|
|
exit 0
|