Dashboard › publish › Distillation
25ac1527-7981-42f6-9f93-9c0bef83b492["lore_tm_v1_INuofT9sYfMB9MZ_hZBlWKmHAC6--vTI1-33nLe5vjc","lore_tm_v1_eJP-3CAFgke-kVcUOAnGBIkMSrS4CtUMs5i4DaG9tAw","lore_tm_v1_s2MCFxeZRpGNMWo7ZsQzMiG7oJJO2wMSpPOoqpzzoyI","lore_tm_v1_B4gfFXXcmUPJXZVmqvYXboryHULq2Kk4f46u6PSMa0g","lore_tm_v1_6CDdvyfplKQXht_7cAQDvtAkJdavOsiAgPxj7szp3ek","lore_tm_v1_a_FqUBSxEqSKPmE0gbyZWC7NuzQdzb4e_Frfb2f9z4E","lore_tm_v1_zO3GCoebffqUzSYuTXeQdCs_Jw3BCuT_3ErOUOQj03g","lore_tm_v1_kqQ_WsjDKPKNqaHPV52fZ-XYmgQX9mSMcAAo1AHW_Os"]
Date: Aug 28, 2026
src/publish/validate-approval-attestation.js defines GitHub REST helpers authenticated with APPROVAL_TOKEN, Accept: application/vnd.github+json, and X-GitHub-Api-Version: 2026-03-10; getAllPages() paginates issue events or comments at per_page=100 until a page has fewer than 100 records.validateApprovalAttestation({ attestationAuthor, issueNumber, issueTitle, repository }) concurrently retrieves the live issue, all issue events, and all comments; it returns true only if the live title equals issueTitle, an accepted label exists, currentAcceptedEvent(events) is non-null, and hasApprovalAttestation() matches the specified attestation author, latest accepted event, and live title. Its main() requires APPROVAL_TOKEN, APPROVAL_ISSUE_NUMBER, APPROVAL_ISSUE_REPOSITORY, APPROVAL_ISSUE_TITLE, and APPROVAL_ATTESTATION_AUTHOR, otherwise throws; invalid approval throws "The current accepted label has no matching approval attestation".src/publish/authorize-approval.js reads nonblank entries from auto-approve-repos.txt into a Set; fetches target-repository collaborator permissions from repos/{owner}/{repository}/collaborators/{username}/permission; and requires GITHUB_OUTPUT, APPROVAL_TOKEN, APPROVAL_ISSUE_NUMBER, and APPROVAL_ISSUE_REPOSITORY.authorize-approval.js refetches the live issue title and all issue events, requires the title to equal APPROVAL_ISSUE_TITLE and the current accepted event actor to equal APPROVAL_ACTOR, then writes authorized=true and approval_attestation=${attestation} to GITHUB_OUTPUT; otherwise it writes authorized=false.accepted-label event and issue title..github/workflows/ci-poller.yml now checks out Publish code via actions/checkout@v6 with persist-credentials: false; before parsing/starting CI, it validates the approval attestation using src/publish/validate-approval-attestation.js with APPROVAL_TOKEN="$GH_TOKEN", APPROVAL_ATTESTATION_AUTHOR="github-actions[bot]", issue number, repository, and title.ci-pending and accepted, comments Approval is invalid or could not be verified. Re-add the accepted label to retry after resolving the issue., and continues without processing that issue.parsePublishTitle from ./src/modules/details-from-context.js; it derives repo as "getsentry/" + .repo and version as .version, and skips issues whose parsed repository or version is empty.ci-ready after all CI checks pass, ci-poller revalidates the attestation with the original title; this is intended to prevent renamed or re-approved issues from reaching ci-ready..github/workflows/publish.yml now checks out Publish code using actions/checkout@v6 with persist-credentials: false, creates a release-bot token using actions/create-github-app-token@v3 (SENTRY_RELEASE_BOT_CLIENT_ID, SENTRY_RELEASE_BOT_PRIVATE_KEY, owner: getsentry) with continue-on-error: true, and runs node src/publish/authorize-approval.js with continue-on-error: true.authorized == 'true', publish.yml posts approval_attestation as an issue comment using GH_TOKEN: ${{ github.token }}; it then validates the comment using src/publish/validate-approval-attestation.js, authenticated as github-actions[bot].Reject invalid approval runs under always() when authorization, attestation creation, or attestation validation fails; it removes accepted, posts the retry comment, and exits 1. This cleanup occurs before resetting labels to ci-pending.ci-ready publishing workflow, Publish checkout at .__publish__ uses persist-credentials: false; it validates approval through node .__publish__/src/publish/validate-approval-attestation.js, and on failure removes accepted, posts the retry comment, and exits 1 before Node setup and Craft publishing.src/modules/details-from-context.js now exports shared PUBLISH_TITLE_REGEX and parsePublishTitle(title). The regex is ^publish: (?:getsentry\/)?(?<repo>[^/@]+)(?<path>\/[\w./-]+)?@(?<version>[\w.+-]+)$; parsePublishTitle() returns named groups or null. detailsFromContext() now throws Invalid publish issue title: ${context.payload.issue.title} instead of dereferencing an unmatched regex.src/modules/approval-authorizer.js allows only write, maintain, or admin through ALLOWED_ROLE_NAMES; it uses shared parsePublishTitle(), maps target repositories to getsentry/${title.repo}, and defines release paths as ${repository}${title.path || ""}. getsantry[bot] is authorized only when its exact release path is allowlisted; otherwise it is denied without permission lookup. Other actors are checked through getPermission({ owner: "getsentry", repository: title.repo, username: actor }).src/libs/github.js, src/modules/update-issue.js, and src/publish/post-result.js; behavior remains that getGitHubToken() requires GITHUB_TOKEN, updateIssue() concurrently updates targets and removes accepted, and post-result.js invokes processEndState({ context, octokit, inputs, status }).src/modules/__tests__/approval-attestation.js tests: (1) matching an attestation for the latest of two accepted events (id: "200") from contractor on publish: getsentry/relay/py@1.2.3; (2) normalizing numeric GitHub event ID 29503999078 to string "29503999078"; and (3) rejecting attestations from an untrusted comment author, mismatched event/title values, and malformed <!-- publish-approval not-base64 -->.src/publish/inputs.js obtains detailsFromContext({ context: github.context }) and exposes the result through core.setOutput("result", result).ci-ready, and the publishing job checks label state plus approval validation but not the ci-ready event actor/provenance. Proposed remedy: bind a CI-ready attestation to the exact accepted-label event and require it before publishing. Affected paths cited: .github/workflows/publish.yml:169-185,196-218 and .github/workflows/ci-poller.yml:234-254.ci-ready addition can permit re-approval after validation but before label reset; a new accepted event could receive an attestation and then be published without CI proven for that event. Proposed remedy: bind CI-ready attestation to the exact accepted event and require it at publish time.src/publish/validate-approval-attestation.js:59-76.write, maintain, or admin, while automatic approval is limited to explicit repository/path entries; passed attestation binding because it ties latest accepted actor/event ID, title, and a github-actions[bot] comment; and passed fail-closed validation prior to credentialed Craft execution.yarn lint still fails on unrelated .github/workflows/cocoapods-keepalive.yml:1 yml/plain-scalar, while yarn test passes with 9 files and 41 tests. Assistant conclusion: DO-NOT-MERGE.