|
| 1 | +name: Merged changelog proof |
| 2 | + |
| 3 | +on: |
| 4 | + push: |
| 5 | + branches: [pylon] |
| 6 | + |
| 7 | +permissions: {} |
| 8 | + |
| 9 | +jobs: |
| 10 | + merged-changelog-proof: |
| 11 | + name: Check changelog fragment |
| 12 | + runs-on: ubuntu-24.04 |
| 13 | + permissions: |
| 14 | + actions: read |
| 15 | + checks: read |
| 16 | + contents: read |
| 17 | + pull-requests: read |
| 18 | + steps: |
| 19 | + - name: Prove the merged pull request head check |
| 20 | + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 |
| 21 | + with: |
| 22 | + script: | |
| 23 | + const owner = context.repo.owner; |
| 24 | + const repo = context.repo.repo; |
| 25 | + const repository = `${owner}/${repo}`; |
| 26 | + const mergeSha = context.sha; |
| 27 | + if ( |
| 28 | + repository !== "pylon-code/prime-agent" || context.eventName !== "push" || |
| 29 | + context.ref !== "refs/heads/pylon" || !/^[0-9a-f]{40}$/.test(mergeSha) |
| 30 | + ) throw new Error("Merged changelog proof requires the canonical exact pylon push."); |
| 31 | + const associated = await github.paginate(github.rest.repos.listPullRequestsAssociatedWithCommit, { |
| 32 | + owner, repo, commit_sha: mergeSha, per_page: 100, |
| 33 | + }); |
| 34 | + const pullRequests = []; |
| 35 | + for (const candidate of associated) { |
| 36 | + const pull = (await github.rest.pulls.get({ owner, repo, pull_number: candidate.number })).data; |
| 37 | + if ( |
| 38 | + pull.merged_at && pull.merge_commit_sha === mergeSha && pull.base.ref === "pylon" && |
| 39 | + pull.base.repo.full_name === repository |
| 40 | + ) pullRequests.push(pull); |
| 41 | + } |
| 42 | + if (pullRequests.length !== 1) throw new Error("Merge SHA does not resolve to exactly one merged pylon pull request."); |
| 43 | + const pull = pullRequests[0]; |
| 44 | + if ( |
| 45 | + pull.head.repo?.full_name !== repository || !/^[0-9a-f]{40}$/.test(pull.head.sha) || |
| 46 | + pull.head.sha === mergeSha |
| 47 | + ) throw new Error("Merged pull request head is not an exact canonical pre-merge SHA."); |
| 48 | + const checks = await github.paginate(github.rest.checks.listForRef, { |
| 49 | + owner, repo, ref: pull.head.sha, filter: "latest", per_page: 100, |
| 50 | + }); |
| 51 | + const candidates = checks.filter((check) => |
| 52 | + check.name === "Check changelog fragment" && check.head_sha === pull.head.sha && |
| 53 | + check.app?.id === 15368 && check.status === "completed" && check.conclusion === "success" |
| 54 | + ); |
| 55 | + let proved = false; |
| 56 | + for (const check of candidates) { |
| 57 | + const suite = (await github.rest.checks.getSuite({ owner, repo, check_suite_id: check.check_suite.id })).data; |
| 58 | + const runId = /^https:\/\/github\.com\/pylon-code\/prime-agent\/actions\/runs\/([0-9]+)(?:\/job\/[0-9]+)?$/.exec(check.details_url ?? "")?.[1]; |
| 59 | + if (!runId) continue; |
| 60 | + const run = (await github.rest.actions.getWorkflowRun({ owner, repo, run_id: Number(runId) })).data; |
| 61 | + const workflow = (await github.rest.actions.getWorkflow({ owner, repo, workflow_id: run.workflow_id })).data; |
| 62 | + if ( |
| 63 | + suite.app?.id === 15368 && suite.head_sha === pull.head.sha && suite.status === "completed" && |
| 64 | + suite.conclusion === "success" && run.check_suite_id === suite.id && run.event === "pull_request" && |
| 65 | + run.status === "completed" && run.conclusion === "success" && run.head_sha === pull.head.sha && |
| 66 | + run.head_branch === pull.head.ref && run.head_repository?.id === 1349002285 && |
| 67 | + run.head_repository?.full_name === repository && run.repository?.id === 1349002285 && |
| 68 | + run.repository?.full_name === repository && workflow.path === ".github/workflows/changelog-fragment.yml" && |
| 69 | + run.pull_requests?.some((candidate) => candidate.number === pull.number) |
| 70 | + ) { |
| 71 | + proved = true; |
| 72 | + break; |
| 73 | + } |
| 74 | + } |
| 75 | + if (!proved) throw new Error("No successful GitHub Actions PR-head changelog check has the exact required provenance."); |
0 commit comments