Dashboard › publish › Distillation
7def4762-23bb-4c3d-84e5-5b252238aa31["lore_tm_v1_Dpo7JJ1nGlEM1VAAiTWG5mIAZCcsFvxGOdQdIWPiKfY","lore_tm_v1_NpGNgFuObXmdJjJJ3tl2dvJc5uZvIGGMmUywL5ZmSAA","lore_tm_v1_4JkDkf9XsjiNZ25mcX0_XWsSBmDNwd6Mx1EnrioHZxU","lore_tm_v1_aiM-vT9gFfi16LLL90T2X0gh9emgOcmCz9y7euCseqE","lore_tm_v1_7E6SiQ2m1JoyohAsKj8f1iZjGX52ZZBwNSuv3fW0HKM","lore_tm_v1_NVU2KmBxlDPWP4zK89z-JWwtOnOeSoQZmRK-ZjzTF-E","lore_tm_v1_ldiLZ3R6vz94Y4QBYiGmpr_AbO1L7OKsEiRgmtQNTwM","lore_tm_v1_rglqWodumr9IlweCYo1bN85no0vp2RtZ2kPKG8Y9HZ4","lore_tm_v1_dZ6sLIbmkn1ESnqHXqb_PSNMmDvyKiJFJwtkbeUgakg","lore_tm_v1_ZzaIpGl6e-a55HSlKrrQCJmTCah8GvyIvs_HDBym44M","lore_tm_v1_Vr9K6iuZBHJz-DT6BuJpWapQNLwvwanqPTWEpMuUVx8","lore_tm_v1_d10iAQ2G2KJjbL0l9fzTSdusKge2g5MFhbfmiWjCRzc","lore_tm_v1_CcD2i1CqVOp56y9prfSMH2hyFj5eKK3_F3PNMY84nyA","lore_tm_v1_QZQVzxgIRroL-o3q_eFTSFGmBwhJK-oV3WRuFyrq2qo","lore_tm_v1_Z2qVwduxy0lATM_bju1LkS9UGTKvIkBJSlpKH9sdp68","lore_tm_v1_sBmEd8wQXS0CAY_kaP4qFdXWiK9dFgPt-a-NDvzqnkc","lore_tm_v1_4H2R_8WhVWYUWvrGO0-PjlbqCiZWkkd4inw5ACj8yc4","lore_tm_v1_uLR8KlMFKNCd69gC3cKQ31E2Y8h9SsEHVK3F-t5azHQ"]
Date: Aug 28, 2026
src/modules/approval-attestation.js defines approval attestations as <!-- publish-approval ${base64url(JSON.stringify({ actor, eventId: String(eventId), title }))} -->; parseApprovalAttestation() returns null for missing, malformed, undecodable, or invalid non-string actor/eventId/title fields.src/modules/approval-attestation.js selects the latest "accepted" label event via currentAcceptedEvent(events), requiring event === "labeled", event.label?.name === "accepted", a string/number event ID, and event.actor?.login; it compares arbitrarily large numeric IDs safely after stripping leading zeroes, throws "Invalid issue event ID" for nonnumeric IDs, and returns null on invalid/no qualifying events.hasApprovalAttestation() in src/modules/approval-attestation.js requires a comment authored by the trusted attestationAuthor whose parsed attestation exactly matches the current accepted-label event’s actor and event ID plus the current issue title.src/modules/approval-authorizer.js authorizes only target-repository collaborator roles write, maintain, or admin; it parses publish titles with parsePublishTitle(), queries getsentry/{title.repo} collaborator permission, and rejects malformed titles without a GitHub permission lookup.src/modules/approval-authorizer.js treats getsantry[bot] as AUTO_APPROVER: it is authorized only if the exact release path (getsentry/{repo} plus optional package path) appears in autoApprovedRepositories; it never falls back to a collaborator-permission lookup for that bot.src/publish/authorize-approval.js reads exact nonblank allowlist entries from auto-approve-repos.txt, uses APPROVAL_TOKEN to call GitHub API version "2026-03-10", and paginates issue events at per_page=100 until a page contains fewer than 100 records.src/publish/authorize-approval.js requires GITHUB_OUTPUT, APPROVAL_TOKEN, APPROVAL_ISSUE_NUMBER, and APPROVAL_ISSUE_REPOSITORY; after authorization, it concurrently re-fetches the publish issue and its events, fails closed if the title differs from APPROVAL_ISSUE_TITLE, no accepted event exists, or the latest accepted-event actor differs from APPROVAL_ACTOR; otherwise it writes authorized=true and approval_attestation=<attestation> to GITHUB_OUTPUT.src/publish/validate-approval-attestation.js concurrently fetches an issue, all issue events, and all comments (each paginated at per_page=100); approval is valid only when the fetched title equals the expected title, the issue currently has accepted, a latest accepted event exists, and a trusted-author attestation exactly matches that event and title. Its CLI requires APPROVAL_TOKEN, APPROVAL_ISSUE_NUMBER, APPROVAL_ISSUE_REPOSITORY, APPROVAL_ISSUE_TITLE, and APPROVAL_ATTESTATION_AUTHOR, throwing "The current accepted label has no matching approval attestation" when invalid..github/workflows/ contains exactly 5 workflows: auto-approve.yml, ci-poller.yml, cocoapods-keepalive.yml, publish.yml, and test.yml..github/workflows/auto-approve.yml workflow auto-approve non-sdks runs on newly opened issues only when the actor is sentry-release-bot[bot] or getsantry[bot] and the title starts with publish: ; it checks out only auto-approve-repos.txt, creates an app token with actions/create-github-app-token@v3, extracts the release path using sed -n 's/^publish: \(.*\)@.*/\1/p', and applies the accepted label only for an exact allowlist match.src/modules/__tests__/approval-attestation.js cover trusted attestations for the latest accepted event, GitHub numeric event ID 29503999078, rejection when the comment author is untrusted, rejection when event ID/title are stale, and rejection of malformed <!-- publish-approval not-base64 -->.src/modules/__tests__/approval-authorizer.js verify that getsantry[bot] bypasses target-repository lookup only for an allowlisted exact release path; human collaborators with write, maintain, or admin are authorized, while none, read, triage, "Elevated Bot", and undefined are rejected; unqualified titles such as publish: sentry-javascript/packages/core@10.0.0 authorize against getsentry/sentry-javascript; malformed titles make no GitHub query; permission lookup errors propagate.src/publish/__tests__/authorize-approval.js verifies allowlist loading preserves exact paths including getsentry/sentry-javascript and getsentry/objectstore/clients; a successful write permission lookup writes an attestation; a GitHub permission 404 causes main() to throw Could not retrieve contractor's permission for getsentry/sentry-javascript: GitHub returned 404 and writes no output.src/publish/__tests__/validate-approval-attestation.js verifies acceptance of a trusted current attestation and rejection after an issue title change, after accepted is removed, and after accepted is re-added with a newer event ID.package.json identifies the private publish package version 0.0.1 as an “Approval-based publishing system for Sentry”; it specifies Volta Node 24.0.0 and Yarn 1.22.22, uses vitest run for tests, eslint src .github --ignore-pattern '!.github' for linting, and pins undici to ^6.23.0.yarn / vitest run succeeded: 9 test files and 41 tests passed, duration 1.83s (tests 253ms). Passing files included approval attestation (3 tests), approval authorizer (14), validation (4), authorization entry point (3), update issue (5), process end state (4), details from context (5), post-workflow details (1), and GitHub library (2).eslint src .github --ignore-pattern '!.github' failed with 1 error and 0 warnings: .github/workflows/cocoapods-keepalive.yml:1:7 violates yml/plain-scalar (“Must use plain style scalar”); ESLint reported it may be fixable using --fix.auto-approve-repos.txt contains 46 exact auto-approved release 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.README.md release flow is: 1. developer triggers release workflow; 2. SDK repo runs craft prepare; 3. build artifacts and create release branch; 4. upload artifacts to GitHub; 5. create a getsentry/publish issue; 6. release manager reviews and adds accepted; 7. publish workflow downloads artifacts; 8. craft publish publishes to registries; 9. success closes the issue, while failure updates it.README.md goals are to prevent employee publishing through personal accounts, prevent employee access to global credentials, prevent building/publishing releases from developer machines, require formal approvals from a limited set of release managers, and still allow any engineer to initiate a release.README.md documents that customer-installed packages such as sentry-cli, SDKs, and symbolicator require explicit approval; internal dependencies such as arroyo can use auto approval because their use in Sentry proper requires another explicit approval, though independent external adoption may warrant reconsideration due to attacker interest.README.md states the publish repository is read-only except for release managers because repository write access could expose its admin-level GitHub and package-registry publishing secrets through arbitrary workflows; it uses short-lived Sentry Release Bot GitHub App tokens, rather than GITHUB_TOKEN, because GitHub prevents that token from triggering further workflows.docs/rfc.md identifies central-publish requirements: rapidly recycle publishing tokens without disrupting releases/developer flow; limit tokens to authorized personnel and prevent leaks; obtain manager or senior-engineer SDK-release approval for SOC/ISO compliance; and avoid divergent per-project release/publish configurations.docs/rfc.md proposes: 1. a central release/publish repository holding workflows and publishing tokens; 2. authorized personnel with write/admin/triage access; 3. all other engineers with read access; 4. structured release-request issues; 5. per-repository release actions running craft prepare and opening requests containing repository name and version; 6. release-repo owners add accepted to trigger release; 7. successful releases close the issue; 8. OSS CalVer releases auto-approve unless blocked.docs/rfc.md notes issue-based releases provide an audit paper trail and reduce friction because GitHub manual workflow-trigger URLs cannot prefill input values.