feat: add trigger-podcast workflow for manual podcast generation (#496)

Allows re-triggering podcast generation for an already-published week without running the full crawl pipeline. Takes week, article_url, and article_path as inputs and calls the handoff script in real mode. Co-authored-by: jmservera <jmservera@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Juan Manuel Servera committed Jun 15, 2026 at 21:27 UTC 50af9c1d4365108cfa8bbe67068859a2d6d52e11
1 file changed +115
.github/workflows/trigger-podcast.yml new
+115
@@ -0,0 +1,115 @@
1 +name: Trigger podcast generation
2 +
3 +on:
4 + workflow_dispatch:
5 + inputs:
6 + week:
7 + description: 'Week slug, e.g. 2026-W25.'
8 + required: true
9 + type: string
10 + article_url:
11 + description: 'Published SquadScope article URL.'
12 + required: true
13 + type: string
14 + article_path:
15 + description: 'Article content path, e.g. content/weekly/2026/W25.md.'
16 + required: true
17 + type: string
18 +
19 +permissions:
20 + contents: read
21 +
22 +jobs:
23 + trigger-podcast:
24 + runs-on: ubuntu-latest
25 + permissions:
26 + contents: read
27 +
28 + steps:
29 + - name: Check out repository
30 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
31 + with:
32 + persist-credentials: false
33 +
34 + - name: Set up Python
35 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
36 + with:
37 + python-version: '3.12'
38 +
39 + - name: Build manifest and trigger podcast generation
40 + env:
41 + PODCASTER_ENDPOINT: ${{ vars.PODCASTER_ENDPOINT }}
42 + PODCASTER_API_KEY: ${{ secrets.PODCASTER_API_KEY }}
43 + WEEK: ${{ inputs.week }}
44 + ARTICLE_URL: ${{ inputs.article_url }}
45 + ARTICLE_PATH: ${{ inputs.article_path }}
46 + PUBLISH_RUN_ID: ${{ github.run_id }}
47 + run: |
48 + set -euo pipefail
49 + if [ -z "$PODCASTER_ENDPOINT" ] || [ -z "$PODCASTER_API_KEY" ]; then
50 + echo "::error::Requires PODCASTER_ENDPOINT variable and PODCASTER_API_KEY secret."
51 + exit 1
52 + fi
53 + if [ ! -f "$ARTICLE_PATH" ]; then
54 + echo "::error::article_path '$ARTICLE_PATH' does not exist in the repository."
55 + exit 1
56 + fi
57 +
58 + # Build a minimal publish manifest for the handoff script
59 + mkdir -p .podcast-trigger
60 + python3 - <<'PY' "$WEEK" "$ARTICLE_PATH" .podcast-trigger/publish-manifest.json
61 + import hashlib
62 + import json
63 + import sys
64 + from pathlib import Path
65 +
66 + week, article_path, manifest_path = sys.argv[1:]
67 + article = Path(article_path)
68 + article_bytes = article.read_bytes()
69 + article_sha = hashlib.sha256(article_bytes).hexdigest()
70 + raw_payload = {"week": week, "source": "github", "article_path": article_path}
71 + raw_bytes = (json.dumps(raw_payload, sort_keys=True) + "\n").encode("utf-8")
72 + raw_sha = hashlib.sha256(raw_bytes).hexdigest()
73 +
74 + manifest = {
75 + "week": week,
76 + "run_mode": "normal",
77 + "candidate": {"summary_sha256": article_sha},
78 + "analysis": {"ai_status": "ai"},
79 + "promotion": {"eligible": True, "decision": "promote"},
80 + "source_artifacts": [
81 + {
82 + "role": "raw_github",
83 + "path": f"data/raw/{week}.json",
84 + "name": f"{week}-raw-github",
85 + "sha256": raw_sha,
86 + "generated_at": "2026-01-01T00:00:00Z",
87 + "source_status": "fresh",
88 + "exists": True,
89 + "size_bytes": len(raw_bytes),
90 + "freshness": {"status": "fresh", "reasons": []},
91 + "provenance": {"path": f"data/raw/{week}.json", "sha256": raw_sha},
92 + },
93 + {
94 + "role": "published_summary",
95 + "path": article_path,
96 + "sha256": article_sha,
97 + "generated_at": "2026-01-01T00:00:00Z",
98 + "exists": True,
99 + "size_bytes": len(article_bytes),
100 + "provenance": {"path": article_path, "sha256": article_sha},
101 + },
102 + ],
103 + }
104 + Path(manifest_path).write_text(json.dumps(manifest, indent=2) + "\n", encoding="utf-8")
105 + PY
106 +
107 + # Trigger real podcast generation (no --podcaster-dry-run)
108 + python3 scripts/podcaster_handoff.py \
109 + --week "$WEEK" \
110 + --article-url "$ARTICLE_URL" \
111 + --article-path "$ARTICLE_PATH" \
112 + --publish-run-id "$PUBLISH_RUN_ID" \
113 + --publish-mode normal \
114 + --manifest .podcast-trigger/publish-manifest.json \
115 + --podcast-config config/podcast.json