Skip to content

Commit aee50fd

Browse files
Pigbibicodex
andcommitted
Close weekly workflow provenance review findings
Co-Authored-By: Codex <noreply@openai.com>
1 parent 7c551e6 commit aee50fd

9 files changed

Lines changed: 130 additions & 57 deletions

File tree

.github/workflows/rss_source_pipeline.yml

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ jobs:
6060
period_end_exclusive: ${{ steps.guard.outputs.period_end_exclusive }}
6161
as_of: ${{ steps.guard.outputs.as_of }}
6262
producer_ref: ${{ steps.guard.outputs.producer_ref }}
63+
source_attempt: ${{ steps.guard.outputs.source_attempt }}
6364
steps:
6465
- name: Validate run identity and period before checkout
6566
id: guard
@@ -91,7 +92,8 @@ jobs:
9192
payload = json.load(open(sys.argv[1], encoding="utf-8"))
9293
if os.environ["WORKFLOW_REF"] != expected_ref or os.environ["GITHUB_REF"] != "refs/heads/main":
9394
raise SystemExit("workflow_identity_invalid")
94-
if payload.get("id") != int(os.environ["RUN_ID"]) or type(payload.get("run_attempt")) is not int or payload["run_attempt"] != 1:
95+
attempt = int(os.environ["RUN_ATTEMPT"])
96+
if not 1 <= attempt <= 9007199254740991 or payload.get("id") != int(os.environ["RUN_ID"]) or type(payload.get("run_attempt")) is not int or payload["run_attempt"] != attempt:
9597
raise SystemExit("workflow_identity_invalid")
9698
if payload.get("event") != os.environ["EVENT_NAME"] or payload.get("path") != expected_path or payload.get("head_branch") != "main":
9799
raise SystemExit("workflow_identity_invalid")
@@ -109,7 +111,7 @@ jobs:
109111
as_of = end - timedelta(days=1)
110112
if os.environ["EVENT_NAME"] == "workflow_dispatch" and (os.environ["PERIOD_START"], os.environ["AS_OF"]) != (start.isoformat(), as_of.isoformat()):
111113
raise SystemExit("manual_period_not_immediate_prior")
112-
for key, value in (("period_start", start.isoformat()), ("period_end_exclusive", end.isoformat()), ("as_of", as_of.isoformat()), ("producer_ref", producer_ref)):
114+
for key, value in (("period_start", start.isoformat()), ("period_end_exclusive", end.isoformat()), ("as_of", as_of.isoformat()), ("producer_ref", producer_ref), ("source_attempt", str(attempt))):
113115
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as output:
114116
output.write(f"{key}={value}\n")
115117
PY
@@ -145,6 +147,7 @@ jobs:
145147
- name: Publish live CSV outputs to repository
146148
env:
147149
COMMIT_OUTPUTS: ${{ github.event_name == 'schedule' && 'true' || github.event.inputs.commit_outputs || 'false' }}
150+
GITHUB_TOKEN: ${{ github.token }}
148151
run: |
149152
set -euo pipefail
150153
if [ "${COMMIT_OUTPUTS}" != "true" ]; then echo "Live output commit disabled."; exit 0; fi
@@ -158,7 +161,17 @@ jobs:
158161
git config user.name "github-actions[bot]"
159162
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
160163
git add data/live
161-
if git diff --cached --quiet; then echo "No live RSS output changes to commit."; else git commit -m "Update live RSS source events [skip ci]"; git push; fi
164+
if git diff --cached --quiet; then
165+
echo "No live RSS output changes to commit."
166+
else
167+
git commit -m "Update live RSS source events [skip ci]"
168+
git config --local http.https://github.com/.extraheader "AUTHORIZATION: bearer ${GITHUB_TOKEN}"
169+
cleanup_git_auth() { git config --local --unset-all http.https://github.com/.extraheader >/dev/null 2>&1 || true; }
170+
trap cleanup_git_auth EXIT
171+
git push
172+
cleanup_git_auth
173+
trap - EXIT
174+
fi
162175
- name: Upload RSS source artifact
163176
uses: actions/upload-artifact@v7
164177
with:
@@ -172,7 +185,7 @@ jobs:
172185
run: |
173186
set -euo pipefail
174187
mkdir -p data/output/political-event-weekly-v1
175-
python scripts/build_weekly_artifact.py --period-start "${{ needs.validate-run-boundary.outputs.period_start }}" --as-of "${{ needs.validate-run-boundary.outputs.as_of }}" --generated-at "$(date -u +%Y-%m-%dT%H:%M:%S.000000Z)" --workflow-ref "${GITHUB_WORKFLOW_REF}" --source-run-id "${GITHUB_RUN_ID}" --producer-ref "${{ needs.validate-run-boundary.outputs.producer_ref }}" --source-events data/output/rss_source_pipeline/source_events.csv --watchlist "${WATCHLIST_PATH}" --feed-status data/output/rss_source_pipeline/source_fetch_status.json --output-dir data/output/political-event-weekly-v1 --run-mode "${RUN_MODE}"
188+
python scripts/build_weekly_artifact.py --period-start "${{ needs.validate-run-boundary.outputs.period_start }}" --as-of "${{ needs.validate-run-boundary.outputs.as_of }}" --generated-at "$(date -u +%Y-%m-%dT%H:%M:%S.000000Z)" --workflow-ref "${GITHUB_WORKFLOW_REF}" --source-run-id "${GITHUB_RUN_ID}" --source-attempt "${{ needs.validate-run-boundary.outputs.source_attempt }}" --producer-ref "${{ needs.validate-run-boundary.outputs.producer_ref }}" --source-events data/output/rss_source_pipeline/source_events.csv --watchlist "${WATCHLIST_PATH}" --feed-status data/output/rss_source_pipeline/source_fetch_status.json --output-dir data/output/political-event-weekly-v1 --run-mode "${RUN_MODE}"
176189
- name: Upload political weekly artifact
177190
uses: actions/upload-artifact@v7
178191
with:

scripts/build_weekly_artifact.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ def main() -> None:
2020
parser.add_argument("--generated-at", required=True)
2121
parser.add_argument("--workflow-ref", required=True)
2222
parser.add_argument("--source-run-id", required=True)
23+
parser.add_argument("--source-attempt", required=True, type=int)
2324
parser.add_argument("--producer-ref", required=True)
2425
parser.add_argument("--source-events", required=True, type=Path)
2526
parser.add_argument("--watchlist", required=True, type=Path)
@@ -34,6 +35,7 @@ def main() -> None:
3435
generated_at=datetime.fromisoformat(args.generated_at.replace("Z", "+00:00")),
3536
workflow_ref=args.workflow_ref,
3637
source_run_id=args.source_run_id,
38+
source_attempt=args.source_attempt,
3739
producer_ref=args.producer_ref,
3840
source_events=args.source_events.read_bytes(),
3941
watchlist=args.watchlist.read_bytes(),

scripts/validate_weekly_workflow.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,13 @@ def main() -> None:
3131
try:
3232
payload = json.loads(args.run_payload.read_text(encoding="utf-8"))
3333
if args.event == "schedule":
34-
evidence = validate_scheduled_run(payload, run_id=args.run_id, workflow_ref=args.workflow_ref)
34+
evidence = validate_scheduled_run(payload, run_id=args.run_id, workflow_ref=args.workflow_ref, run_attempt=args.run_attempt)
3535
start, end, as_of = evidence.period_start, evidence.period_end_exclusive, evidence.as_of
3636
else:
37-
evidence = validate_manual_run(payload, run_id=args.run_id, workflow_ref=args.workflow_ref)
37+
evidence = validate_manual_run(payload, run_id=args.run_id, workflow_ref=args.workflow_ref, run_attempt=args.run_attempt)
3838
start, as_of = validate_manual_period(args.period_start, args.as_of, run_created_at=evidence.created_at)
3939
end = start.fromordinal(start.toordinal() + 7)
40-
args.output.write_text(json.dumps({"period_start": start.isoformat(), "period_end_exclusive": end.isoformat(), "as_of": as_of.isoformat(), "producer_ref": evidence.producer_ref}, sort_keys=True, separators=(",", ":")) + "\n", encoding="utf-8")
40+
args.output.write_text(json.dumps({"period_start": start.isoformat(), "period_end_exclusive": end.isoformat(), "as_of": as_of.isoformat(), "producer_ref": evidence.producer_ref, "source_attempt": evidence.run_attempt}, sort_keys=True, separators=(",", ":")) + "\n", encoding="utf-8")
4141
except WorkflowBoundaryError as error:
4242
raise SystemExit(error.code) from None
4343
except (OSError, UnicodeError, json.JSONDecodeError, TypeError, ValueError):

src/political_event_tracking_research/rss_source_fetch.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,9 @@ def write_fetch_status(path: str | Path, statuses: list[FeedFetchStatus], *, ite
178178
"feed_count": len(statuses),
179179
"successful_feed_count": sum(1 for item in statuses if item.ok),
180180
"failed_feed_count": sum(1 for item in statuses if not item.ok),
181+
"stale_feed_count": 0,
182+
"missing_feed_count": 0,
183+
"complete": all(item.ok for item in statuses),
181184
"item_count": item_count,
182185
"feeds": [item.to_json() for item in statuses],
183186
}

0 commit comments

Comments
 (0)