Samme indhold, URL'er og udseende, bygget af en enkelt zola-binær og Tailwind standalone-CLI uden Node. Kanonisk domæne er asger.weirsoe.dk. Blogindlæg ligger som page bundles med frontmatter Zola forstår, så de gamle stempler er ugyldige, indtil scripts/omstempl.py er kørt med PUBLISH_KEY. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
104 lines
4.1 KiB
Python
Executable File
104 lines
4.1 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 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 = stamp_of(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()
|