1- # appends the dethrone row to competition/LEADERBOARD.md after a win lands
2- # on the ladder branch. the scoring gates stay read-only by design (the
3- # engine gate holds no write token at all); this workflow never runs on a
4- # PR event — only on push to the ladder branch, after the merge, from
5- # base-branch code — so an untrusted PR can never reach its write token.
6- # idempotent: update_leaderboard.py no-ops when the PR is already cited,
7- # so the workflow triggering on its own ledger push converges.
1+ # appends dethrone rows to competition/LEADERBOARD.md for merged ladder
2+ # PRs. the scoring gates stay read-only by design (the engine gate holds
3+ # no write token at all); this workflow never runs on a PR event, so an
4+ # untrusted PR can never reach its write token.
5+ #
6+ # it is a SWEEP, not a per-push hook: the gate arms auto-merge with the
7+ # workflow GITHUB_TOKEN, and github's recursion guard suppresses workflow
8+ # triggers for pushes caused by that token — so an auto-merged dethrone
9+ # never fires a push event here (round 1, PR #566, proved it live). the
10+ # push trigger still catches human-merged rows immediately; the schedule
11+ # and manual dispatch pick up auto-merged ones. update_leaderboard.py is
12+ # idempotent per PR, so overlapping sweeps converge instead of duplicating.
813name : koth-ledger
914on :
1015 push :
16+ schedule :
17+ - cron : " 17 */6 * * *"
18+ workflow_dispatch :
1119permissions : {}
1220concurrency :
13- group : koth-ledger-${{ github.ref_name }}
21+ group : koth-ledger
1422 cancel-in-progress : false
1523jobs :
1624 ledger :
1725 if : >-
1826 vars.KOTH_LADDER_BASE != '' &&
19- github.ref_name == vars.KOTH_LADDER_BASE &&
20- !startsWith(github.event.head_commit.message, 'docs(competition): ledger row')
27+ (github.event_name != 'push' ||
28+ (github.ref_name == vars.KOTH_LADDER_BASE &&
29+ !startsWith(github.event.head_commit.message, 'docs(competition): ledger row')))
2130 runs-on : ubuntu-latest
2231 permissions :
2332 contents : write
2433 pull-requests : read
2534 steps :
2635 - uses : actions/checkout@v4
2736 with :
28- ref : ${{ github.ref_name }}
37+ ref : ${{ vars.KOTH_LADDER_BASE }}
2938
30- - name : find the merged pr and its scorecard comment
31- id : scorecard
39+ - name : append rows for recently merged ladder prs
3240 env :
3341 GH_TOKEN : ${{ github.token }}
34- SHA : ${{ github.sha }}
42+ LADDER : ${{ vars.KOTH_LADDER_BASE }}
3543 run : |
36- pr_json="$(gh api "repos/${GITHUB_REPOSITORY}/commits/${SHA}/pulls" \
37- --jq '[.[] | select(.merged_at != null)][0] // empty')"
38- if [ -z "$pr_json" ]; then
39- echo "no merged pr for ${SHA}; nothing to ledger"
40- echo "found=false" >> "$GITHUB_OUTPUT"
41- exit 0
42- fi
43- pr_number="$(printf '%s' "$pr_json" | jq -r '.number')"
44- pr_author="$(printf '%s' "$pr_json" | jq -r '.user.login')"
45- # only the gate's own comments are trusted: a scorecard-shaped
46- # comment from anyone else must never reach the ledger
47- gh api "repos/${GITHUB_REPOSITORY}/issues/${pr_number}/comments" \
48- --paginate \
49- --jq '.[] | select(.user.login == "github-actions[bot]") | .body' \
50- > /tmp/comments.txt
51- python3 - <<'PYEOF'
44+ gh pr list --repo "$GITHUB_REPOSITORY" --base "$LADDER" \
45+ --state merged --limit 15 --json number,author \
46+ --jq '.[] | "\(.number) \(.author.login)"' > /tmp/merged.txt
47+ appended=0
48+ while read -r pr author; do
49+ [ -n "$pr" ] || continue
50+ # only the gate's own comments are trusted: a scorecard-shaped
51+ # comment from anyone else must never reach the ledger
52+ gh api "repos/${GITHUB_REPOSITORY}/issues/${pr}/comments" \
53+ --paginate \
54+ --jq '.[] | select(.user.login == "github-actions[bot]") | .body' \
55+ > /tmp/comments.txt || continue
56+ rm -f /tmp/report.json
57+ python3 - <<'PYEOF'
5258 import re
5359
5460 body = open("/tmp/comments.txt", encoding="utf-8").read()
@@ -58,35 +64,22 @@ jobs:
5864 with open("/tmp/report.json", "w", encoding="utf-8") as fh:
5965 fh.write(reports[-1])
6066 PYEOF
61- if [ ! -f /tmp/report.json ]; then
62- echo "no gate scorecard with a dethrone on pr ${pr_number}"
63- echo "found=false" >> "$GITHUB_OUTPUT"
64- exit 0
65- fi
66- echo "found=true" >> "$GITHUB_OUTPUT"
67- echo "pr=${pr_number}" >> "$GITHUB_OUTPUT"
68- echo "author=${pr_author}" >> "$GITHUB_OUTPUT"
67+ if [ -f /tmp/report.json ]; then
68+ python3 .github/scripts/update_leaderboard.py \
69+ --report /tmp/report.json --pr "$pr" --author "$author" \
70+ && appended=1
71+ fi
72+ done < /tmp/merged.txt
73+ echo "sweep done (appended=$appended)"
6974
70- - name : append the ledger row
71- if : steps.scorecard.outputs.found == 'true'
72- env :
73- PR : ${{ steps.scorecard.outputs.pr }}
74- AUTHOR : ${{ steps.scorecard.outputs.author }}
75- run : |
76- python3 .github/scripts/update_leaderboard.py \
77- --report /tmp/report.json --pr "$PR" --author "$AUTHOR"
78-
79- - name : commit the row
80- if : steps.scorecard.outputs.found == 'true'
81- env :
82- PR : ${{ steps.scorecard.outputs.pr }}
75+ - name : commit the rows
8376 run : |
8477 if git diff --quiet -- competition/LEADERBOARD.md; then
85- echo "ledger unchanged (row already present) "
78+ echo "ledger unchanged"
8679 exit 0
8780 fi
8881 git config user.name "github-actions[bot]"
8982 git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
9083 git add competition/LEADERBOARD.md
91- git commit -m "docs(competition): ledger row for #${PR} "
84+ git commit -m "docs(competition): ledger rows from sweep "
9285 git push
0 commit comments