Dashboard › publish › Distillation
6271a1b1-65a9-4e07-9c88-aa7983269d30["lore_tm_v1_Ll5_Rv9v-2PkS_WMUpH-njGzTt4A2QQw3rA5o8DuyRE","lore_tm_v1_QyywERZ0Ye4X4jSaO0xivrkvu_EO3Fio7dUFzGaqKfg","lore_tm_v1_Zo8BTtzNDLm3mUK4hNX-nOlD1_5cO9LX31lGbIyWNN8","lore_tm_v1_avBtTK0LkJhHLU71iBngLkWBhm6kRZaPtArqKnSeCio","lore_tm_v1_-eEmBZIk-Qn8a7ki3bPDGDIgg25kyzwMwIc_2pYiFEc"]
π΄ (10:18) User requires workflow_dispatch always remain available for manual recovery.
π΄ (10:18) User requires manually dispatched workflows to always run trusted code from the repository default branch rather than code from an arbitrary dispatch ref.
π΄ (10:18) User requires that a release never move to ci-ready after its issue or approval changes.
π΄ (10:18) User requires a renamed or re-approved issue never reach ci-ready.
π΄ (10:18) GitHub App permissions grep returned exactly 29 matching UAT endpoints: GET /user/memberships/orgs/{org} (read, UAT, no additional permission); GET /user/repository_invitations (read, UAT, no additional permission); GET /user/codespaces/{codespace_name}/exports/{export_id} (read, UAT, no additional permission); GET /user/codespaces/{codespace_name}/machines (read, UAT, no additional permission); GET /user/codespaces (read, UAT, no additional permission); GET /user/codespaces/{codespace_name} (read, UAT, no additional permission); GET /user/installations/{installation_id}/repositories (read, UAT, no additional permission); GET /user/repos (read, UAT, no additional permission); GET /user/blocks (read, UAT, no additional permission); GET /user/blocks/{username} (read, UAT, no additional permission); GET /user/codespaces/secrets (read, UAT, no additional permission); GET /user/codespaces/secrets/public-key (read, UAT, no additional permission); GET /user/codespaces/secrets/{secret_name} (read, UAT, no additional permission); GET /user/codespaces/secrets/{secret_name}/repositories (read, UAT, no additional permission); GET /user/emails (read, UAT, no additional permission); GET /user/public_emails (read, UAT, no additional permission); GET /user/followers (read, UAT, no additional permission); GET /user/following (read, UAT, no additional permission); GET /user/following/{username} (read, UAT, no additional permission); GET /user/gpg_keys (read, UAT, no additional permission); GET /user/gpg_keys/{gpg_key_id} (read, UAT, no additional permission); GET /user/keys (read, UAT, no additional permission); GET /user/keys/{key_id} (read, UAT, no additional permission); GET /user/interaction-limits (read, UAT, no additional permission); GET /user/ssh_signing_keys (read, UAT, no additional permission); GET /user/ssh_signing_keys/{ssh_signing_key_id} (read, UAT, no additional permission); GET /user/starred (read, UAT, no additional permission); GET /user/starred/{owner}/{repo} (read, UAT, additional permission indicated); and GET /user/subscriptions (read, UAT, no additional permission).
π΄ (10:18) .github/workflows/ci-poller.yml was changed from a direct workflow_dispatch trigger with an attempt input to repository_dispatch type ci-poller plus workflow_run completion events for workflow Run CI Status Poller; scheduled execution remains cron: "*/5 * * * *".
π΄ (10:18) .github/workflows/ci-poller.yml changed top-level permissions from contents: read and issues: read to contents: read and issues: write.
π΄ (10:18) The check-ci job gate in .github/workflows/ci-poller.yml now runs when vars.CI_POLLER_HAS_PENDING == 'true', on repository_dispatch, or after a successful workflow_run whose head_branch equals github.event.repository.default_branch. The protected workflow-dispatch relay and repository_dispatch are the trusted manual-recovery paths.
π΄ (10:18) .github/workflows/ci-poller.yml added step Validate dispatch attempt (id: dispatch-attempt) for repository_dispatch. It reads github.event.client_payload.attempt || '0', accepts only regex ^(0|[1-5][0-9])$ (integers 0 through 59), emits value=$ATTEMPT to $GITHUB_OUTPUT, and fails with repository_dispatch attempt must be an integer from 0 through 59 otherwise.
π΄ (10:18) .github/workflows/ci-poller.yml added Get publish code using actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803, with ref: ${{ github.event.repository.default_branch }} and persist-credentials: false, ensuring trusted default-branch code is used.
π΄ (10:18) All shown actions/create-github-app-token uses in .github/workflows/ci-poller.yml were pinned from mutable @v3 to commit @bcd2ba49218906704ab6c1aa796996da409d3eb1, including Get auth token, Get release bot auth token, and Get poller app token.
π΄ (10:18) The CI poller now binds each cycle to the current accepted-label event by running src/publish/current-accepted-event.js with APPROVAL_TOKEN, APPROVAL_ISSUE_NUMBER, and APPROVAL_ISSUE_REPOSITORY, then extracts .actor as accepted_actor and .eventId as accepted_event_id.
π΄ (10:18) If src/publish/current-accepted-event.js cannot bind the approval event, .github/workflows/ci-poller.yml removes both ci-pending and accepted, comments Approval is invalid or could not be verified. Re-add the accepted label to retry after resolving the issue., and skips the issue.
π΄ (10:18) Before inspecting CI, the poller validates approval through src/publish/validate-approval-attestation.js using APPROVAL_ATTESTATION_AUTHOR="github-actions[bot]", the issue number/repository/title, and EXPECTED_ACCEPTED_ACTOR plus EXPECTED_ACCEPTED_EVENT_ID. Invalid approval removes ci-pending and accepted, posts the retry comment, and skips the issue.
π΄ (10:18) CI poller title parsing was changed from sed expressions to Publishβs shared parsePublishTitle() from ./src/modules/details-from-context.js. It sets repo to "getsentry/" + .repo and reads .version, ensuring CI and publishing target the same repository and version.
π΄ (10:18) After all statuses and checks pass, .github/workflows/ci-poller.yml re-runs src/publish/validate-approval-attestation.js; if the approval changed during CI, it removes ci-pending and accepted, posts the retry comment, and prevents ci-ready.
π΄ (10:18) Before adding ci-ready, the poller runs src/publish/record-ci-ready-attestation.js against the still-current accepted actor/event and captures its $GITHUB_OUTPUT via mktemp. Failure to create proof removes ci-pending and accepted, comments with retry guidance, and skips the release.
π΄ (10:18) The CI-ready proof output must begin exactly with ci_ready_attestation=. An invalid output removes ci-pending and accepted, posts the retry comment, and skips the release.
π΄ (10:18) The poller records the generated CI-ready attestation as an issue comment using GH_TOKEN="${{ github.token }}", so the proof author is github-actions[bot]. If comment creation fails, it removes ci-pending and accepted, posts the retry comment, and prevents ci-ready.
π΄ (10:18) After recording the CI-ready proof, the poller validates src/publish/validate-approval-attestation.js once more immediately before moving the release forward. If approval changed before ci-ready, it removes ci-pending and accepted, posts retry guidance, and skips the release.
π΄ (10:18) Only after successful CI and all approval/attestation checks does .github/workflows/ci-poller.yml remove ci-pending and add ci-ready.
π΄ (10:18) The CI poller self-dispatch mechanism was changed from gh workflow run ci-poller.yml -f attempt="${attempt}" to a repository dispatch API call: jq -n --arg attempt "$attempt" '{event_type: "ci-poller", client_payload: {attempt: $attempt}}' | gh api --method POST "repos/$GITHUB_REPOSITORY/dispatches" --input -.
π΄ (10:18) Self-dispatch now reads ATTEMPT from ${{ github.event_name == 'repository_dispatch' && steps.dispatch-attempt.outputs.value || '0' }} and increments it with attempt=$((10#$ATTEMPT + 1)), explicitly forcing base-10 interpretation.
π΄ (10:18) The self-dispatch chain remains capped at 60 attempts (approximately 30 min at roughly 30β60s between checks); once attempt >= 60, it emits Max self-dispatch attempts (60) reached. Relying on cron fallback. The */5 cron fallback remains because GitHub cron can drift to 30β40 min under load.
π΄ (10:18) The pollerβs pending-state comments and logic now describe repository_dispatch bypassing the variable gate and restarting through an accepted-label manual trigger or workflow_dispatch relay, replacing references to direct workflow_dispatch.
π΄ (10:19) yarn vitest run --printConsoleTrace completed successfully under Vitest v4.0.17 in /home/byk/Code/getsentry/publish: exactly 16 test files passed and exactly 84 tests passed, with duration 2.48s (transform 271ms, setup 0ms, import 1.50s, tests 406ms, environment 3ms); Yarn completed in 3.09s.
π΄ (10:19) Passing test files and exact counts were: src/modules/__tests__/approval-authorizer.js β 15; src/modules/__tests__/approval-attestation.js β 5; src/publish/__tests__/validate-approval-attestation.js β 9; src/modules/__tests__/post-workflow-details.js β 1; src/modules/__tests__/update-issue.js β 6; src/publish/__tests__/current-accepted-event.js β 2; src/modules/__tests__/process-end-state.js β 4; src/publish/__tests__/authorize-approval.js β 7; src/publish/__tests__/record-auto-approval-attestation.js β 2; src/modules/__tests__/details-from-context.js β 5; src/publish/__tests__/record-ci-ready-attestation.js β 3; src/publish/__tests__/workflow-action-pinning.js β 16; src/publish/__tests__/ci-poller-workflow.js β 3; src/libs/__tests__/github.js β 2; src/publish/__tests__/publish-workflow.js β 2; and src/publish/__tests__/auto-approval-workflow.js β 2.
π΄ (10:19) yarn eslint src .github --ignore-pattern '!.github' completed successfully with no reported lint errors in 1.02s.