Dashboard › publish › Distillation
9fb858c4-9e00-4ecb-89d7-f6b2de91edcc["lore_tm_v1_DeOTPOQYdPZTQQcUj1gvVjDQqttbcnp74VT9d9H_hBI","lore_tm_v1_7AnXBhbXh7YPSmytg9EVwKx1XpfVswHCDWY20LdDuxc","lore_tm_v1_B26YeLtvi7vC8KPLbit9OfGOasZ7urY8mmMQYPLFQKs","lore_tm_v1_yZPoo5LWWKzfGCq2wBvrUWW7GB_9_gh3f-brNfenP64","lore_tm_v1_kBRscDGkmASUYfWmGRAaOsBvAWro19CMD2mFnwxPzkE","lore_tm_v1_41Mq9Pdw6iHFBUegJqPap_s10Ob4M1Pzq6OSBOBdxls","lore_tm_v1_DWoLEmafoijvbM04lr_AenzHlvm7SdbazxIt1eIiec8","lore_tm_v1_-0tnoThCM5jd-WnDdvo-r1Y9YWGLGopLCVSqKrT6EYA","lore_tm_v1_aa4Y47M0XbjUibWlv6IZIi9JWs3OtNJgaD4mvnQKoa0","lore_tm_v1_G5VQEMX8SmHbUGgowd_wUdV8FXOCAkqM8cytcwY7--Q","lore_tm_v1_-d1_Mfq3F8Yar09eSu6lRZwk4H6vullENkuxY2Kry2c","lore_tm_v1_lfIzUza7CLiwygxXJYZs0Z-c-_lIOF6Y23-oaCywsPU","lore_tm_v1_FsPStkDOx-WE1ply5Xz2qeih3tOyaXm7S1fSzXQZ8n4","lore_tm_v1_MjuqA9MZXX1RXB-dkfNUWaFTyfWrNAq5c4MY0rxKgI0","lore_tm_v1_YtQN8UqCAbPOv5AYcAY9t5bhxAMgkubuXO8coJByh84","lore_tm_v1_mwlR51kDCcquEJ-8UAY5FodPlFP_InGKFUR2NjDr8oI","lore_tm_v1_JU8ANh9SVVPAYCx6ZboA7Vbi2TY6G-e89PR8XzgpP1I","lore_tm_v1_ctkgko5nQkrFGqLoxPWSHNZyRxaus7Uw-licfiQwXsM","lore_tm_v1_A0_bcTTMI4_04_crhXdpcDeSwhLEHouCPfEQRBmvBR4","lore_tm_v1__Ynkv_PkRuMTk6f6F2ro3SzEuQNPu64gWikV_aMvzqY","lore_tm_v1_GMAQQ-5ALcYONjokMVJ1bwqg4PaxMguIiGgs6Qm8sDI"]
Date: Aug 29, 2026
src/publish/validate-approval-attestation.js (138 lines). validateApprovalAttestation() fetches issue metadata, all events, and all comments; requires matching issueTitle, live accepted label, latest accepted-label event, and hasApprovalAttestation(). When requireCiReadyAttestation is true, it additionally requires a latest ci-ready event and hasCiReadyAttestation(). getGitHubResponse() authenticates with APPROVAL_TOKEN and GitHub API version "2026-03-10"; getAllPages() retrieves 100 records/page. Required CLI environment variables are APPROVAL_TOKEN, APPROVAL_ISSUE_NUMBER, APPROVAL_ISSUE_REPOSITORY, APPROVAL_ISSUE_TITLE, and APPROVAL_ATTESTATION_AUTHOR; REQUIRE_CI_READY_ATTESTATION === "true" enables CI-ready proof validation.accepted-label event and issue title..github/workflows/ci-poller.yml lines 70–356. It polls open issues carrying both ci-pending and accepted labels (up to 200), validates the approval attestation before CI lookup and again after CI passes/immediately before transition, and on invalid/drifted approval removes both ci-pending and accepted then comments: “Approval is invalid or could not be verified. Re-add the accepted label to retry after resolving the issue.”ci-poller.yml derives target repository/version with parsePublishTitle() from src/modules/details-from-context.js, extracts a 40-hex commit SHA from the issue’s /commit/{SHA}/checks link, resolves release branch and its current HEAD where possible, and updates the issue body’s check-run link if branch HEAD changed.ci-poller.yml considers CI ready only when at least one check run or commit status exists; commit status is "success" or there are no statuses; no check run is pending; and no completed check has a conclusion other than "success", "neutral", or "skipped". It treats failure, cancelled, timed_out, action_required, stale, and startup_failure as unsuccessful.ci-poller.yml transitions passing issues by removing ci-pending and adding ci-ready; failed checks/statuses remove ci-pending and accepted, add ci-failed, and provide failed-check/status details plus a check-runs URL. Cleanup synchronizes CI_POLLER_HAS_PENDING; self-dispatch retries up to 60 attempts, then relies on the 5-minute cron fallback.ci-ready on the passing transition..github/workflows/publish.yml. It triggers on labeled issues, includes waiting-for-ci and publishing jobs, uses cancel-in-progress: false, treats ci-ready as the publishing trigger, and contains comments that the poller always adds ci-ready, guaranteeing the happy-path publishing trigger.src/modules/details-from-context.js (75 lines). parsePublishTitle(title) returns named groups from PUBLISH_TITLE_REGEX: /^publish: (?:getsentry\/)?(?<repo>[^/@]+)(?<path>\/[\w./-]+)?@(?<version>[\w.+-]+)$/, or null. detailsFromContext() validates an issue context/title; sets dry_run to "1" when label dry-run is present; parses optional Merge target: <[\w.\-/]+>; derives path as "." + (titleMatch.path || ""); and returns title fields plus dry_run, merge_target, path, and checked targets.src/publish/__tests__/validate-approval-attestation.js (175 lines). Existing tests cover accepting a current trusted approval attestation; rejecting title changes; rejecting missing live accepted; and rejecting approval attestations after accepted is re-added with newer event ID "200" replacing "100".ci-ready event before that event existed. Chosen design: record a proof before label write that names the exact internal-app identity expected to apply ci-ready; publication must require the actual live ci-ready event’s actor identity plus a proof bound to the current approval. This replaced the incomplete attempt to bind a future event directly.src/modules/approval-attestation.js, src/publish/record-ci-ready-attestation.js, src/publish/validate-approval-attestation.js, and src/modules/__tests__/approval-attestation.js.ci-ready; planned wiring: poller records it, publisher requires it, and poller revalidates approval immediately before the label transition..github/workflows/ci-poller.yml and .github/workflows/publish.yml.yarn vitest run src/modules/__tests__/approval-attestation.js src/publish/__tests__/validate-approval-attestation.js under Vitest v4.0.17 completed with 2/2 test files and 8/8 tests passing (4 tests each), duration 259ms; Yarn completed in 0.84s.issues: read, so it could not write the trusted CI-ready proof comment. Planned fix: grant issue-write permission and add end-to-end validation tests for genuine versus manually added ci-ready labels..github/workflows/ci-poller.yml and src/publish/__tests__/validate-approval-attestation.js.src/modules/__tests__/approval-attestation.js 4 tests and src/publish/__tests__/validate-approval-attestation.js 6 tests—duration 312ms; Yarn completed in 0.79s.prettier --write src --write src/modules/approval-attestation.js src/modules/__tests__/approval-attestation.js src/publish/record-ci-ready-attestation.js src/publish/validate-approval-attestation.js src/publish/__tests__/validate-approval-attestation.js .github/workflows/ci-poller.yml .github/workflows/publish.yml; it formatted all listed src files and both workflow YAML files; completed in 0.82s.ci-ready; planned direct unit coverage for proof recording, including approval drift immediately before transition.src/publish/__tests__/record-ci-ready-attestation.js.yarn vitest run src/modules/__tests__/approval-attestation.js src/publish/__tests__/validate-approval-attestation.js src/publish/__tests__/record-ci-ready-attestation.js completed with 3/3 files and 12/12 tests passing—4, 6, and 2 tests respectively—duration 379ms; Yarn completed in 0.88s.src/modules/approval-attestation.js (180 lines). It defines APPROVAL_ATTESTATION_PREFIX = "<!-- publish-approval ", CI_READY_ATTESTATION_PREFIX = "<!-- publish-ci-ready ", and ATTESTATION_SUFFIX = " -->"; createAttestation() base64url-encodes JSON payloads.createApprovalAttestation({ actor, eventId, title }) emits { actor, eventId: String(eventId), title }. createCiReadyAttestation({ acceptedEvent, ciReadyActor, title }) emits { acceptedActor: acceptedEvent.actor, acceptedEventId: String(acceptedEvent.eventId), ciReadyActor, title }.parseAttestation() locates a prefixed HTML comment and safely base64url-decodes/JSON-parses it, returning null for absent, unterminated, or malformed data. parseApprovalAttestation() requires string actor, eventId, and title; parseCiReadyAttestation() requires string acceptedActor, acceptedEventId, ciReadyActor, and title.compareEventIds(left, right) strips leading zeros, validates numeric IDs with /^\d+$/, compares digit length then localeCompare, and throws "Invalid issue event ID" for nonnumeric values. currentLabeledEvent(events, labelName) selects the latest valid labeled event for a label based on this numeric comparison, requiring string/number event.id and event.actor.login; it returns { actor, eventId: String(event.id) } or null. currentAcceptedEvent() and currentCiReadyEvent() call it for "accepted" and "ci-ready".hasApprovalAttestation() accepts only comments authored by attestationAuthor whose approval proof exactly matches current accepted event actor/event ID and title. hasCiReadyAttestation() accepts only comments by attestationAuthor whose CI-ready proof exactly matches current accepted actor/event ID, live ciReadyEvent.actor, and title—thereby rejecting manually applied ci-ready labels with a different actor.