Dashboard › publish › Distillation
8c6222b8-7347-4536-aaba-33c2247fd4ef["lore_tm_v1_D2gLZcnUGXcKW3AltK_w-aSomXNx68bjJMhkGAv-kOM","lore_tm_v1_trSfgBtJmVKj3bhWjc5Nc6hwsCq5bHVpolAfP9nwq4E","lore_tm_v1_87EliFJDwMVp1dSnyYXTnTlfIUl1-5gyFJUtNWOxKq0","lore_tm_v1_Q6T03o4dTUh63kE7NxPdrDCqAmfxFbtvXnKIbcXfthI","lore_tm_v1_QmVno_UOqMgb7gLkETc7XG9PHjm_Fmhl3S0MqNqeSGs","lore_tm_v1_HVVVtbXLh-917ouyA5Trrpi5jThRzlsU0etwhc1wrB0","lore_tm_v1_tIb1ToXBSOnfLFE2RUuTpoGoI2hlIEHWW5so2droluM","lore_tm_v1_Cwyj-uH00bwDvmqaXj5z1PbtNdNVPMYy0H-FaDIz1pU","lore_tm_v1_HFG2J0eez6t2KKYKbfQYn-Smpg3-y3VhFTt35mjKY2g","lore_tm_v1_amBDSM-emsUPWnMh4AKVhvBFI-IVNyA85tjekfDU5tc"]
Date: Aug 27, 2026
src/modules/approval-authorizer.js and modification of .github/workflows/publish.yml for the release-approval authorization gate..github/workflows/publish.yml.accepted label events before CI polling, remove the unsafe accepted label, and fail closed when the GitHub permission lookup fails.src/modules/approval-authorizer.js: ALLOWED_ROLE_NAMES is new Set(["write", "maintain", "admin"]); TRUSTED_BOTS is new Set(["getsantry[bot]", "sentry-release-bot[bot]"]); PUBLISH_TITLE_REGEX is /^publish: (?<owner>[^/]+)\/(?<repository>[^/@]+)(?:\/[^@]+)?@[^@]+$/.authorizeApproval({ actor, issueTitle, getPermission }) in src/modules/approval-authorizer.js matches issueTitle against PUBLISH_TITLE_REGEX; returns { authorized: false, repository: null } if the title is invalid or its owner is not getsentry; builds repository as ${title.groups.owner}/${title.groups.repository}; trusts either configured bot without calling getPermission; otherwise calls getPermission({ owner: title.groups.owner, repository: title.groups.repository, username: actor }) and authorizes only role_name values write, maintain, or admin. It exports { authorizeApproval }.src/modules/__tests__/approval-authorizer.js imports Vitest describe, expect, test, and vi, requires authorizeApproval from ../approval-authorizer.js, and uses issue title publish: getsentry/sentry-javascript@10.0.0.getsantry[bot] is authorized for getsentry/sentry-javascript without a target-repository permission lookup; roles write, maintain, and admin are authorized after getPermission is called with {owner: "getsentry", repository: "sentry-javascript", username: "contractor"}; roles none, read, triage, Elevated Bot, and undefined are rejected; title publish: example/other@1.0.0 is rejected without querying GitHub; and a rejected getPermission promise with Error("Not Found") propagates the error..github/workflows/publish.yml waiting-for-ci job runs on ubuntu-latest, is named Waiting for CI, uses environment: production, and runs only when github.event.label.name == 'accepted', the issue is open, and its title starts with publish: .waiting-for-ci creates a release-bot GitHub App token in step Get release bot auth token (id: release-token) using actions/create-github-app-token@v3, vars.SENTRY_RELEASE_BOT_CLIENT_ID, secrets.SENTRY_RELEASE_BOT_PRIVATE_KEY, and owner: getsentry.waiting-for-ci step Authorize approval (id: authorization) uses GH_TOKEN: ${{ steps.release-token.outputs.token }}, ACTOR: ${{ github.actor }}, and ISSUE_TITLE: ${{ github.event.issue.title }}. Its shell script extracts only getsentry/... publish repositories using sed -n 's|^publish: \(getsentry/[^/@]*\).*@[^@]*$|\1|p'; writes authorized=false and exits successfully for an unparseable repository; writes authorized=true for getsantry[bot] or sentry-release-bot[bot]; otherwise runs gh api "repos/${repo}/collaborators/${ACTOR}/permission" --jq '.role_name' and authorizes only write|maintain|admin. No error suppression is configured for the GitHub API lookup, so lookup failure halts the step.waiting-for-ci creates a separate internal-app token in step Get auth token (id: token) using actions/create-github-app-token@v3, vars.SENTRY_INTERNAL_APP_ID, and secrets.SENTRY_INTERNAL_APP_PRIVATE_KEY; this token is now created before the rejection step that consumes it.Reject unauthorized approval runs when steps.authorization.outputs.authorized != 'true', uses GH_TOKEN: ${{ steps.token.outputs.token }}, removes the accepted label with gh issue edit "${{ github.event.issue.number }}" -R "$GITHUB_REPOSITORY" --remove-label "accepted", posts @${{ github.actor }} cannot approve this release. Approvers must have write, maintain, or admin access to the target repository., then exits with status 1.Mark ci-pending uses the internal-app token to idempotently remove ci-failed and ci-ready and add ci-pending; removing ci-ready is intended to ensure the CI poller’s later --add-label ci-ready produces the labeled event that starts publishing.Comment on issue uses ${{ github.token }} and posts either Retrying — CI was previously failed. Checking CI status now. when the issue had ci-failed, or Approved. Checking CI status on the release branch. Publishing will start automatically when CI passes. otherwise.Get poller app token step is best-effort (continue-on-error: true) and uses a dedicated app because sentry-internal-app lacks actions_variables:write.