Dashboard › publish › Distillation
6acd1795-c2c8-4043-be71-63cfcb2b4266["lore_tm_v1_e4nB3VklaxRf0a5NqowPtSlFhv1TF7LSwH9x5Yy9mIs","lore_tm_v1_rgOf4Q46fpjqEs3PTEYlPhCyJC22A74K8RvJ3WXqX0Y","lore_tm_v1_sLAWJi9QdGXsO0_lf8quYcKBgJICBhFqYDD0dipNCrg","lore_tm_v1_M-GZHyv-pCqvLwKtRPd9YAsrerGvb3dSYKM-doNZe9U","lore_tm_v1_OJdXffIHfiOS9FFh-mcvgfEAxM3IhEfb24bwvIk5wEY","lore_tm_v1__E9Y-EW-G24Rp7jOp9XGFbHVQAcoj7rdRTLTNk4lQ1E","lore_tm_v1_3jxT06zbJfyAPJdT9SZAl2QR7TpLQtJfpYx-lhC1jhg","lore_tm_v1_Rac3WGJ330z5YcQonCTEQbFyALAMRYUTUSlI4VslXKU","lore_tm_v1_g4sPxObGvtVAgXfznf4Wn2syw9ylySHLqTRZ6fIPIow","lore_tm_v1_Fx9l2JpSgbmBn42HUt9hTcIIMsuFd7VOteATxO0dJyk","lore_tm_v1_n9lQcsjVWjH78AS528AbQv78oU5ORcrfLaE_MTajIOs"]
π΄ (11:57) src/modules/__tests__/approval-authorizer.js contains tests for authorizeApproval() using issue title publish: getsentry/sentry-javascript@10.0.0.
π΄ (11:57) authorizeApproval() rejects direct approval by actors "getsantry[bot]" and "sentry-release-bot[bot]", returns { authorized: false, repository: "getsentry/sentry-javascript" }, and does not call getPermission, even when "getsentry/sentry-javascript" is in autoApprovedRepositories.
π΄ (11:57) authorizeApproval() rejects "getsantry[bot]" when the target release repository is outside the auto-approval allowlist; with allowlist new Set(["getsentry/sentry-python"]), it returns { authorized: false, repository: "getsentry/sentry-javascript" } without calling getPermission.
π΄ (11:57) authorizeApproval() authorizes target-repository collaborators with role_name "write", "maintain", or "admin" and calls getPermission({ owner: "getsentry", repository: "sentry-javascript", username: "contractor" }).
π΄ (11:57) authorizeApproval() resolves the unqualified title publish: sentry-javascript/packages/core@10.0.0 against getsentry/sentry-javascript, authorizing a "write" collaborator after the same getPermission() lookup against owner "getsentry" and repository "sentry-javascript".
π΄ (11:57) authorizeApproval() rejects collaborator roles "none", "read", "triage", "Elevated Bot", and undefined, returning { authorized: false, repository: "getsentry/sentry-javascript" }.
π΄ (11:57) authorizeApproval() rejects malformed titles publish: @1.0.0 and publish: getsentry/sentry-javascript without querying GitHub, returning { authorized: false, repository: null }.
π΄ (11:57) authorizeApproval() propagates errors from getPermission; a rejected lookup with new Error("Not Found") results in rejection with "Not Found".
π΄ (11:58) src/publish/__tests__/authorize-approval.js restores process.env, mocks, and modules after each test; helper 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, APPROVAL_ISSUE_TITLE, and EXPECTED_REQUEST_DIGEST for body "Merge target: main" with label "accepted".
π΄ (11:58) getAutoApprovedRepositories() is tested to parse exact newline-delimited release paths into new Set(["getsentry/sentry-javascript", "getsentry/objectstore/clients"]).
π΄ (11:58) On a successful "write" permission lookup for "contractor", the authorize-approval entry point fetches https://api.github.com/repos/getsentry/sentry-javascript/collaborators/contractor/permission with header Authorization: "Bearer release-bot-token" and appends output matching authorized=true\napproval_attestation=<!-- publish-approval .+ -->\n to /tmp/github-output.
π΄ (11:58) The successful authorization test binds approval to an open issue authored by "requester" with body "Merge target: main", label "accepted", title publish: getsentry/sentry-javascript@10.0.0, and accepted-label event ID "100" performed by "contractor".
π΄ (11:58) The authorize-approval entry point rejects self-approval when the approving actor and issue author are both "contractor", appending exactly "authorized=false\n" to /tmp/github-output.
π΄ (11:58) The authorize-approval entry point rejects a live approval when the issue snapshot differs from EXPECTED_REQUEST_DIGEST; the test changes the body to "changed after accepted" and labels to "accepted" plus "dry-run", then expects "authorized=false\n".
π΄ (11:58) The authorize-approval entry point authorizes an automated request for publish: getsentry/relay@1.2.3 when the issue opener is "getsantry[bot]", the approving actor is "sentry-internal-app[bot]", accepted-label event ID "100" belongs to that actor, and a comment by "github-actions[bot]" contains createAutoApprovalAttestation({ autoApprover: "getsantry[bot]", issue }); it performs no /collaborators/ fetch and writes an approval attestation.
π΄ (11:58) The authorize-approval entry point rejects the same automated approval flow when no auto-approval request-proof comment exists, appending exactly "authorized=false\n".
π΄ (11:58) The authorize-approval entry point rejects direct approval by automated opener "sentry-release-bot[bot]" for publish: getsentry/relay@1.2.3 without any fetch calls and writes "authorized=false\n".
π΄ (11:58) The authorize-approval entry point fails closed when GitHub returns HTTP 404 for a permission lookup, rejecting with Could not retrieve contractor's permission for getsentry/sentry-javascript: GitHub returned 404; it neither logs through console.error nor writes to GITHUB_OUTPUT.
π΄ (11:58) src/publish/__tests__/record-auto-approval-attestation.js tests recordAutoApprovalAttestation() and parses its output with parseAutoApprovalAttestation().
π΄ (11:58) recordAutoApprovalAttestation() binds automated requester "getsantry[bot]" to the live open request publish: getsentry/sentry-javascript@10.0.0; for matching body "Merge target: main", empty labels, author, title, and expected digest, the parsed attestation is { autoApprover: "getsantry[bot]", requestDigest: expect.any(String), title }.
π΄ (11:58) recordAutoApprovalAttestation() rejects with "The automated approval request changed before approval" if the expected auto-approver is "getsantry[bot]" but the live issue author is "contractor".
π΄ (11:58) recordAutoApprovalAttestation() rejects with "The automated approval request changed before approval" when the expected opened-event snapshot has body "old request" and no labels but the live request has body "changed request" and label "dry-run".
π΄ (11:59) src/publish/__tests__/record-ci-ready-attestation.js tests recordCiReadyAttestation() and parses its output with parseCiReadyAttestation().
π΄ (11:59) recordCiReadyAttestation() binds the current approval to the authenticated app that will add "ci-ready"; for accepted event { actor: "contractor", eventId: "100" }, authenticated login "sentry-internal-app[bot]", and a matching live issue snapshot, the parsed attestation is { acceptedActor: "contractor", acceptedEventId: "100", ciReadyActor: "sentry-internal-app[bot]", requestDigest: expect.any(String), title }.
π΄ (11:59) recordCiReadyAttestation() rejects with "The approval changed before CI could be marked ready" when the observed issue title was publish: getsentry/sentry-javascript@10.0.0 but the live title is publish: getsentry/sentry-python@10.0.0.
π΄ (11:59) recordCiReadyAttestation() rejects with "The approval changed before CI could be marked ready" when expectedAcceptedEvent is { actor: "contractor", eventId: "100" } but the latest live accepted-label event has ID "200".
π΄ (11:59) recordCiReadyAttestation() rejects with "The approval changed before CI could be marked ready" when the pollerβs expected request snapshot has body "old revision" and only label "accepted", while the live issue has body "new revision" and labels "accepted" plus "dry-run".
π΄ (11:59) src/publish/__tests__/current-accepted-event.js verifies that getCurrentAcceptedEvent() returns the latest accepted-label event; from event IDs "100" and "200" by "contractor", it returns { actor: "contractor", eventId: "200" }.
π΄ (11:59) getCurrentAcceptedEvent() rejects an empty issue-event list with "The issue has no current accepted label event".
π΄ (11:59) src/publish/__tests__/request-digest-from-event.js verifies that requestDigestFromEvent({ issue }) returns requestDigest(issue) for the exact event snapshot, including body "Merge target: main", labels "accepted" and "dry-run", and title publish: getsentry/relay@1.2.3.
π΄ (11:59) requestDigestFromEvent({}) throws "The GitHub event has no issue snapshot"; an incomplete snapshot { issue: { body: "", labels: [] } } throws "Invalid publish request".
π΄ (11:59) src/publish/__tests__/workflow-action-pinning.js enumerates every .yml file in .github/workflows and verifies every actions/* reference is pinned to a lowercase 40-character hexadecimal revision.
π΄ (11:59) src/publish/__tests__/workflow-action-pinning.js defines secret-bearing jobs as: .github/workflows/auto-approve.yml β "auto-approve"; .github/workflows/ci-poller.yml β "check-ci"; .github/workflows/cocoapods-keepalive.yml β "keepalive"; .github/workflows/publish.yml β "waiting-for-ci" and "publish".
π΄ (11:59) Every job listed in SECRET_BEARING_JOBS must contain environment: production.
π΄ (11:59) Workflow tests prohibit workflow_dispatch: in every workflow except .github/workflows/ci-poller-dispatch.yml.
π΄ (11:59) .github/workflows/ci-poller-dispatch.yml is the protected workflow-dispatch relay and must contain workflow_dispatch:, permissions: {}, and environment: production, while containing no secrets. reference.
π΄ (12:00) .github/workflows/test.yml pins actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803, actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38, and actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830.
π΄ (12:00) .github/workflows/publish.yml references actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803, actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1, actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38, actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1, and docker://getsentry/craft@sha256:9a4a5d5efa44a00c2215078ead39800d4aaa5a97908b94f45a64d7d506d6e14b.
π΄ (12:00) .github/workflows/ci-poller.yml references actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 and actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1.
π΄ (12:00) .github/workflows/auto-approve.yml references actions/checkout@11d5960a326750d5838078e36cf38b85af677262 and actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1.
π΄ (12:00) The repository contains 6 workflow files: .github/workflows/test.yml, .github/workflows/publish.yml, .github/workflows/cocoapods-keepalive.yml, .github/workflows/ci-poller.yml, .github/workflows/auto-approve.yml, and .github/workflows/ci-poller-dispatch.yml.