Dashboard › publish › Distillation
cf50c21b-9a4a-4d8b-bb2f-917dd0931d59["lore_tm_v1_PNaPTWCYkHxMCSWRMVSkpFHZLMxVok5ZSy0NHX9yM-0","lore_tm_v1_vsQ-KVud52EyrvyIZEU1wZvb8dmFrfBmtZfDXq2vK0U","lore_tm_v1_b3LT07zqBN6rIEvOzMefS9k4wkTy9Aw-QBJHSPwnZBA","lore_tm_v1_8c8CEQhy_5QgZ1nSkCeu4RPSD_eEfdGwqCgibzH7NXk","lore_tm_v1_oIcyjrTJvBD8EoNs0Ln5CiMpG4oOS0-2PA1GJElF3zQ","lore_tm_v1_nuKFJfKcRwMKJSOn8BCy-5Ok19ifn_P_V8RN5Ts8f7g","lore_tm_v1_gQ0KEj0dRemmgx1aBj0i2hhz5HrVoWSkr8ipNIUxClc","lore_tm_v1_vL580C6I0eTuD6hDL63u5Oa2pmzwgs2xHz5-_KYLV6Y","lore_tm_v1_UC2Q_xUK98eWkLbiSsxpmSbg00IrqTlzko7Na40eo9k","lore_tm_v1_K93gRNvnYoq5DGf2ashH10yskIhrP42bBPjsxSc6J8U","lore_tm_v1_mQdJoIkobvZJk2JShXfExAj6I0qo1AbODoJiX6AFjZU","lore_tm_v1_YS6q4ckLn7xDog8f0AWDJuIQruKsifbLzZCXgDVWfzk","lore_tm_v1_PJVMZKONAkVwu_-VO7k07hfp7NGZe06Kj8WS2gWNPyc","lore_tm_v1_n3dLezatNsssaDJRTywF0S7d4sMQEBWQl87T0vW_-bs","lore_tm_v1_VGyCssm1ZJE2InfVIyjmXrtODWpygw_laQ5WX4lp-cs","lore_tm_v1_5HN1j4jW-QW4j_KayvFeKms1a_JJyFcoDRCqNIMDk0I","lore_tm_v1_UwTBilzyfeplXqWBts7LL1hsSKy5Y6pJVVcEPZC0hN4","lore_tm_v1_oZ22artp4JzI8gaxZoqzf0Oyn48cWovT7TI-LJVO0CY","lore_tm_v1_AQle-jI2hnNBkQLjNZBDkwUl1qKdsA-AZRpgLDU9Ymk","lore_tm_v1_WvE2IVN8xTxSqArIZRAQgCuoBaZ2DA4qoCDY6fnVN7I"]
Date: Sep 8, 2026
src/modules/__tests__/approval-authorizer.js verifies authorizeApproval() rejects direct approval by getsantry[bot] and sentry-release-bot[bot] without calling getPermission, even when getsentry/sentry-javascript is allowlisted; it also rejects getsantry[bot] when that target is absent from the allowlist.src/modules/__tests__/approval-authorizer.js verifies target-repository collaborators with role_name exactly write, maintain, or admin are authorized; for actor contractor and title publish: getsentry/sentry-javascript@10.0.0, permission lookup uses { owner: "getsentry", repository: "sentry-javascript", username: "contractor" }.src/modules/__tests__/approval-authorizer.js verifies unqualified title publish: sentry-javascript/packages/core@10.0.0 resolves to repository getsentry/sentry-javascript; roles none, read, triage, Elevated Bot, and undefined are rejected.src/modules/__tests__/approval-authorizer.js verifies malformed titles publish: @1.0.0 and publish: getsentry/sentry-javascript return { authorized: false, repository: null } without GitHub permission lookup; a getPermission rejection such as Error("Not Found") propagates.src/publish/__tests__/ contains workflow-action-pinning.js, record-auto-approval-attestation.js, publish-workflow.js, auto-approval-workflow.js, ci-poller-workflow.js, current-accepted-event.js, record-ci-ready-attestation.js, validate-approval-attestation.js, and authorize-approval.js.src/publish/__tests__/workflow-action-pinning.js reads every .github/workflows/*.yml and requires every uses: actions/...@... reference to be a 40-character lowercase hexadecimal SHA. Secret-bearing jobs required to use environment: production are auto-approve in .github/workflows/auto-approve.yml, check-ci in .github/workflows/ci-poller.yml, keepalive in .github/workflows/cocoapods-keepalive.yml, and waiting-for-ci plus publish in .github/workflows/publish.yml.src/publish/__tests__/workflow-action-pinning.js forbids workflow_dispatch: in all workflows except .github/workflows/ci-poller-dispatch.yml; that protected relay must contain workflow_dispatch:, permissions: {}, and environment: production, and must contain neither secrets. nor other workflows’ manual-dispatch capability.src/publish/__tests__/publish-workflow.js requires the “Revalidate approval attestation” fence in .github/workflows/publish.yml to occur after “Set targets” and before “Publish using Craft”, with REQUIRE_CI_READY_ATTESTATION: "true"; it also requires Craft’s Docker image reference to have an immutable 64-character SHA-256 digest.src/publish/__tests__/ci-poller-workflow.js requires .github/workflows/ci-poller.yml to check out trusted publish code from ${{ github.event.repository.default_branch }} via SHA-pinned actions/checkout before “Get auth token”.src/publish/__tests__/ci-poller-workflow.js requires CI poller triggering through repository_dispatch type [ci-poller] and a successful workflow_run for Run CI Status Poller on the default branch; it forbids direct workflow_dispatch: in ci-poller.yml. .github/workflows/ci-poller-dispatch.yml is the manual recovery relay and must have no repository_dispatch: or secrets..src/publish/__tests__/ci-poller-workflow.js requires repository-dispatch attempt validation before app-token acquisition: ATTEMPT defaults to '0' unless github.event_name == 'repository_dispatch', must match ^(0|[1-5][0-9])$, is incremented with attempt=$((10#$ATTEMPT + 1)), and retries dispatch through gh api --method POST "repos/$GITHUB_REPOSITORY/dispatches" --input -.src/publish/__tests__/auto-approval-workflow.js requires node src/publish/record-auto-approval-attestation.js to run before --add-label accepted; “Post automated approval attestation” must use GH_TOKEN: ${{ github.token }} before labeling, while subsequent internal-app labeling uses GH_TOKEN: ${{ steps.token.outputs.token }}.src/publish/__tests__/authorize-approval.js verifies getAutoApprovedRepositories() reads exact allowed repository paths—including nested paths—from auto-approve-repos.txt; sample lines getsentry/sentry-javascript and getsentry/objectstore/clients produce the same two Set entries.src/publish/__tests__/authorize-approval.js verifies successful human authorization writes authorized=true and an HTML <!-- publish-approval ... --> attestation to GITHUB_OUTPUT; with APPROVAL_TOKEN=release-bot-token, lookup is https://api.github.com/repos/getsentry/sentry-javascript/collaborators/contractor/permission using Authorization: Bearer release-bot-token.src/publish/__tests__/authorize-approval.js verifies authorize-approval.js writes authorized=false when requester and approving actor are both contractor; direct approval by sentry-release-bot[bot] makes no fetch calls and writes authorized=false.src/publish/__tests__/authorize-approval.js verifies sentry-internal-app[bot] authorizes an allowlisted getsantry[bot] request for publish: getsentry/relay@1.2.3 only when a matching github-actions[bot] auto-approval attestation exists; this path does not query /collaborators/. Missing request proof writes authorized=false.src/publish/__tests__/authorize-approval.js verifies a failed permission endpoint response (ok: false, status: 404) causes main() to throw Could not retrieve contractor's permission for getsentry/sentry-javascript: GitHub returned 404, without writing output or logging to console.error.src/publish/__tests__/validate-approval-attestation.js verifies validateApprovalAttestation() accepts only a trusted github-actions[bot] approval attestation tied to the current open issue, current title, current accepted label/event, and request body; it rejects after title/body changes, issue closure, accepted-label removal, or accepted re-addition with a newer event ID ("200" superseding "100").src/publish/__tests__/validate-approval-attestation.js verifies expectedAcceptedEvent: { actor: "contractor", eventId: "100" } rejects a current later approval event "200", even where an attestation exists for "200".src/publish/__tests__/validate-approval-attestation.js verifies requireCiReadyAttestation: true accepts only a CI-ready attestation bound to accepted event { actor: "contractor", eventId: "100" } and CI-ready label actor sentry-internal-app[bot]; a ci-ready label added by contractor is rejected despite an otherwise valid attestation.src/publish/__tests__/record-auto-approval-attestation.js verifies recordAutoApprovalAttestation() creates an auto-approval proof binding autoApprover: "getsantry[bot]", request digest, and title to a live open request from that same bot; it throws The automated approval request changed before approval when live issue author is contractor.src/publish/__tests__/record-ci-ready-attestation.js verifies recordCiReadyAttestation() binds acceptedActor: "contractor", acceptedEventId: "100", authenticated ciReadyActor: "sentry-internal-app[bot]", request digest, and title. It throws The approval changed before CI could be marked ready if the title differs or the live accepted event changed from the poller-observed "100" to "200".src/publish/__tests__/current-accepted-event.js verifies getCurrentAcceptedEvent() returns the latest accepted-label event as { actor: "contractor", eventId: "200" } over prior event "100" and throws The issue has no current accepted label event if no valid event exists.src/modules/details-from-context.js defines publish-title regex ^publish: (?:getsentry\/)?(?<repo>[^/@]+)(?<path>\/[\w./-]+)?@(?<version>[\w.+-]+)$; parsePublishTitle() returns named groups or null. It supports optional getsentry/ prefix and optional package path, while detailsFromContext() throws Issue context is not defined for missing issue context and Invalid publish issue title: ${title} for malformed titles.detailsFromContext() returns parsed repo, path prefixed with . (default "."), version, dry_run as "1" only when the issue has label dry-run (otherwise ""), merge_target from Merge target: [\w.\-/]+ (otherwise ""), and optionally checked target IDs from the contiguous issue-body target-checkbox section.src/publish/inputs.js invokes detailsFromContext({ context: github.context }) and writes its full result as Actions output result through @actions/core.setOutput.src/modules/update-issue.js updateTargets() reads Craft state from ${GITHUB_WORKSPACE}/__repo__/.craft-publish-${version}.json; if absent it returns without modifying targets. If present, it fetches the publish issue and state concurrently, transforms issue targets, and updates the issue body.src/modules/update-issue.js transformIssueBody() preserves declared target ordering, marks each declared target - [x] when craftState.published[targetId] is truthy (otherwise - [ ]), and appends state-published targets not declared in the issue body. updateIssue() concurrently performs this target update only when inputs?.version exists and removes the accepted label.src/publish/update-issue.js parses optional JSON PUBLISH_ARGS; malformed JSON logs Could not parse publish inputs; skipping target update and still calls updateIssue({ context, octokit, inputs }). It uses github.getOctokit(getGitHubToken()); CLI execution catches errors, logs them, and sets process.exitCode = 1.src/libs/github.js getGitHubToken() returns process.env.GITHUB_TOKEN or throws No "GITHUB_TOKEN" environment variable found. Please ensure the workflow is configured correctly.src/modules/process-end-state.js retrieves the workflow run, posts a status-specific issue comment, closes the publish issue only for status === "success", then reports a Sentry session. Success comment is Published successfully: [run#${run_id}](${workflowInfo.html_url}); failure/cancellation comments link run logs at ?check_suite_focus=true#step:8 and provide a branch-deletion link for https://github.com/getsentry/${repo}/branches/all?query=${encodeURIComponent(version)}.processEndState() initializes @sentry/node with SENTRY_DSN and release ${repo}@${version}, tags repository, and records issue number plus inputs in release context. It reports failure as Release failed: ${repo}, severity error, session status crashed; cancellation as Release cancelled: ${repo}, severity warn, status crashed; and success as Release succeeded: ${repo}, severity info, status ok; unknown statuses throw Unknown status: '${status}'.src/modules/post-workflow-details.js fetches the current Actions workflow run and posts Publishing: [run#${run_id}](${workflowInfo.html_url}) to the publish issue.src/publish/post-result.js immediately parses PUBLISH_ARGS, takes status from process.argv.slice(2)[0], obtains an Octokit client with getGitHubToken(), and calls processEndState({ context, octokit, inputs, status }).src/publish/post-workflow-details.js immediately obtains GitHub context and an Octokit client using getGitHubToken(), then calls postWorkflowDetails({ context, octokit }).