Regenerate episode pages and sitemap during publish

add_episode_to_sitemap() was removed in the previous commit, so nothing was
writing the sitemap any more. Publishing now shells out to
generate_episode_pages.py --sitemap right after the transcript is copied into
website/transcripts/, which builds the new episode's static page and rebuilds
the sitemap from the feed.

Deliberately non-fatal: by this point the audio is live on Castopod and the RSS
feed has been rebuilt, so a generator failure must not abort the publish. It
warns and moves on, and the page lands on the next run. Covered for non-zero
exit, timeout, and a missing generator script.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-14 04:12:29 -05:00
co-authored by Claude Opus 5
parent fa5ab9aad2
commit 2429a2eb07
2 changed files with 108 additions and 1 deletions
+80
View File
@@ -0,0 +1,80 @@
"""Publishing must rebuild the static episode pages and the sitemap.
Before static pages existed, publish_episode.py appended one entry to
sitemap.xml itself. That appender is gone — generate_episode_pages.py owns the
sitemap now — so the publish has to invoke it, or a newly published episode
would have no page and never reach the sitemap.
A generator failure must never abort a publish: by the time this runs the audio
is already live on Castopod and the RSS feed has been rebuilt.
"""
import subprocess
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
import publish_episode
class _Result:
def __init__(self, returncode=0, stderr=""):
self.returncode = returncode
self.stderr = stderr
self.stdout = ""
def test_invokes_the_generator_with_sitemap(monkeypatch):
calls = []
monkeypatch.setattr(subprocess, "run", lambda cmd, **kw: calls.append(cmd) or _Result())
assert publish_episode.regenerate_website_pages() is True
assert len(calls) == 1
cmd = calls[0]
assert "generate_episode_pages.py" in " ".join(cmd)
assert "--sitemap" in cmd
def test_uses_the_running_interpreter(monkeypatch):
"""Must not shell out to a bare 'python' that may not have the venv."""
calls = []
monkeypatch.setattr(subprocess, "run", lambda cmd, **kw: calls.append(cmd) or _Result())
publish_episode.regenerate_website_pages()
assert calls[0][0] == sys.executable
def test_generator_failure_is_not_fatal(monkeypatch):
monkeypatch.setattr(subprocess, "run",
lambda cmd, **kw: _Result(returncode=1, stderr="boom"))
assert publish_episode.regenerate_website_pages() is False
def test_generator_timeout_is_not_fatal(monkeypatch):
def _boom(cmd, **kw):
raise subprocess.TimeoutExpired(cmd, 300)
monkeypatch.setattr(subprocess, "run", _boom)
assert publish_episode.regenerate_website_pages() is False
def test_missing_generator_is_not_fatal(monkeypatch):
def _boom(cmd, **kw):
raise OSError("no such file")
monkeypatch.setattr(subprocess, "run", _boom)
assert publish_episode.regenerate_website_pages() is False
def test_publish_flow_calls_it_after_copying_the_transcript():
"""Guards the wiring, not just the helper."""
source = Path(publish_episode.__file__).read_text()
copy_at = source.index("Transcript copied to website/transcripts/")
call_at = source.index("regenerate_website_pages()", copy_at)
assert call_at > copy_at