#!/usr/bin/env python3 """Static SEO guardrails run in CI before deployment.""" from pathlib import Path import re import sys ROOT = Path(__file__).resolve().parents[1] WEB = ROOT / "src" / "TaxBaik.Web" errors: list[str] = [] def read(relative: str) -> str: return (ROOT / relative).read_text(encoding="utf-8") def require(condition: bool, message: str) -> None: if not condition: errors.append(message) sitemap_view = read("src/TaxBaik.Web/Pages/Sitemap.cshtml") rss_view = read("src/TaxBaik.Web/Pages/Rss.cshtml") feed_view = read("src/TaxBaik.Web/Pages/Feed.cshtml") sitemap_model = read("src/TaxBaik.Web/Pages/Sitemap.cshtml.cs") layout = read("src/TaxBaik.Web/Pages/_Layout.cshtml") robots = read("src/TaxBaik.Web/wwwroot/robots.txt") require("Layout = null" in sitemap_view, "sitemap.xml must render without the HTML layout") require("Layout = null" in rss_view, "rss.xml must render without the HTML layout") require("Html.Raw(rssContent.ToString())" in rss_view, "RSS XML must not be HTML-encoded") require("Model.Items" in rss_view, "RSS view must render from the combined feed items") require("StatusCodes.Status308PermanentRedirect" in feed_view, "feed.xml must permanently redirect to rss.xml") require("DateTime.UtcNow:yyyy-MM-dd" not in sitemap_view, "sitemap lastmod must not use the current request date") require("GetAllPublishedPostsAsync" in sitemap_model, "sitemap must page through all published blog posts") require("/faq" not in sitemap_model and "/announcement" not in sitemap_model and "/inquiry" not in sitemap_model, "redirect-only URLs must not be included in the sitemap") require("var canonicalUrl" in layout and "Context.Request.Path" in layout, "default canonical URL must use the current path") sitemap_directives = re.findall(r"^Sitemap:\s*(\S+)\s*$", robots, flags=re.MULTILINE) require(sitemap_directives == ["https://www.taxbaik.com/sitemap.xml"], "robots.txt must declare exactly one canonical sitemap URL") require(not re.search(r"^Disallow:\s*.*\.xml", robots, flags=re.MULTILINE), "robots.txt must not block XML feeds or the sitemap") if errors: print("SEO guardrail validation failed:", file=sys.stderr) for error in errors: print(f"- {error}", file=sys.stderr) sys.exit(1) print("SEO guardrail validation passed")