Files
pw/scripts/omstempl.py
T
Asger Geel WeirsøeandClaude Opus 5.5 41e7dacae6 fix: spærren godtager kun præcis stempellinjen
canonical() fjernede enhver linje med godkendt: fra det signerede, og Zola kan lukke
frontmatter midt på en linje. Sammen gav det plads til ugodkendt tekst i et gyldigt
stemplet indlæg. Nu er kun den linje tjenesten skriver usigneret, og kun én gang.

Bruger runnerens python3 i stedet for setup-python, som kun har builds til Ubuntu.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-01 16:47:08 +01:00

112 lines
4.5 KiB
Python
Executable File

#!/usr/bin/env python3
"""Stempler de udgivne indlæg om efter flytningen fra src/content/blog/<slug>.md til
content/blog/<slug>/index.md. Stien og frontmatter indgår i stemplet, så de gamle er ugyldige.
Kør på zola-branchen, med master stadig på Astro-versionen:
PUBLISH_KEY=<hex> python3 scripts/omstempl.py [--fra master]
PUBLISH_KEY=<hex> python3 scripts/verify-approvals.py
git commit -am "blog: stempl indlæg om efter flytning til Zola"
Scriptet underskriver kun det der allerede var godkendt. For hvert indlæg tjekker det at
det gamle stempel er gyldigt, og at den nye fil er præcis den gamle i nyt format. Fejler
ét indlæg, skrives der intet.
"""
import argparse
import hashlib
import hmac
import os
import re
import subprocess
import sys
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from godkendelse import BLOG, assets_for, read_text, sign, stamp_of # noqa: E402
OLD = "src/content/blog"
def git(*args, binary=False):
return subprocess.run(["git", *args], check=True, capture_output=True, text=not binary).stdout
def old_sign(key, rel, text, assets):
"""Det gamle format: stemplet stod som topniveau-linje og er det eneste der skæres fra."""
lines = text.replace("\r\n", "\n").split("\n")
end = lines.index("---", 1)
lines = [l for i, l in enumerate(lines) if not (0 < i < end and l.startswith("godkendt:"))]
tail = "".join(f"\n@asset {p} {h}" for p, h in assets)
body = f"{rel}\n" + "\n".join(lines) + tail
return "v1:" + hmac.new(key, body.encode(), hashlib.sha256).hexdigest()
def old_stamp(text):
"""(stempel eller None, draft) i det gamle format."""
head = text.split("\n---\n", 1)[0].split("\n")
g = next((l for l in head if l.startswith("godkendt:")), None)
stamp = re.sub(r"^godkendt:\s*", "", g).strip("'\"") if g else None
return stamp, any(re.fullmatch(r"draft:\s*true\s*", l) for l in head)
def migrated(text, stamp):
"""Den gamle fil i det nye format: tags under taxonomies, stemplet under extra."""
lines = text.split("\n")
end = lines.index("---", 1)
head = []
for l in lines[1:end]:
if l.startswith("godkendt:"):
continue
head += ["taxonomies:", " " + l] if l.startswith("tags:") else [l]
return "\n".join(["---", *head, "extra:", f' godkendt: "{stamp}"', *lines[end:]])
def old_assets(ref, slug):
out = git("ls-tree", "--name-only", ref, f"{OLD}/{slug}/").split()
return sorted((p, hashlib.sha256(git("show", f"{ref}:{p}", binary=True)).hexdigest()) for p in out)
def main():
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument("--fra", default="master", help="ref med de gamle, godkendte indlæg")
args = ap.parse_args()
if not os.environ.get("PUBLISH_KEY"):
sys.exit("PUBLISH_KEY mangler.")
key = bytes.fromhex(os.environ["PUBLISH_KEY"])
slugs = sorted(n for n in os.listdir(BLOG) if os.path.isdir(f"{BLOG}/{n}"))
new, errors = {}, []
for slug in slugs:
rel = f"{BLOG}/{slug}/index.md"
old_rel = f"{OLD}/{slug}.md"
try:
old = git("show", f"{args.fra}:{old_rel}")
except subprocess.CalledProcessError:
errors.append(f"{rel}: findes ikke i {args.fra} som {old_rel}; kun godkendelsesflowet må stemple nyt")
continue
text = read_text(rel)
stamp_gl, draft = old_stamp(old)
o_assets = old_assets(args.fra, slug)
n_assets = assets_for(slug)
if draft or not stamp_gl or not hmac.compare_digest(stamp_gl, old_sign(key, old_rel, old, o_assets)):
errors.append(f"{old_rel}: det gamle stempel er ikke gyldigt med denne nøgle")
continue
if text != migrated(old, stamp_of(text)[0]):
errors.append(f"{rel}: indholdet er ændret i forhold til {args.fra}")
continue
if [h for _, h in o_assets] != [h for _, h in n_assets]:
errors.append(f"{rel}: billederne er ændret i forhold til {args.fra}")
continue
new[rel] = migrated(old, sign(key, rel, text, n_assets))
if errors:
print("\n".join(f"✗ {e}" for e in errors), file=sys.stderr)
sys.exit(f"\n{len(errors)} fejl. Intet er skrevet.")
for rel, text in new.items():
with open(rel, "w", encoding="utf-8", newline="") as f:
f.write(text)
print(f"✓ {rel}")
print(f"\n{len(new)} indlæg stemplet om. Kør scripts/verify-approvals.py og commit.")
if __name__ == "__main__":
main()