Dashboard › publish › Distillation
bba9630a-abdd-4262-9ebf-29e31182fae0["lore_tm_v1_Q9r4bGquTdOrbS5SihZCAD04rOmHlaoEHAkOEa5xJ1Y","lore_tm_v1_dJ2QuYLyISkaWMczuv0GtI2xdEU-mudS6XiHffjFe9U","lore_tm_v1_WIvoQ-5gX0MKdHnoCQbEfXcSlqBuejh1Ufd0mTpKkcA","lore_tm_v1_p0dWWRGQpewOct0N4k-1-ixREbvIWqGjEmC_6uy2OkQ","lore_tm_v1_SII9SdaWaQ65lE3bQSUGMQEJClUoNbIFeyE9UYcaqRk","lore_tm_v1_m43P3cTl2fwdkT5DOBIa52ode3jQYq_leZV1Ub7Cqho","lore_tm_v1_I4tR4uDiuJrBtlBCCBXAQJV2-uhwLTX-5YPCTls8ZtU","lore_tm_v1_6v6gZX8UhPVxmxPTvBM_Ya0fdJJvdeQskOVr_L2Dmv8","lore_tm_v1_OEX-WJTr474JHteaBU4A4zesZrH8XhFc9rEgAm52V4U","lore_tm_v1_ZVl9t3quLTG5tkU3GAevXA_QbrlVm_raE_7zyjVNLI4","lore_tm_v1_Tgj_AGf9wsUi6_nDtl5xtVYXMQAv2O316J2MoM04EV0","lore_tm_v1_XptP6maXKWbpAipi6--JpgoP_REH0SnY-y73CJd6pZU","lore_tm_v1_9ll4VbLmJNgQPK6k3ocL3r-LOTy2LysOCa_xEuJa-CQ","lore_tm_v1_neAnspA2gLcrRQM7UmFhBI4Y9af9Mpk2-wcDNgnOWHc"]
Date: Sep 8, 2026
src/modules/approval-attestation.js defines HTML-comment attestation formats <!-- publish-approval ${base64url(JSON)} --> and <!-- publish-ci-ready ${base64url(JSON)} -->; createAttestation() serializes JSON using Buffer.from(JSON.stringify(value)).toString("base64url").requestDigest({ body, labels, title }) in src/modules/approval-attestation.js rejects a non-string body or non-array labels with Error("Invalid publish request"), then returns a SHA-256 hex digest of JSON containing body, title, and dryRun determined by a dry-run label.createApprovalAttestation() bind actor, stringified eventId, requestDigest, and issue title; CI-ready attestations created by createCiReadyAttestation() bind acceptedActor, stringified acceptedEventId, ciReadyActor, requestDigest, and issue title.src/modules/approval-attestation.js finds a prefix and --> suffix, decodes base64url JSON, and returns null for absent/malformed data. parseApprovalAttestation() requires string actor, eventId, requestDigest, and title; parseCiReadyAttestation() requires string acceptedActor, acceptedEventId, ciReadyActor, requestDigest, and title.compareEventIds(left, right) normalizes leading zeroes, compares arbitrary-length decimal event IDs by digit length then localeCompare, and throws Error("Invalid issue event ID") for non-decimal IDs. currentLabeledEvent() selects the latest valid labeled event for a requested label, requiring string/number id and actor.login; it returns { actor, eventId } or null if no valid event or invalid IDs. currentAcceptedEvent() and currentCiReadyEvent() use labels accepted and ci-ready, respectively.hasApprovalAttestation() requires a comment authored by attestationAuthor whose approval attestation exactly matches the current accepted event actor/event ID, current request digest, and current issue title. hasCiReadyAttestation() similarly requires a trusted comment whose CI-ready attestation exactly matches the current accepted event, the actor of the latest ci-ready label event, request digest, and title.src/modules/approval-authorizer.js allows approval actors with target-repository roles write, maintain, or admin; it identifies the automated approver as getsantry[bot]. Parsed publish titles map to repository getsentry/${title.repo} and allowlist key ${repository}${title.path || ""}.authorizeApproval(): malformed publish titles are rejected with { authorized: false, repository: null }; getsantry[bot] is authorized only when its exact release path appears in autoApprovedRepositories, without GitHub permission lookup; non-bot actors are checked through getPermission({ owner: "getsentry", repository: title.repo, username: actor }), with only write/maintain/admin accepted.src/publish/authorize-approval.js reads exact nonempty allowlist lines from auto-approve-repos.txt into a Set; GitHub REST requests use APPROVAL_TOKEN, Accept: application/vnd.github+json, and X-GitHub-Api-Version: "2026-03-10".src/publish/authorize-approval.js paginates issue events at repos/${repository}/issues/${issueNumber}/events?per_page=100&page=${page} until a page has fewer than 100 records. It requires GITHUB_OUTPUT, APPROVAL_TOKEN, APPROVAL_ISSUE_NUMBER, and APPROVAL_ISSUE_REPOSITORY; after authorization, it concurrently fetches the issue and all events, then fails closed (authorized=false) if the title changed, no accepted event exists, or the latest accepted-label actor differs from APPROVAL_ACTOR.src/publish/authorize-approval.js appends authorized=true plus approval_attestation=${attestation} to GITHUB_OUTPUT; GitHub permission lookup failures throw, e.g. Could not retrieve contractor's permission for getsentry/sentry-javascript: GitHub returned 404, rather than emitting an authorization result.src/publish/current-accepted-event.js fetches paginated issue events through getAllPages() from validate-approval-attestation.js, returns the latest accepted-label event, and throws Error("The issue has no current accepted label event") when absent. Its CLI requires APPROVAL_TOKEN, APPROVAL_ISSUE_NUMBER, and APPROVAL_ISSUE_REPOSITORY, then emits JSON to stdout.src/publish/record-ci-ready-attestation.js obtains the current authenticated GitHub login from the user API endpoint and requires it to be a string. recordCiReadyAttestation() concurrently fetches the issue, all events, and that authenticated login; it throws Error("The approval changed before CI could be marked ready") if the issue title differs, accepted is absent, no accepted event exists, or optional expectedAcceptedEvent actor/event ID no longer matches.recordCiReadyAttestation() creates a CI-ready attestation bound to the accepted event and authenticated app login. Its CLI requires GITHUB_OUTPUT, APPROVAL_TOKEN, APPROVAL_ISSUE_NUMBER, APPROVAL_ISSUE_REPOSITORY, and APPROVAL_ISSUE_TITLE; if both EXPECTED_ACCEPTED_ACTOR and EXPECTED_ACCEPTED_EVENT_ID exist, they form the expected event; output is ci_ready_attestation=${attestation}.src/publish/validate-approval-attestation.js fetches issue data plus all event/comment pages concurrently; getAllPages() uses ?per_page=100&page=${page} and throws on non-OK GitHub responses. Validation requires a matching issue title, current accepted label, latest accepted event, optional expected actor/event match, and matching trusted approval attestation.requireCiReadyAttestation: true, validateApprovalAttestation() additionally requires a current ci-ready label, latest valid ci-ready event, and trusted CI-ready attestation bound to both the current accepted event and current ci-ready label actor. CLI input REQUIRE_CI_READY_ATTESTATION === "true" enables this requirement; invalid results throw Error("The current accepted label has no matching approval attestation").src/modules/__tests__/approval-attestation.js verifies latest accepted event selection ("200" over "100"), numeric GitHub event ID 29503999078 normalization, trusted-author matching, malformed base64 rejection, invalidation after issue title/body/dry-run changes, and CI-ready proof invalidation when either the accepted event changes ("201") or ci-ready label actor differs from attested sentry-internal-app[bot].src/modules/__tests__/approval-authorizer.js tests auto-authorization of getsantry[bot] for allowlisted getsentry/sentry-javascript, rejection outside the allowlist without permission lookup, human collaborator authorization for write/maintain/admin, rejection for none/read/triage/Elevated Bot/undefined, unqualified publish: sentry-javascript/packages/core@10.0.0 resolution to getsentry/sentry-javascript, malformed-title rejection without GitHub calls, and propagation of permission lookup errors.src/publish/__tests__ contains exactly 5 test files: authorize-approval.js, ci-poller-workflow.js, current-accepted-event.js, record-ci-ready-attestation.js, and validate-approval-attestation.js.src/publish/__tests__/ci-poller-workflow.js asserts .github/workflows/ci-poller.yml checks out trusted publish code using actions/checkout@v6 at ${{ github.event.repository.default_branch }} before the workflowβs Get auth token step.src/publish/__tests__/authorize-approval.js verifies auto-approve-repos.txt preserves exact release paths including getsentry/objectstore/clients, successful human authorization writes an approval comment-form attestation, and a 404 permission response fails closed by throwing without writing GITHUB_OUTPUT.src/publish/__tests__/current-accepted-event.js verifies getCurrentAcceptedEvent() chooses event "200" over "100" and rejects an issue with no valid accepted-label event.src/publish/__tests__/record-ci-ready-attestation.js verifies CI-ready proof contains accepted actor contractor, accepted event ID "100", and authenticated app sentry-internal-app[bot]; it rejects both a title/event change before recording and re-approval changing expected event "100" to "200".src/publish/__tests__/validate-approval-attestation.js verifies approval validation rejects title changes, body changes, removed accepted, accepted label re-addition creating a newer event, and mismatch against an earlier expected accepted event; it accepts CI-ready proof only when the latest ci-ready event actor is the attested sentry-internal-app[bot] and rejects a manually applied ci-ready label by contractor.