Dashboard › publish › Distillation
860f6e6a-2a7d-483b-b764-c4042225af96["lore_tm_v1_snmku1OHjkI5XG1NBJho7FDPyeJJc08KlidO_gd0cs0","lore_tm_v1_2X4BA4XGESaI_UHyFyrV5WKtjAkCRjUfzzqrBy4uW50","lore_tm_v1_xOon0EtVI5h1zzTUm3TKYmmOWDf7DviOxyJdDPgzwSg","lore_tm_v1_iQOjwfTwqcaBsHYz_IjDr4n7pq4f9uveGv1GzrGsf14","lore_tm_v1_UO7jmNfqg2d1-_h1gy3yWHvd1jLStAcDSP0w_Si1PNs"]
🔴 (13:19) [enforced-workflow] User directed: “Always run trusted code.” Because workflow_dispatch can target any ref, the CI poller workflow must check out trusted code rather than code from the trigger’s arbitrary ref.
🔴 (13:19) [enforced-workflow] User directed: “Never move a release to ci-ready after it changes.” Each polling cycle must bind itself to the current accepted-label event before inspecting CI.
🔴 (13:19) [enforced-workflow] User specified that a renamed or re-approved publish issue “never reaches ci-ready”; the poller must revalidate the issue and approval immediately before the ci-ready transition.
🔴 (13:19) [enforced-workflow] User specified that the CI poller “always adds ci-ready” after checking CI, even if the label was previously present; .github/workflows/publish.yml first removes ci-ready so the later add produces a fresh labeled event.
🔴 (13:19) /home/byk/Code/getsentry/publish/.github/workflows/publish.yml defines the Publish workflow, triggered by issue labeled events. Its concurrency group is ${{ github.event.issue.title }} with cancel-in-progress: false; permissions are contents: read, issues: write, and packages: write.
🔴 (13:19) In .github/workflows/publish.yml, the waiting-for-ci job runs on ubuntu-latest in the production environment only when the added label is accepted, the issue is open, and its title starts with publish: .
🔴 (13:19) The waiting-for-ci sequence is: authorize the approval with src/publish/authorize-approval.js; post the resulting approval attestation; validate it with src/publish/validate-approval-attestation.js; reject invalid approval by removing accepted, commenting Approval is invalid or could not be verified. Re-add the accepted label to retry after resolving the issue., and exiting 1; then reset labels by removing ci-failed and ci-ready and adding ci-pending.
🔴 (13:19) Approval authorization uses APPROVAL_TOKEN, APPROVAL_ACTOR, APPROVAL_ISSUE_NUMBER, APPROVAL_ISSUE_REPOSITORY, and APPROVAL_ISSUE_TITLE. Validation expects APPROVAL_ATTESTATION_AUTHOR=github-actions[bot].
🔴 (13:19) When marking an accepted issue pending, .github/workflows/publish.yml comments either Retrying — CI was previously failed. Checking CI status now. when ci-failed was present, or Approved. Checking CI status on the release branch. Publishing will start automatically when CI passes. otherwise.
🔴 (13:19) The workflow best-effort enables scheduled polling by setting repository variable CI_POLLER_HAS_PENDING to "true" using gh variable set CI_POLLER_HAS_PENDING -R "$GITHUB_REPOSITORY" -b "true", then immediately triggers polling with POST repos/$GITHUB_REPOSITORY/dispatches and event_type=ci-poller; an app token is used because GITHUB_TOKEN workflow-dispatch events are suppressed.
🔴 (13:19) The publish job runs on ubuntu-latest in production, has timeout-minutes: 90, and fires only from a ci-ready label event on an open issue that currently has both accepted and ci-ready and has neither ci-pending nor ci-failed.
🔴 (13:19) The publish job validates approval with REQUIRE_CI_READY_ATTESTATION: "true" before setup and revalidates it again immediately before Craft publishing. Either failed validation removes accepted, posts the invalid-approval retry comment, and exits 1.
🔴 (13:19) .github/workflows/publish.yml uses actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803, actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1, and actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38; Node is version 24, with Yarn caching against .__publish__/yarn.lock.
🔴 (13:19) Target-repository Craft configuration may be selected from protected merge-target branches for: sentry-migr8/tmp-merge-target; sentry-javascript/v10, v9, v8, v7, or master; sentry-python/alpha; and sentry-wizard/1.x.
🔴 (13:19) The Set targets step writes Craft publish state outside the checked-out target repository under $GITHUB_WORKSPACE/.craft-state/craft. It hashes the container cwd with SHA-1 and takes characters 1-12; the state filename is publish-state-${owner_sanitised}-${repo_sanitised}-${cwd_hash}-${version_sanitised}.json.
🔴 (13:19) Craft runs from docker://getsentry/craft@sha256:9a4a5d5efa44a00c2215078ead39800d4aaa5a97908b94f45a64d7d506d6e14b, executes craft publish <version>, and uses XDG_STATE_HOME=/github/workspace/.craft-state so target-repository contents cannot pre-populate Craft’s publish state.
🔴 (13:19) The publish workflow runs src/publish/update-issue.js on cancellation or failure, src/publish/post-result.js cancelled on cancellation, src/publish/post-result.js failure on failure, and src/publish/post-result.js success on success.
🔴 (13:21) /home/byk/Code/getsentry/publish/src/publish/record-ci-ready-attestation.js defines getAuthenticatedLogin(), recordCiReadyAttestation(), and main(). getAuthenticatedLogin() calls getGitHubResponse("user"), requires an OK response with a string login, and otherwise throws either Could not retrieve the authenticated user: GitHub returned ${response.status} or GitHub returned no authenticated user login.
🔴 (13:21) recordCiReadyAttestation() concurrently loads the issue, issue events, and authenticated login; obtains currentAcceptedEvent(events); and rejects with The approval changed before CI could be marked ready unless the issue remains open, its title still equals issueTitle, it still has accepted, an accepted event exists, and any supplied expectedAcceptedEvent still matches both actor and event ID.
🔴 (13:21) record-ci-ready-attestation.js requires environment variables GITHUB_OUTPUT, APPROVAL_TOKEN, APPROVAL_ISSUE_NUMBER, APPROVAL_ISSUE_REPOSITORY, and APPROVAL_ISSUE_TITLE. Optional EXPECTED_ACCEPTED_ACTOR and EXPECTED_ACCEPTED_EVENT_ID bind recording to a specific acceptance event. The script appends ci_ready_attestation=${attestation} to GITHUB_OUTPUT.
🔴 (13:21) /home/byk/Code/getsentry/publish/src/modules/approval-attestation.js uses attestation markers <!-- publish-approval , <!-- publish-auto-approval , and <!-- publish-ci-ready with suffix -->; payloads are JSON encoded as base64url.
🔴 (13:21) requestDigest({ body, labels, title }) validates that body is a string and labels is an array, then computes a SHA-256 hex digest over JSON containing body, dryRun derived from the presence of the dry-run label, and title.
🔴 (13:21) Approval attestations contain actor, stringified eventId, requestDigest, and title; auto-approval attestations contain autoApprover, requestDigest, and title; CI-ready attestations contain acceptedActor, stringified acceptedEventId, ciReadyActor, requestDigest, and title.
🔴 (13:21) compareEventIds(left, right) strips leading zeroes, validates decimal-only IDs, compares differing lengths numerically by length, and otherwise uses localeCompare; invalid values throw Invalid issue event ID.
🔴 (13:21) currentLabeledEvent(events, labelName) selects the labeled event with the greatest event ID among events having a matching label, string-or-number ID, and actor login. currentAcceptedEvent() targets accepted; currentCiReadyEvent() targets ci-ready.
🔴 (13:21) hasApprovalAttestation(), hasAutoApprovalAttestation(), and hasCiReadyAttestation() only accept comments from the configured attestationAuthor and require the attestation’s actors/event IDs, current request digest, and issue title to match. CI-ready validation additionally binds the attestation’s ciReadyActor to the actor of the current ci-ready labeled event.
🔴 (13:21) User supplied malformed and edge-case Base64 samples and observed outputs: not-base64 → ��~m��; !!!! → an empty result; e30garbage → {} followed by binary garbage j�ځ; YWJjZA==junk → abcd; and eyJhIjoxfQ → {"a":1}.