Dashboard › publish › Distillation
9c83f9df-952c-4eb6-b7b9-d0aad869721c["lore_tm_v1_J1k2x-RN8tkgQX0xMIWOqKBfWD84z6jWvnu9XZBLkL8","lore_tm_v1_91LVR2H9vEYRFzdG3FG9zzaTM-BV0HbYUnFX3BVC8Wg","lore_tm_v1_-x-4tOtUmGnd1HdKVJ3rOimUzq66CpUnqJBQf6QAoH8","lore_tm_v1_7jQDi6mWtnEtpHiRk0b1fXSxzgiNkTiPBVT5hFF_QME","lore_tm_v1_j6OXApANe9LF_KLxTHbVQU4Vm6Qn4PhN1rFb23PlUmo","lore_tm_v1_Je3EFFZF-qpL5R0Q8QtO4FZ4IWD2u54Z7F6hESok4_Y","lore_tm_v1_zxrnFD-kV9nghU2iG3xW-E0SMX4ii9ks4PnHLGJ9_Ms","lore_tm_v1_S5tNfXclsaJinijn_am7ZclDC0DSgnJfMTjv1FTw4tE","lore_tm_v1_BuUIohZ8-Qod8SzzLsY3o47ADJN8Qo77Q3CeZvjN7CY"]
Date: Aug 27, 2026
src/modules/__tests__/details-from-context.js test fixture inputsArgs uses issue number: "223", title publish: getsentry/sentry@21.3.1, merge target custom-branch, labels ["accepted"], and Targets github, pypi, docker[release], npm[@sentry/opentelemetry], npm[@sentry/node], and docker[latest]; its expected parsed result is dry_run: "", merge_target: "custom-branch", path: ".", repo: "sentry", targets: ["github", "npm[@sentry/node]", "docker[latest]"], and version: "21.3.1".src/modules/__tests__/details-from-context.js verifies detailsFromContext() parses version 4.2.6+sentry1 from title publish: getsentry/sentry-forked-django-stubs@4.2.6+sentry1; verifies Merge target: (default) returns merge_target: ""; and verifies detailsFromContext({ context: {} }) rejects with Issue context is not defined..github/workflows/test.yml workflow Test runs on pushes to main and on pull_request, with contents: read permission and Bash default shell. Job unit-test runs on ubuntu-latest, checks out via actions/checkout@v6, sets up Node 24 via actions/setup-node@v6, caches node_modules using actions/cache@v4 key ${{ runner.os }}-node_modules-${{ hashFiles('package.json', 'yarn.lock') }}, runs yarn install --frozen-lockfile only on cache miss, then runs yarn test.bash: line 1: $(id >&2): syntax error: operand expected (error token is "$(id >&2)").auto-approve-repos.txt contains 46 repositories/paths: getsentry/arroyo, getsentry/auto-type-annotate, getsentry/devenv, getsentry/infra-event-notifier, getsentry/jest-sentry-environment, getsentry/json-schema-diff, getsentry/js-source-scopes, getsentry/objectstore/clients, getsentry/ophio, getsentry/pdb, getsentry/pyo3-python-tracing-subscriber, getsentry/pytest-sentry, getsentry/relay/py, getsentry/responses, getsentry/rust-proguard, getsentry/rust-sourcemap, getsentry/rust-usage-accountant, getsentry/script-runner, getsentry/sentry-api-schema, getsentry/sentry-forked-djangorestframework-stubs, getsentry/sentry-forked-django-stubs, getsentry/sentry-forked-jsonnet, getsentry/sentry-infra-tools, getsentry/sentry-kafka-management, getsentry/sentry-kafka-schemas, getsentry/sentry-protos, getsentry/sentry-redis-tools, getsentry/service-registry, getsentry/skrooge, getsentry/snuba-sdk, getsentry/statsdproxy, getsentry/status-page-list, getsentry/streams/sentry_streams, getsentry/symbolic, getsentry/taskbroker/clients, getsentry/usage-accountant, getsentry/watto, getsentry/sentry, getsentry/snuba, getsentry/vroom, getsentry/relay, getsentry/symbolicator, getsentry/taskbroker, getsentry/uptime-checker, getsentry/launchpad, and getsentry/self-hosted.No files found..github/workflows/ci-poller.yml, .github/workflows/publish.yml, AGENTS.md, src/libs/__tests__/github.js, src/libs/github.js, src/modules/details-from-context.js, src/modules/post-workflow-details.js, src/modules/process-end-state.js, src/modules/update-issue.js, src/publish/inputs.js, src/publish/post-result.js, src/publish/post-workflow-details.js, and src/publish/update-issue.js; untracked .lore.md, src/modules/__tests__/approval-attestation.js, src/modules/__tests__/approval-authorizer.js, src/modules/approval-attestation.js, src/modules/approval-authorizer.js, src/publish/__tests__/, src/publish/authorize-approval.js, and src/publish/validate-approval-attestation.js.ci-ready after either the latest accepted-label event or issue title changes..github/workflows/ci-poller.yml was changed to check out publish code with actions/checkout@v6 and persist-credentials: false, then validate approval attestations before promoting a ci-pending + accepted issue. It runs node src/publish/validate-approval-attestation.js with APPROVAL_TOKEN, APPROVAL_ATTESTATION_AUTHOR="github-actions[bot]", issue number/repository/title; on failure it warns, removes 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 that issue..github/workflows/publish.yml was changed to check out publish code with persist-credentials: false; create a release-bot app token with actions/create-github-app-token@v3, owner: getsentry, SENTRY_RELEASE_BOT_CLIENT_ID, and SENTRY_RELEASE_BOT_PRIVATE_KEY; run node src/publish/authorize-approval.js; record its approval_attestation as an issue comment when authorized; validate it using node src/publish/validate-approval-attestation.js; and reject invalid approvals by removing accepted, posting the retry comment, and exiting 1.publish.yml publish job also validates the approval attestation after checking out the publish repository at .__publish__; if validation fails, it removes accepted, posts the same invalid-approval retry comment, and exits 1.src/modules/details-from-context.js change promoted the publish-title parser to exported constant PUBLISH_TITLE_REGEX; detailsFromContext() now matches issue titles against this constant rather than an inline titleParser.src/modules/approval-attestation.js defines ATTESTATION_PREFIX = "<!-- publish-approval " and ATTESTATION_SUFFIX = " -->". createApprovalAttestation({ actor, eventId, title }) base64url-encodes JSON { actor, eventId: String(eventId), title } inside those markers; parseApprovalAttestation(body) locates, decodes, JSON-parses, and type-checks those three string fields, returning null for missing/malformed data.src/modules/approval-attestation.js, compareEventIds(left, right) strips leading zeroes, requires decimal-only normalized IDs, compares first by string length then localeCompare, and throws Error("Invalid issue event ID") for invalid IDs. currentAcceptedEvent(events) filters labeled events with label accepted, string/number id, and actor.login, selects the highest event ID, and returns { actor, eventId }; it returns null if no valid event or comparison fails.src/modules/approval-attestation.js, hasApprovalAttestation({ comments, event, title, attestationAuthor }) requires a comment authored by attestationAuthor whose parsed attestation exactly matches event.actor, event.eventId, and the current title. Module exports: compareEventIds, createApprovalAttestation, currentAcceptedEvent, hasApprovalAttestation, and parseApprovalAttestation.workflow_dispatch for manual recovery..github/workflows/ci-poller.yml self-dispatches itself when issues remain pending and token/remaining-count steps succeed. It increments ${{ github.event.inputs.attempt || '0' }}, caps self-dispatch at 60 attempts with warning Max self-dispatch attempts (60) reached. Relying on cron fallback., otherwise runs gh workflow run ci-poller.yml -R "$GITHUB_REPOSITORY" -f attempt="${attempt}"; the stated cap corresponds to approximately 30 minutes.ci-ready.publish.yml documents that the publish job fires only on ci-ready label events, not accepted, to avoid racing with waiting-for-CI handling on the same event; its gate requires the issue to contain accepted and the poller is responsible for adding ci-ready.