Dashboard › publish › Distillation
9c1e4c1a-deac-4171-98c1-f78af4ae8f78["lore_tm_v1_u3Wj9Mxk8wqQX5CfSsN8e6KYR7aACWxfHtc1J_vwW7U","lore_tm_v1_ZiUwKCZUUtTuUzPAIjUb7IM5kBw4PnG8837p7DbX1lY","lore_tm_v1_kyCwU5CMOXbT17zphK0DmwwAUj9tAjJwj-ae6ZCHNZE","lore_tm_v1_8bg7OOLZnZEWdnKHyeOxoJrWCro9r92PwCzZsF8qsgU","lore_tm_v1_nkDWxnmgq5TweIMdtR3GuWSsqd1dB1VqNfkYnyoIaZQ","lore_tm_v1_qs-zkcgG6CtsPGJluDnyPMtdkXxvQ-iYjL11HCQL9cw","lore_tm_v1_zcQfUEbK5RbdJjfVwsOGbyLNY6QJnREIIU71005I7hs","lore_tm_v1_gjcqBF2XHc-kZouSJ62NdtaLkLBhxL5ZyezckpKVy-g","lore_tm_v1_690RSJ0LEd13gEOt8MVjG3HFAlI2gGvqAFkI0fjeir0","lore_tm_v1_JPgHZB3CBH5LkuXAP0USbdO1bkd_6DJomsHR7XWMi_Y","lore_tm_v1_YOD-th7bkWrZeo-JUVr-E2YIru8E1HntnBZLww_iwdI","lore_tm_v1_kUnEEXdJoMnKnMBQKXSbzWbQwGkYDc-UCd1fQa1L4fE","lore_tm_v1_-CLsMM7y2XH8Ht_69hqA6ZZZqKdCsyBVbOrcqR1kEWw","lore_tm_v1_uNptfm8pih-Lpe1aBdtHZ3hb7RR9rqail2LOV9_Ds6A"]
π΄ (01:03) User-provided /home/byk/Code/getsentry/publish/src/publish/validate-approval-attestation.js is 153 lines and imports currentAcceptedEvent, currentCiReadyEvent, hasApprovalAttestation, and hasCiReadyAttestation from ../modules/approval-attestation.js.
π΄ (01:03) User-provided src/publish/validate-approval-attestation.js function getGitHubResponse(path) fetches https://api.github.com/${path} with headers Accept: application/vnd.github+json, Authorization: Bearer ${process.env.APPROVAL_TOKEN}, and X-GitHub-Api-Version: 2026-03-10.
π΄ (01:03) User-provided src/publish/validate-approval-attestation.js function getIssue({ repository, issueNumber }) requests repos/${repository}/issues/${issueNumber} and throws Could not retrieve issue #${issueNumber}: GitHub returned ${response.status} for a non-OK response.
π΄ (01:03) User-provided src/publish/validate-approval-attestation.js function getAllPages({ repository, issueNumber, resource }) requests repos/${repository}/issues/${issueNumber}/${resource}?per_page=100&page=${page}, beginning at page 1 and incrementing by 1; it accumulates records until a page has fewer than 100 entries and throws Could not retrieve ${resource} for issue #${issueNumber}: GitHub returned ${response.status} on a non-OK response.
π΄ (01:03) User-provided validateApprovalAttestation({ attestationAuthor, expectedAcceptedEvent, issueNumber, issueTitle, repository, requireCiReadyAttestation = false }) concurrently retrieves the live issue, all issue events, and all issue comments, then derives event via currentAcceptedEvent(events) and ciReadyEvent via currentCiReadyEvent(events).
π΄ (01:03) User-provided validateApprovalAttestation(...) considers the approval valid only if the issue is open, its title exactly equals issueTitle, it currently has an accepted label, event !== null, any supplied expectedAcceptedEvent exactly matches both event.actor and event.eventId, and hasApprovalAttestation({ attestationAuthor, comments, event, issue }) succeeds.
π΄ (01:03) User-provided validateApprovalAttestation(...) returns the basic approval result immediately when it is false or requireCiReadyAttestation is false; when CI-ready proof is required, it additionally requires ciReadyEvent !== null, a current ci-ready label, and hasCiReadyAttestation({ acceptedEvent: event, attestationAuthor, comments, ciReadyEvent, issue }).
π΄ (01:03) User-provided src/publish/validate-approval-attestation.js function main() requires APPROVAL_TOKEN, APPROVAL_ISSUE_NUMBER, APPROVAL_ISSUE_REPOSITORY, APPROVAL_ISSUE_TITLE, and APPROVAL_ATTESTATION_AUTHOR, throwing No "${name}" environment variable found for a missing value.
π΄ (01:03) User-provided src/publish/validate-approval-attestation.js supplies expectedAcceptedEvent only when both EXPECTED_ACCEPTED_ACTOR and EXPECTED_ACCEPTED_EVENT_ID are truthy, copying them verbatim to actor and eventId; it sets requireCiReadyAttestation only when REQUIRE_CI_READY_ATTESTATION === "true".
π΄ (01:03) User-provided src/publish/validate-approval-attestation.js throws The current accepted label has no matching approval attestation when validation returns false, invokes main() only when run directly with console.error and process.exitCode = 1, and exports getAllPages, getGitHubResponse, getIssue, main, and validateApprovalAttestation.
π΄ (01:03) User-provided workflow search found 18 pinned uses: matches: .github/workflows/publish.yml uses actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 at lines 36, 192, and 274; actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 at lines 42, 52, 149, and 268; actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 at line 223; and docker://getsentry/craft@sha256:9a4a5d5efa44a00c2215078ead39800d4aaa5a97908b94f45a64d7d506d6e14b at line 353.
π΄ (01:03) User-provided workflow search found .github/workflows/ci-poller.yml uses actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 at line 49 and actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 at lines 60, 71, and 420.
π΄ (01:03) User-provided workflow search found .github/workflows/auto-approve.yml uses actions/checkout@11d5960a326750d5838078e36cf38b85af677262 at line 18 and actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 at line 23.
π΄ (01:03) User-provided workflow search found .github/workflows/test.yml uses actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 at line 19, actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 at line 20, and actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 at line 23.
π΄ (01:03) User-provided /home/byk/Code/getsentry/publish/src/modules/__tests__/approval-attestation.js is 174 lines and imports createApprovalAttestation, createCiReadyAttestation, currentAcceptedEvent, hasApprovalAttestation, hasCiReadyAttestation, parseApprovalAttestation, and requestDigest from ../approval-attestation.js.
π΄ (01:03) User-provided src/modules/__tests__/approval-attestation.js helper issue(title, { body = "", dryRun = false } = {}) returns { body, labels, title }, with labels equal to [{ name: "dry-run" }] only when dryRun is true.
π΄ (01:03) User-provided approval-attestation tests verify: 1. the latest of accepted events "100" and "200" becomes { actor: "contractor", eventId: "200" } and matches a github-actions[bot] approval attestation; 2. numeric GitHub event ID 29503999078 is normalized to string "29503999078"; 3. a CI-ready attestation for accepted event { actor: "contractor", eventId: "200" } and ciReadyActor: "sentry-internal-app[bot]" matches a ci-ready event by that app but fails for accepted event ID "201" or a ci-ready event by "contractor"; 4. malformed or stale attestations are rejected, including an untrusted comment author, changed event ID/title, and <!-- publish-approval not-base64 -->; 5. an approval attestation becomes invalid when the issueβs dry-run state changes.
π΄ (01:04) User-provided /home/byk/Code/getsentry/publish/src/modules/__tests__/approval-authorizer.js is 147 lines, imports authorizeApproval from ../approval-authorizer.js, and uses issueTitle = "publish: getsentry/sentry-javascript@10.0.0".
π΄ (01:04) User-provided authorizeApproval tests verify direct approvals by "getsantry[bot]" and "sentry-release-bot[bot]" are rejected without calling getPermission, returning { authorized: false, repository: "getsentry/sentry-javascript" }.
π΄ (01:04) User-provided authorizeApproval tests verify "getsantry[bot]" is rejected without a permission lookup when the target getsentry/sentry-javascript is outside an allowlist containing only getsentry/sentry-python.
π΄ (01:04) User-provided authorizeApproval tests verify collaborator roles "write", "maintain", and "admin" authorize contractor for getsentry/sentry-javascript, with getPermission({ owner: "getsentry", repository: "sentry-javascript", username: "contractor" }).
π΄ (01:04) User-provided authorizeApproval tests verify unqualified title publish: sentry-javascript/packages/core@10.0.0 resolves to repository getsentry/sentry-javascript and uses the same getPermission arguments.
π΄ (01:04) User-provided authorizeApproval tests verify roles "none", "read", "triage", "Elevated Bot", and undefined are rejected for getsentry/sentry-javascript.
π΄ (01:04) User-provided authorizeApproval tests verify malformed titles publish: @1.0.0 and publish: getsentry/sentry-javascript return { authorized: false, repository: null } without querying GitHub, and a getPermission rejection with Error("Not Found") propagates.
π΄ (01:04) User-provided /home/byk/Code/getsentry/publish/src/publish/__tests__/authorize-approval.js is 237 lines; its afterEach restores the original environment, restores mocks, and resets modules.
π΄ (01:04) User-provided runAuthorization({ actor, issueTitle, responses }) sets GITHUB_OUTPUT=/tmp/github-output, APPROVAL_TOKEN=release-bot-token, APPROVAL_ACTOR, APPROVAL_ISSUE_NUMBER=123, APPROVAL_ISSUE_REPOSITORY=getsentry/publish, and APPROVAL_ISSUE_TITLE; it mocks fs.appendFileSync and sequential global.fetch responses, runs main(), and waits for output.
π΄ (01:04) User-provided authorize-approval entry-point tests verify getAutoApprovedRepositories() converts getsentry/sentry-javascript\ngetsentry/objectstore/clients\n into an exact Set containing getsentry/sentry-javascript and getsentry/objectstore/clients.
π΄ (01:04) User-provided authorize-approval entry-point tests verify a successful "write" permission request for contractor calls https://api.github.com/repos/getsentry/sentry-javascript/collaborators/contractor/permission with Authorization: Bearer release-bot-token and writes text matching ^authorized=true\napproval_attestation=<!-- publish-approval .+ -->\n$ to /tmp/github-output.
π΄ (01:04) User-provided authorize-approval entry-point tests verify case-matching requester/approver "contractor" is self-approval and writes authorized=false\n.
π΄ (01:04) User-provided authorize-approval entry-point tests verify an allowlisted automated request opened by "getsantry[bot]", accepted by "sentry-internal-app[bot]", and supported by a github-actions[bot] comment containing createAutoApprovalAttestation(...) is authorized without any /collaborators/ request and emits an approval attestation.
π΄ (01:04) User-provided authorize-approval entry-point tests verify the same automated approval is rejected with authorized=false\n when the comments response contains no request proof.
π΄ (01:04) User-provided authorize-approval entry-point tests verify direct approval by automated opener "sentry-release-bot[bot]" is rejected as authorized=false\n without any fetch.
π΄ (01:04) User-provided authorize-approval entry-point tests verify a GitHub permission HTTP 404 rejects main() with Could not retrieve contractor's permission for getsentry/sentry-javascript: GitHub returned 404, while neither logging through console.error nor writing GITHUB_OUTPUT.
π΄ (01:04) User-provided /home/byk/Code/getsentry/publish/src/publish/__tests__/auto-approval-workflow.js is 36 lines and reads .github/workflows/auto-approve.yml as UTF-8.
π΄ (01:04) User-provided auto-approval workflow tests verify: 1. checkout before Record automated approval attestation does not use sparse-checkout:; 2. node src/publish/record-auto-approval-attestation.js occurs before --add-label accepted; 3. Post automated approval attestation uses GH_TOKEN: ${{ github.token }} before labeling, while later labeling uses GH_TOKEN: ${{ steps.token.outputs.token }}.
π΄ (01:04) User-provided /home/byk/Code/getsentry/publish/src/publish/__tests__/ci-poller-workflow.js is 66 lines and reads both .github/workflows/ci-poller.yml and .github/workflows/ci-poller-dispatch.yml.
π΄ (01:04) User-provided CI-poller workflow tests verify Get publish code uses actions/checkout@ with an exact 40-character lowercase hexadecimal revision and ref: ${{ github.event.repository.default_branch }}, and occurs before Get auth token.
π΄ (01:04) User-provided CI-poller workflow tests verify the protected manual-recovery relay design: .github/workflows/ci-poller.yml contains repository_dispatch:, types: [ci-poller], workflow_run:, workflows: [Run CI Status Poller], guards for github.event.workflow_run.conclusion == 'success' and github.event.workflow_run.head_branch == github.event.repository.default_branch, and no workflow_dispatch:; .github/workflows/ci-poller-dispatch.yml contains workflow_dispatch:, permissions: {}, and environment: production, but no secrets. or repository_dispatch:.
π΄ (01:04) User-provided CI-poller workflow tests extract the shell regex used by Validate dispatch attempt, require validation before Get auth token, accept decimal attempts 0 through 59, and reject "", "00", "01", "60", "-1", "1x", and "$(id)".
π΄ (01:04) User-provided CI-poller workflow tests require exact workflow snippets ATTEMPT: ${{ github.event_name == 'repository_dispatch' && steps.dispatch-attempt.outputs.value || '0' }}, attempt=$((10#$ATTEMPT + 1)), and gh api --method POST "repos/$GITHUB_REPOSITORY/dispatches" --input -.
π΄ (01:04) User-provided /home/byk/Code/getsentry/publish/src/publish/__tests__/current-accepted-event.js is 38 lines and tests getCurrentAcceptedEvent.
π΄ (01:04) User-provided current-accepted-event tests verify accepted-label events "100" then "200" return { actor: "contractor", eventId: "200" }, while an empty event list rejects with The issue has no current accepted label event; both cases use issue number "123" and repository "getsentry/publish".
π΄ (01:04) User-provided /home/byk/Code/getsentry/publish/src/publish/__tests__/publish-workflow.js is 22 lines and reads .github/workflows/publish.yml.
π΄ (01:04) User-provided publish-workflow tests verify Revalidate approval attestation occurs after Set targets and before Publish using Craft, with REQUIRE_CI_READY_ATTESTATION: "true" present from that fence onward.
π΄ (01:04) User-provided publish-workflow tests verify Craft is pinned using uses: docker://getsentry/craft@sha256: followed by exactly 64 lowercase hexadecimal characters.
π΄ (01:04) User-provided /home/byk/Code/getsentry/publish/src/publish/__tests__/record-auto-approval-attestation.js is 54 lines and tests recordAutoApprovalAttestation using parseAutoApprovalAttestation.
π΄ (01:04) User-provided record-auto-approval-attestation tests verify an open live request titled publish: getsentry/sentry-javascript@10.0.0, authored by getsantry[bot], with body Merge target: main and no labels produces a parsed attestation { autoApprover: "getsantry[bot]", requestDigest: expect.any(String), title }.
π΄ (01:04) User-provided record-auto-approval-attestation tests verify changing the live issue author to contractor rejects with The automated approval request changed before approval.
π΄ (01:04) User-provided /home/byk/Code/getsentry/publish/src/publish/__tests__/record-ci-ready-attestation.js is 104 lines and tests recordCiReadyAttestation using parseCiReadyAttestation.
π΄ (01:04) User-provided record-CI-ready tests verify an authenticated login "sentry-internal-app[bot]", open accepted issue publish: getsentry/sentry-javascript@10.0.0, and accepted event ID "100" by "contractor" produce parsed fields acceptedActor: "contractor", acceptedEventId: "100", ciReadyActor: "sentry-internal-app[bot]", requestDigest: expect.any(String), and the matching title.
π΄ (01:04) User-provided record-CI-ready tests verify a live title changed to publish: getsentry/sentry-python@10.0.0 rejects with The approval changed before CI could be marked ready.
π΄ (01:04) User-provided record-CI-ready tests verify a re-approval event ID "200" rejects with The approval changed before CI could be marked ready when expectedAcceptedEvent was { actor: "contractor", eventId: "100" }.
π΄ (01:04) User provided a partially elided test file for validateApprovalAttestation; visible portions show it uses createApprovalAttestation, issue number "123", repository "getsentry/publish", accepted-label events, github-actions[bot] comments, and includes tests rejecting an attestation after the issue body changes and validating approval/CI-ready event-binding scenarios.
π΄ (01:04) User-provided /home/byk/Code/getsentry/publish/src/publish/__tests__/workflow-action-pinning.js is 76 lines; it enumerates every .yml file in .github/workflows, reads each as UTF-8, and uses jobContents(workflow, jobName) to isolate a named top-level job.
π΄ (01:04) User-provided SECRET_BEARING_JOBS maps .github/workflows/auto-approve.yml to ["auto-approve"], .github/workflows/ci-poller.yml to ["check-ci"], .github/workflows/cocoapods-keepalive.yml to ["keepalive"], and .github/workflows/publish.yml to ["waiting-for-ci", "publish"].
π΄ (01:04) User-provided workflow-action-pinning tests verify every actions/...@revision reference in every workflow uses exactly 40 lowercase hexadecimal characters.
π΄ (01:04) User-provided workflow-action-pinning tests verify no workflow except .github/workflows/ci-poller-dispatch.yml contains workflow_dispatch:.
π΄ (01:04) User-provided workflow-action-pinning tests verify every job listed in SECRET_BEARING_JOBS contains environment: production.
π΄ (01:04) User-provided workflow-action-pinning tests verify .github/workflows/ci-poller-dispatch.yml contains workflow_dispatch:, permissions: {}, and environment: production, and does not contain secrets..
π΄ (01:05) User-provided /home/byk/Code/getsentry/publish/src/modules/details-from-context.js is 75 lines.
π΄ (01:05) User-provided src/modules/details-from-context.js defines TARGETS_SECTION_PARSER_REGEX = /^(?!### Targets$\s)(?: *- \[[ xX]\] \S+\s*$(?:\r?\n)?)+/m, TARGETS_PARSER_REGEX = /^\s*- \[[ x]\] (\S+)/gim, and CHECKED_TARGETS_PARSER_REGEX = /^\s*- \[x\] (\S+)/gim.
π΄ (01:05) User-provided src/modules/details-from-context.js defines PUBLISH_TITLE_REGEX = /^publish: (?:getsentry\/)?(?<repo>[^/@]+)(?<path>\/[\w./-]+)?@(?<version>[\w.+-]+)$/; parsePublishTitle(title) returns its named groups or null.
π΄ (01:05) User-provided detailsFromContext({ context }) throws Issue context is not defined unless context.payload.issue exists, and throws Invalid publish issue title: ${context.payload.issue.title} when parsePublishTitle(...) fails.
π΄ (01:05) User-provided detailsFromContext(...) sets dry_run to "1" when the issue has label dry-run, otherwise ""; sets path to "." + (titleMatch.path || ""); and parses merge_target from /^Merge target: (?<merge_target>[\w.\-/]+)$/m, defaulting to "".
π΄ (01:05) User-provided detailsFromContext(...) extracts the matched targets section with TARGETS_SECTION_PARSER_REGEX, then returns only checked target IDs found by CHECKED_TARGETS_PARSER_REGEX; if no targets section matches, targets remains undefined.
π΄ (01:05) User-provided detailsFromContext(...) returns { ...titleMatch, dry_run, merge_target, path, targets }; the module exports detailsFromContext, TARGETS_SECTION_PARSER_REGEX, TARGETS_PARSER_REGEX, CHECKED_TARGETS_PARSER_REGEX, parsePublishTitle, and PUBLISH_TITLE_REGEX.