Dashboard › publish › Distillation
ac8021c5-004f-4467-ab1c-3b780fb1aa55["lore_tm_v1_n_bR6Bobe0L6xio_0y7rvQwRRAxWb9RjWefkadnApF0","lore_tm_v1_st23NdzKv1r_aQqzWDRVGlVGeqP0riZL3dM0nii6bLs","lore_tm_v1_eEAFH4-AGO1bUHgVqfSbrI72PEPNgAmyTK4UvNPPdHs","lore_tm_v1_LydhaUFn8qihjboMO9qMTm4qdGKFj-UrcpAZZUObTuQ","lore_tm_v1_gGbr_UGi47CV8I8-4NwuvfnAzw4Z-hpvOver6zcqe54","lore_tm_v1_Ic928xjyUMBvpVbLONdhweMjX6JPpWRJ1z1B0CHwHto","lore_tm_v1_95AWDt2RHIbUb-Px4YdW7kHKrJ0vfRtLSwOwlL2Qe98"]
π΄ (01:01) User-provided CI poller workflow pins actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803.
π΄ (01:01) User-provided publish workflow groups issues for the same repo@version into one concurrency group.
π΄ (01:01) User-provided publish workflow has a waiting-for-ci job and publishes only when the ci-ready label is present after the poller adds it.
π΄ (01:01) User stated the poller always adds ci-ready via a separate event, so publishing cannot run concurrently with waiting-for-ci on the same event.
π΄ (01:01) User-provided publish workflow installs dependencies with yarn install --cwd ".__publish__" and runs node .__publish__/src/publish/post-workflow-details.js.
π΄ (01:01) User-provided publish workflow includes special handling when fromJSON(steps.inputs.outputs.result).repo == 'sentry-migr8' and fromJSON(steps.inputs.outputs.result).merge_target == 'tmp-merge-target'.
π΄ (01:01) User-provided publish workflow hashes the container working directory seen by Craftβs Node process; its fallback container_cwd is /github/workspace/__repo__/${CRAFT_PUBLISH_PATH}.
π΄ (01:01) User-provided publish workflow uses docker://getsentry/craft@sha256:9a4a5d5efa44a00c2215078ead39800d4aaa5a97908b94f45a64d7d506d6e14b.
π΄ (01:01) User-provided publish workflow sets GIT_COMMITTER_NAME to sentry-release-bot[bot] and exposes publication credentials including DOCKER_GHCR_IO_PASSWORD, HEX_API_KEY, and GPG_PRIVATE_KEY through secrets without revealing their values.
π΄ (01:02) User-provided .github/workflows/ci-poller-dispatch.yml is 16 lines and defines Run CI Status Poller, triggered only by workflow_dispatch, with top-level permissions: {}.
π΄ (01:02) User-provided .github/workflows/ci-poller-dispatch.yml defines job dispatch, which runs on ubuntu-latest in the production environment and contains only the no-op step run: ":".
π΄ (01:02) User directive for .github/workflows/ci-poller-dispatch.yml: repository secrets must never be used by this workflow; protected production environment secrets ensure an arbitrary dispatched ref cannot add them.
π΄ (01:02) User stated the production environment permits deployments only from main.
π΄ (01:02) User-provided .github/workflows/cocoapods-keepalive.yml is 27 lines and defines CocoaPods Token Keep-Alive, scheduled by cron "0 0 * * *" every day at midnight UTC.
π΄ (01:02) User-provided .github/workflows/cocoapods-keepalive.yml notes CocoaPods sessions currently expire after 3 days of inactivity according to VALIDITY_LENGTH, referencing https://github.com/CocoaPods/trunk.cocoapods.org/blob/a1869790e9ae2229b6985b6af4532da814202558/app/models/session.rb#L9.
π΄ (01:02) User-provided .github/workflows/cocoapods-keepalive.yml grants contents: read; job keepalive / Refresh CocoaPods Session runs on macos-15 in the production environment.
π΄ (01:02) User-provided .github/workflows/cocoapods-keepalive.yml reads COCOAPODS_TRUNK_TOKEN from the correspondingly named secret, installs CocoaPods with gem install cocoapods, prints the version with pod --version, and refreshes the session with pod trunk me > /dev/null 2>&1.
π΄ (01:02) User-provided .github/workflows/test.yml is 34 lines and defines Test, triggered by pushes to main and all pull_request events, with contents: read and Bash as the default run shell.
π΄ (01:02) User-provided .github/workflows/test.yml defines job unit-test / unit tests on ubuntu-latest, pins actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803, and pins actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 with Node.js 24.
π΄ (01:02) User-provided .github/workflows/test.yml pins actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830, caches node_modules, and uses key ${{ runner.os }}-node_modules-${{ hashFiles('package.json', 'yarn.lock') }}.
π΄ (01:02) User-provided .github/workflows/test.yml runs yarn install --frozen-lockfile only when steps.cache.outputs.cache-hit != 'true', then runs yarn test.
π΄ (01:02) User-provided src/modules/approval-attestation.js is 259 lines and defines attestation prefixes <!-- publish-approval , <!-- publish-auto-approval , and <!-- publish-ci-ready , with suffix -->.
π΄ (01:02) User-provided src/modules/approval-attestation.js function createAttestation(prefix, value) serializes value as JSON and encodes it using base64url.
π΄ (01:02) User-provided src/modules/approval-attestation.js function requestDigest({ body, labels, title }) rejects non-string bodies or non-array labels with Invalid publish request, then computes a SHA-256 hex digest over JSON containing body, title, and dryRun, where dryRun is true when a label is named dry-run.
π΄ (01:02) User-provided src/modules/approval-attestation.js creates approval attestations containing 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.
π΄ (01:02) User-provided src/modules/approval-attestation.js function parseAttestation(body, prefix) finds the first matching prefix and --> suffix, decodes the intervening base64url JSON as UTF-8, and returns null for missing markers or parsing failures.
π΄ (01:02) User-provided src/modules/approval-attestation.js validates parsed approval, auto-approval, and CI-ready attestations by requiring all corresponding fields to be strings; invalid structures return null.
π΄ (01:02) User-provided src/modules/approval-attestation.js function compareEventIds(left, right) strips leading zeroes, treats an empty result as "0", rejects non-decimal IDs with Invalid issue event ID, compares differing lengths numerically by length, and otherwise uses localeCompare.
π΄ (01:02) User-provided src/modules/approval-attestation.js function currentLabeledEvent(events, labelName) filters for matching labeled events with string-or-number IDs and an actor login, selects the greatest event ID via compareEventIds, and returns { actor, eventId }; it returns null when no valid event exists or comparison throws.
π΄ (01:02) User-provided src/modules/approval-attestation.js functions currentAcceptedEvent(events) and currentCiReadyEvent(events) call currentLabeledEvent for labels accepted and ci-ready, respectively.
π΄ (01:02) User-provided src/modules/approval-attestation.js function hasApprovalAttestation({ comments, event, issue, attestationAuthor }) requires a comment authored by attestationAuthor whose parsed attestation exactly matches the approval actor, event ID, current request digest, and issue title.
π΄ (01:02) User-provided src/modules/approval-attestation.js function hasAutoApprovalAttestation({ autoApprover, attestationAuthor, comments, issue }) requires a comment by attestationAuthor matching autoApprover, the current request digest, and the issue title.
π΄ (01:02) User-provided src/modules/approval-attestation.js function hasCiReadyAttestation({ comments, acceptedEvent, ciReadyEvent, issue, attestationAuthor }) requires a comment by attestationAuthor matching the accepted actor and event ID, CI-ready actor, current request digest, and issue title.
π΄ (01:02) User-provided src/modules/approval-attestation.js exports compareEventIds, createApprovalAttestation, createAutoApprovalAttestation, createCiReadyAttestation, currentAcceptedEvent, currentCiReadyEvent, currentLabeledEvent, hasApprovalAttestation, hasAutoApprovalAttestation, hasCiReadyAttestation, parseApprovalAttestation, parseAutoApprovalAttestation, parseCiReadyAttestation, and requestDigest.
π΄ (01:02) User-provided src/modules/approval-authorizer.js is 57 lines; ALLOWED_ROLE_NAMES contains exactly write, maintain, and admin.
π΄ (01:02) User-provided src/modules/approval-authorizer.js defines AUTO_APPROVAL_LABELER = "sentry-internal-app[bot]" and AUTO_APPROVERS containing getsantry[bot] and sentry-release-bot[bot].
π΄ (01:02) User-provided src/modules/approval-authorizer.js function isAutoApprovedRepository({ actor, autoApprovedRepositories, issueTitle }) requires the actor to be in AUTO_APPROVERS, requires parsePublishTitle(issueTitle) to succeed, and checks for exact membership of getsentry/${title.repo}${title.path || ""}.
π΄ (01:02) User-provided src/modules/approval-authorizer.js function authorizeApproval({ actor, issueTitle, getPermission }) returns { authorized: false, repository: null } for an invalid publish title.
π΄ (01:02) User-provided src/modules/approval-authorizer.js function authorizeApproval derives repository getsentry/${title.repo} and always rejects actors in AUTO_APPROVERS and actor sentry-internal-app[bot].
π΄ (01:02) User-provided src/modules/approval-authorizer.js authorizes other actors only when getPermission({ owner: "getsentry", repository: title.repo, username: actor }) returns a role_name of write, maintain, or admin.
π΄ (01:02) User-provided src/modules/approval-authorizer.js exports AUTO_APPROVAL_LABELER, AUTO_APPROVERS, authorizeApproval, and isAutoApprovedRepository.