Dashboard › publish › Distillation
1edf9d90-c350-4056-83a4-b67946815b8c["lore_tm_v1_bsX8YDRg3-qDhmT0D9pvfBApl7IkQYeNKt4m0qKpi34","lore_tm_v1_qhRPS16DTJuC6Jn5dF38jKxn7vEJ4R_efM3E58KBqPo","lore_tm_v1_ehHMSs0fNGDwHLlNhW4sWhnKf6cQ2qk4ITWuNM74iOY","lore_tm_v1_7g0vmOqD1w2uWWpsnFlj2I9X-VlU3d2kF_5Sh4QdiKM","lore_tm_v1_j9O3SMApCqxC73hCREqH7k_V5SX6sw8aHGnXSuToS00","lore_tm_v1_8FqtoUzI9t1oddU3m-ojo0U_AgXhSTMxbBUQUVZZ7Tc","lore_tm_v1_9GQJE_BxelB8z_LkLNJGNtoD9rmQD7RtRMSftwNknTA","lore_tm_v1_HWFfUznSwVtSe6fEuS473NExqs9rzSl3RcbqkGWIZP8","lore_tm_v1_L6Rw92U9sygfrsypDDnfsBogZdMB7F59BDjLZLeC-2s","lore_tm_v1_kD8PYGT-vCqRR5YPomVm-_pl0GSVeAgLcK1XWyUo4f0","lore_tm_v1_9RUVEwlGiipmmbvdCqt7g-BUvAq-d74po-pWsXg0vPc","lore_tm_v1_qSJc_bN93BidvKNjQS6-6ocu1hlFhMib-u-REgpGPyY"]
Date: Sep 8, 2026
src/modules/details-from-context.js defines PUBLISH_TITLE_REGEX as /^publish: (?:getsentry\/)?(?<repo>[^/@]+)(?<path>\/[\w./-]+)?@(?<version>[\w.+-]+)$/; parsePublishTitle(title) returns named match groups or null.details-from-context.js, TARGETS_SECTION_PARSER_REGEX is /^(?!### Targets$\s)(?: *- \[[ xX]\] \S+\s*$(?:\r?\n)?)+/m; TARGETS_PARSER_REGEX is /^\s*- \[[ x]\] (\S+)/gim; CHECKED_TARGETS_PARSER_REGEX is /^\s*- \[x\] (\S+)/gim.detailsFromContext({ context }) in src/modules/details-from-context.js throws Error("Issue context is not defined") unless context.payload.issue exists, and throws Invalid publish issue title: ${context.payload.issue.title} unless the issue title parses as a publish title.detailsFromContext() derives dry_run as "1" when an issue label is exactly "dry-run" (otherwise ""), path as "." + (titleMatch.path || ""), and merge_target from a body line matching /^Merge target: (?<merge_target>[\w.\-/]+)$/m (otherwise "").detailsFromContext() obtains targets only when TARGETS_SECTION_PARSER_REGEX matches the issue body; it extracts only lowercase checked (- [x]) target IDs via CHECKED_TARGETS_PARSER_REGEX, leaving targets undefined when no matching section exists. It returns title groups plus dry_run, merge_target, path, and targets.src/modules/post-workflow-details.js postWorkflowDetails({ context, octokit }) obtains publishRepo and run_id from context, gets issue_number from context.payload.issue.number, calls octokit.rest.actions.getWorkflowRun({ ...publishRepo, run_id }), then creates a publish-issue comment: Publishing: [run#${run_id}](${workflowInfo.html_url}).src/publish/post-workflow-details.js initializes github.context, an Octokit client from github.getOctokit(getGitHubToken()), and invokes postWorkflowDetails({ context, octokit }).src/libs/github.js getGitHubToken() returns process.env.GITHUB_TOKEN; if missing, it throws No "GITHUB_TOKEN" environment variable found. Please ensure the workflow is configured correctly.src/modules/process-end-state.js processEndState({ context, octokit, inputs, status }) derives repo and version from inputs; obtains the GitHub workflow run and issue number; assembles { repo, version, publishRepo, run_id, issue_number, workflowInfo, status }; posts an end-state issue comment; closes the issue only for status === "success"; then reports a Sentry session.githubIssueComment() in process-end-state.js supports: "failure" → Failed to publish. with step-8 workflow logs and a branch deletion link filtered by encodeURIComponent(version); "cancelled" → analogous Publish workflow cancelled. comment; "success" → Published successfully: [run#${run_id}](${workflowInfo.html_url}); any other status throws Unknown status: '${status}'.reportSession() in process-end-state.js initializes @sentry/node with dsn: process.env.SENTRY_DSN and release ${details.repo}@${details.version}, tags the scope with "repository", sets "release" context containing issue_number and inputs, captures a message, starts/ends a session, and awaits Sentry.close().sentryInfoFromDetails() maps "failure" to { message: "Release failed: ${repo}", severity: "error", status: "crashed" }, "cancelled" to { message: "Release cancelled: ${repo}", severity: "warn", status: "crashed" }, and "success" to { message: "Release succeeded: ${repo}", severity: "info", status: "ok" }; unknown statuses throw Unknown status: '${status}'.src/publish/post-result.js parses process.env.PUBLISH_ARGS as JSON, takes status from process.argv.slice(2)[0], creates Octokit using getGitHubToken(), and calls processEndState({ context, octokit, inputs, status }) without awaiting it.src/publish/__tests__/ci-poller-workflow.js verifies .github/workflows/ci-poller.yml checks out trusted default-branch publish code using a 40-character SHA-pinned actions/checkout in step "Get publish code" before "Get auth token"."Validate dispatch attempt" step before "Get auth token"; for workflow_dispatch it validates ATTEMPT against ^[0-9]+$, and defines ATTEMPT: ${{ github.event_name == 'workflow_dispatch' && steps.dispatch-attempt.outputs.value || '0' }}.src/publish/__tests__/workflow-action-pinning.js reads .github/workflows/auto-approve.yml, ci-poller.yml, publish.yml, and test.yml; it asserts every uses: actions/...@... reference is pinned to a 40-character lowercase hexadecimal revision (/^[a-f0-9]{40}$/).src/modules/__tests__/approval-attestation.js tests that currentAcceptedEvent() selects the latest "accepted" label event, e.g. actor "contractor" and event ID "200" over earlier ID "100", and that an approval attestation authored by trusted "github-actions[bot]" matches that current event.29503999078 becomes event ID "29503999078".hasCiReadyAttestation() requires the attestation to match both the current accepted event and the "ci-ready" label actor; changing accepted event ID "200" to "201" or using "contractor" instead of "sentry-internal-app[bot]" for the CI-ready event rejects it.<!-- publish-approval not-base64 -->), or when the issue dry-run state changes; requestDigest() includes the dry-run state.src/modules/__tests__/approval-authorizer.js confirms both "getsantry[bot]" and "sentry-release-bot[bot]" are directly rejected by authorizeApproval() with repository "getsentry/sentry-javascript" and no getPermission() lookup, even if autoApprovedRepositories includes that repository."contractor" is authorized for target-repository roles "write", "maintain", and "admin", with lookup { owner: "getsentry", repository: "sentry-javascript", username: "contractor" }; roles "none", "read", "triage", "Elevated Bot", and undefined reject authorization.publish: sentry-javascript/packages/core@10.0.0) resolves to repository "getsentry/sentry-javascript" and queries permissions against repository "sentry-javascript"; malformed titles such as publish: @1.0.0 or publish: getsentry/sentry-javascript return { authorized: false, repository: null } without a GitHub lookup; permission lookup errors propagate unchanged.src/publish/__tests__/authorize-approval.js verifies getAutoApprovedRepositories() treats getsentry/sentry-javascript and getsentry/objectstore/clients as distinct exact allowlist paths."write" permission lookup requests https://api.github.com/repos/getsentry/sentry-javascript/collaborators/contractor/permission with Authorization: "Bearer release-bot-token" and writes authorized=true plus a <!-- publish-approval ... --> attestation to /tmp/github-output.authorized=false\n) when the requester is also the approving "contractor"; direct approval by "sentry-release-bot[bot]" makes no GitHub fetches; and a failed permission lookup with HTTP 404 throws Could not retrieve contractor's permission for getsentry/sentry-javascript: GitHub returned 404 without output or console error."sentry-internal-app[bot]" can authorize an allowlisted automated request opened by "getsantry[bot]" only when a "github-actions[bot]" comment contains createAutoApprovalAttestation({ autoApprover: "getsantry[bot]", issue }); this path does not call a collaborator permission endpoint, and it rejects if the request proof/comment is absent.src/publish/__tests__/validate-approval-attestation.js confirms validateApprovalAttestation() accepts an open, accepted issue only when the current accepted event and a matching attestation comment by "github-actions[bot]" correspond."accepted" label is absent, or the issue is "closed"."100" after a new accepted event ID "200" becomes current, including when expectedAcceptedEvent explicitly requires the earlier event."accepted" and "ci-ready" labels, an approval attestation for accepted event { actor: "contractor", eventId: "100" }, a CI-ready attestation for "sentry-internal-app[bot]", and a matching CI-ready label event; a manually applied "ci-ready" label by "contractor" is rejected despite otherwise valid attestations.