Dashboard › publish › Distillation
9b87f8b3-e006-4c1d-b097-634cda92161b["lore_tm_v1__-zXFnSXvbR4j8axBmExE27RE772oi9EhnuLaENta-c","lore_tm_v1_AHK4hHAefx-yO1eZNp7HusPwccwtjevbKUBV1ZpcJSg","lore_tm_v1_CkR02LpXlVhSWLNag1dQuI5GrOFbM7l8si5VL41tFsY","lore_tm_v1_y4WuIfVkWw4tv9MlM-ZyDUl6eoNWNKYU-Vlhf2IASM8","lore_tm_v1_WRJavWTZ8zuP4Ei65KAZfmOwhg_FX1OKUIFHlB1JhtM","lore_tm_v1_iVqaSbD-qRc3yBZSIp15IISLHPi51_rf1ei4T9xzqZs"]
.github/workflows/publish.yml is 435 lines and defines workflow Publish, triggered by issue labeled events, with concurrency group ${{ github.event.issue.title }}, cancel-in-progress: false, and permissions contents: read, issues: write, and packages: write.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: .waiting-for-ci checks out trusted publish code using actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 with persist-credentials: false; it obtains a sentry-internal-app token and, with continue-on-error: true, a release-bot token scoped to owner getsentry.waiting-for-ci runs node src/publish/authorize-approval.js with APPROVAL_TOKEN, APPROVAL_ACTOR, APPROVAL_ISSUE_NUMBER, APPROVAL_ISSUE_REPOSITORY, and APPROVAL_ISSUE_TITLE; when authorized, it posts ${{ steps.authorization.outputs.approval_attestation }} as an issue comment using ${{ github.token }}.node src/publish/validate-approval-attestation.js with APPROVAL_ATTESTATION_AUTHOR=github-actions[bot]; authorization, attestation posting, or validation failure removes accepted, comments Approval is invalid or could not be verified. Re-add the accepted label to retry after resolving the issue., and exits 1.ci-failed and ci-ready and adding ci-pending; removing an existing ci-ready ensures a later fresh ci-ready labeled event can trigger publishing.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.waiting-for-ci best-effort enables the cron poller by obtaining a token from vars.CI_POLLER_APP_CLIENT_ID/secrets.CI_POLLER_APP_PRIVATE_KEY and running gh variable set CI_POLLER_HAS_PENDING -R "$GITHUB_REPOSITORY" -b "true"; it immediately dispatches event_type=ci-poller through repos/$GITHUB_REPOSITORY/dispatches using the app token because GITHUB_TOKEN workflow-dispatch events are suppressed.ci-ready after checking CI; even if ci-ready was already present, waiting-for-ci removes it first so a fresh labeled event always fires.publish job runs only for an open issueβs ci-ready labeled event when both accepted and ci-ready are present and neither ci-pending nor ci-failed is present; it runs on ubuntu-latest in the production environment with timeout 90 minutes.publish job checks out publish code to .__publish__ with credentials disabled, then validates approval via node .__publish__/src/publish/validate-approval-attestation.js with REQUIRE_CI_READY_ATTESTATION="true"; failure removes accepted, posts the invalid-approval retry comment, and exits 1.24, Yarn caching keyed by .__publish__/yarn.lock, yarn install --cwd ".__publish__", and node .__publish__/src/publish/inputs.js; successful input parsing runs node .__publish__/src/publish/post-workflow-details.js.merge_target only for: sentry-migr8 β tmp-merge-target; sentry-javascript β v10, v9, v8, v7, or master; sentry-python β alpha; and sentry-wizard β 1.x. The workflow states these registered branches must be protected.getsentry/${{ fromJSON(steps.inputs.outputs.result).repo }} into __repo__, using ${{ steps.target-repo-branch.outputs.target_repo_branch || ''}} and fetch-depth: 0.Set targets step renders Craftβs already-published state as {"published": ...}, places it outside __repo__ under $GITHUB_WORKSPACE/.craft-state/craft, and computes the filename publish-state-${owner_sanitised}-${repo_sanitised}-${cwd_hash}-${version_sanitised}.json./github/workspace/__repo__, monorepo paths beneath that directory, strips a trailing slash, hashes the container working directory using SHA-1 truncated to 12 characters, and sanitizes owner/repository/version by lowercasing and replacing disallowed runs with _.validate-approval-attestation.js with REQUIRE_CI_READY_ATTESTATION="true"; revoked or changed approval removes accepted, posts the invalid-approval retry comment, and exits 1.docker://getsentry/craft@sha256:9a4a5d5efa44a00c2215078ead39800d4aaa5a97908b94f45a64d7d506d6e14b; it runs /bin/bash -e -c, sets HOME=/root, changes to __repo__/${path}, and executes craft publish ${version}.XDG_STATE_HOME=/github/workspace/.craft-state, CRAFT_MERGE_TARGET, CRAFT_LOG_LEVEL defaulting to Info, CRAFT_DRY_RUN, release-bot Git author/committer identity, and credential environment-variable names including GITHUB_TOKEN, DOCKER_GHCR_IO_USERNAME, DOCKER_GHCR_IO_PASSWORD, GITHUB_API_TOKEN, CLOUDFLARE_API_TOKEN, COCOAPODS_TRUNK_TOKEN, CRAFT_GCS_TARGET_CREDS_JSON, CRAFT_GCS_STORE_CREDS_JSON, CRATES_IO_TOKEN, DOCKER_USERNAME, DOCKER_PASSWORD, HEX_API_KEY, TWINE_USERNAME, TWINE_PASSWORD, TWINE_VERBOSE, NPM_TOKEN, GEM_HOST_API_KEY, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, NUGET_API_TOKEN, POWERSHELL_API_KEY, GPG_PRIVATE_KEY, GPG_PASSPHRASE, OSSRH_USERNAME, OSSRH_PASSWORD, PUBDEV_ACCESS_TOKEN, PUBDEV_REFRESH_TOKEN, VERCEL_ORG_ID, and VERCEL_TOKEN; secret values were referenced but are omitted.node .__publish__/src/publish/update-issue.js; cancellation runs node .__publish__/src/publish/post-result.js cancelled, failure runs node .__publish__/src/publish/post-result.js failure, and success runs node .__publish__/src/publish/post-result.js success.src/publish/record-ci-ready-attestation.js is 109 lines and imports createCiReadyAttestation and currentAcceptedEvent from ../modules/approval-attestation.js, plus getAllPages, getGitHubResponse, and getIssue from ./validate-approval-attestation.js.getAuthenticatedLogin() requests GitHub endpoint user, throws an error containing the HTTP status on a non-OK response, throws GitHub returned no authenticated user login unless login is a string, and otherwise returns the login.recordCiReadyAttestation() concurrently loads the live issue, all issue events, and the authenticated login; it derives the current accepted event with currentAcceptedEvent(events).recordCiReadyAttestation() throws The approval changed before CI could be marked ready if the issue is not open, its title changed, it lacks accepted, no accepted event exists, or an optional expectedAcceptedEvent differs by actor or event ID; otherwise it calls createCiReadyAttestation({ acceptedEvent, ciReadyActor, issue }).main() in src/publish/record-ci-ready-attestation.js requires GITHUB_OUTPUT, APPROVAL_TOKEN, APPROVAL_ISSUE_NUMBER, APPROVAL_ISSUE_REPOSITORY, and APPROVAL_ISSUE_TITLE; it uses EXPECTED_ACCEPTED_ACTOR and EXPECTED_ACCEPTED_EVENT_ID only when both exist, paginates the issue events resource through getAllPages(), and appends ci_ready_attestation=${attestation} to GITHUB_OUTPUT.src/publish/record-ci-ready-attestation.js runs main() only when invoked directly, logs caught errors, sets process.exitCode = 1, and exports getAuthenticatedLogin, main, and recordCiReadyAttestation.src/modules/approval-attestation.js is 259 lines and defines prefixes <!-- publish-approval , <!-- publish-auto-approval , and <!-- publish-ci-ready with suffix -->; attestations encode JSON using Base64URL.requestDigest({ body, labels, title }) rejects a non-string body or non-array labels, then computes a SHA-256 hex digest over JSON containing the exact body, title, and dryRun, where dryRun is true when a label named dry-run exists.createApprovalAttestation() records actor, stringified eventId, requestDigest, and title; createAutoApprovalAttestation() records autoApprover, requestDigest, and title; createCiReadyAttestation() records accepted actor, stringified accepted event ID, CI-ready actor, request digest, and title.parseAttestation() locates a prefix and suffix within a comment, Base64URL-decodes and JSON-parses the enclosed value, and returns null for missing markers or parsing errors; typed parsers reject values lacking their required string fields.compareEventIds(left, right) strips leading zeroes, normalizes an empty result to "0", rejects non-decimal IDs with Invalid issue event ID, and compares arbitrary-length decimal IDs first by normalized length and then lexicographically.currentLabeledEvent(events, labelName) filters valid labeled events for the named label having a string/number ID and actor login, selects the greatest ID with compareEventIds(), and returns { actor, eventId }; it returns null when no event exists or ID comparison fails. currentAcceptedEvent() and currentCiReadyEvent() specialize it for accepted and ci-ready.hasApprovalAttestation() requires a comment by the configured attestation author whose parsed approval actor, event ID, request digest, and title match the current event and issue.hasAutoApprovalAttestation() requires a comment by the configured attestation author whose parsed auto-approver, request digest, and title match.hasCiReadyAttestation() requires a comment by the configured attestation author whose parsed accepted actor/event ID match the accepted event, CI-ready actor matches the current ci-ready event actor, and request digest and title match the live issue.src/modules/approval-attestation.js exports compareEventIds, all 3 attestation creators, currentAcceptedEvent, currentCiReadyEvent, currentLabeledEvent, all 3 attestation-verification helpers, all 3 typed parsers, and requestDigest.No files found; the search criteria were not shown.src/publish/__tests__/record-ci-ready-attestation.js is 93 lines and contains exactly 3 recordCiReadyAttestation tests.publish: getsentry/sentry-javascript@10.0.0 issue with accepted event ID "100" by contractor produces a CI-ready attestation binding acceptedActor: "contractor", acceptedEventId: "100", and ciReadyActor: "sentry-internal-app[bot]".The approval changed before CI could be marked ready when the live issue title is publish: getsentry/sentry-python@10.0.0, the expected title is publish: getsentry/sentry-javascript@10.0.0, and no accepted event exists.{ actor: "contractor", eventId: "100" } but the current accepted event ID is "200".src/publish/__tests__/authorize-approval.js is 236 lines and contains exactly 7 authorization-entry-point tests; test setup restores process.env, mocks /tmp/github-output, uses APPROVAL_TOKEN="release-bot-token", and mocks global.fetch.getAutoApprovedRepositories() preserves exact release paths, parsing getsentry/sentry-javascript and getsentry/objectstore/clients into a Set.contractor with GitHub role_name: "write" for getsentry/sentry-javascript is authorized after the live open issue and accepted event ID "100" match; it expects the collaborators permission endpoint and output matching authorized=true followed by a <!-- publish-approval ... --> attestation.contractor cannot approve their own release and produces exactly authorized=false\n.publish: getsentry/relay@1.2.3, opened by getsantry[bot] and accepted by sentry-internal-app[bot], is authorized when a matching auto-approval attestation authored by github-actions[bot] exists; no collaborators endpoint is queried.authorized=false\n when no request-proof comment exists.sentry-release-bot[bot] is rejected without any GitHub fetch and produces exactly authorized=false\n.404 for contractorβs permission on getsentry/sentry-javascript: main() rejects with Could not retrieve contractor's permission for getsentry/sentry-javascript: GitHub returned 404, does not log through the mocked console.error inside the direct main() call, and writes nothing to GITHUB_OUTPUT.