Dashboard › publish › Distillation
f2e217be-ded9-4de5-88e7-b883652d38b9["lore_tm_v1_12LdK2hxOFOM7dz1cPNMdwjVRLbdOkpjvY8sRn_fPGQ","lore_tm_v1_KePUxLPxeK-lJg-4N4rTbMaTWGdKi0Amp9vfYhzTx4k","lore_tm_v1_pDdfFd3rrNA-evMqS1WhQMnnbHHRYg04dcDFKap62NE","lore_tm_v1_ZFPtzhEIyCXEOTrKpSGynAREbz5rk5V20Nndm6xmQBA","lore_tm_v1_MLpNNAXuFmZgNBk-JSaQ8joLKmfF9lQGnSJYFl5WXQM","lore_tm_v1_F8YMeUSDksABAuWzCHU8JOh1gKtVto0oVIC0vxeTMjA","lore_tm_v1_s7UXFhzOk0Bg-CQ4uO9P-iqIS4zrj8bDfJM09goztgc","lore_tm_v1_Xusu7FdZjnBiHKmyAKgrdxmZck_PMg3O7UhppvXoKS4","lore_tm_v1_V4vjfe_NO6eMcss4-RRsFYqSvOm_D1OsNjIqwaDwUFg","lore_tm_v1_Z8ljJunpbH577hYMGkThUj-FtpoUFbWQ_Du7JAsGG5A","lore_tm_v1_aNUIiu-eG92nB1ljaM2vl_vh0zUgrCn_DQqWyItSbg8","lore_tm_v1_hVrPOp4vp82OLmfbrnWUbQUfM7K6TC3JenAftjDVJlc","lore_tm_v1_M1udjEWbhXqLn0RpUSBoxkDjRiQy3nQVE77NhShQKwA","lore_tm_v1_aJg1dMlyzpwKsdqM70p3Vj2XP4RMxqjaKNizFjxNgok","lore_tm_v1_Ag-hl73meqO6Bes3hXWUImy4rnUgbP8swhf1EQXz0f8","lore_tm_v1_7z3vd3iLIF-pPnp3os20fZBjy1kLiMFWVRNgPYiZ-yI","lore_tm_v1_bDNXWp3MDhlR4Xg3Sh-lYrstKGbl9oPTMfSeSAgydAo","lore_tm_v1_Z7uxmHLGf-2ltLaehu2lmzm8oqHm4EjDVPP6hqpBUj0","lore_tm_v1_qWu7r7iMDWYmKJMgANGVtCleblHve21K8E2zb5yMxqc","lore_tm_v1_tHFNffl9iNNIy0nAkimIQd7UHyyk42vA9ABa2jOACWE","lore_tm_v1_k_eQkUggyaVGqr3P_Fb8WoEQa22WIF42719q_XRmKJw","lore_tm_v1_L5pjDVc1gcpNmQ-_GXs0bldZPIqWjkvCw2cFo2pHhwc","lore_tm_v1_TP6gP6FNR8XzDgmpZbYshLW3CeHkIoRJhff06GAYhvg"]
Date: Sep 10, 2026
/home/byk/Code/getsentry/publish/.github/workflows, containing exactly 6 workflows: auto-approve.yml, ci-poller-dispatch.yml, ci-poller.yml, cocoapods-keepalive.yml, publish.yml, and test.yml./home/byk/Code/getsentry/publish/.github/workflows/test.yml; it runs on pushes to main and pull requests with contents: read, uses ubuntu-latest, Bash, Node 24, actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803, actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38, and actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830. It caches node_modules under ${{ runner.os }}-node_modules-${{ hashFiles('package.json', 'yarn.lock') }}, runs yarn install --frozen-lockfile only on a cache miss, then runs yarn test./home/byk/Code/getsentry/publish/.github/workflows/cocoapods-keepalive.yml; the keepalive job runs on macos-15 in production every day at midnight UTC via cron "0 0 * * *", because CocoaPods sessions expire after 3 days of inactivity (VALIDITY_LENGTH). It exposes COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }}, runs gem install cocoapods, prints pod --version, then refreshes the session with pod trunk me > /dev/null 2>&1./home/byk/Code/getsentry/publish/src/publish/, 14 tests under src/publish/__tests__/, 12 modules under src/modules/, 11 tests under src/modules/__tests__/, plus src/libs/github.js and src/libs/__tests__/github.js./home/byk/Code/getsentry/publish/src/modules/approval-attestation.js; attestation comments use prefixes <!-- publish-approval , <!-- publish-auto-approval , and <!-- publish-ci-ready with suffix -->, encoding JSON as base64url.src/modules/approval-attestation.js, requestDigest({ body, labels, title }) validates a string body, array labels, and string title, then computes a hexadecimal SHA-256 over JSON.stringify({ body, dryRun, title }), where dryRun is true when a label is named "dry-run"; invalid input throws "Invalid publish request".src/modules/approval-attestation.js, approval attestations bind actor, stringified eventId, requestDigest, and title; auto-approval attestations bind autoApprover, requestDigest, and title; CI-ready attestations bind acceptedActor, stringified acceptedEventId, ciReadyActor, requestDigest, and title. Parsing returns null for missing delimiters, malformed base64url/JSON, or incorrectly typed required fields.src/modules/approval-attestation.js, compareEventIds(left, right) strips leading zeroes, validates decimal-only IDs, compares first by normalized length and then lexicographically, and throws "Invalid issue event ID" for invalid IDs. currentLabeledEvent(events, labelName) considers "labeled" and "unlabeled" events, chooses the greatest event ID rather than array order, and returns { actor, eventId } only if the latest event is a valid "labeled" event; currentAcceptedEvent() and currentCiReadyEvent() specialize it for "accepted" and "ci-ready".src/modules/approval-attestation.js, hasApprovalAttestation(), hasAutoApprovalAttestation(), and hasCiReadyAttestation() accept only comments by the configured attestationAuthor and require exact matches against the live request digest/title and their respective actor/event bindings./home/byk/Code/getsentry/publish/src/modules/approval-authorizer.js; allowed manual approver roles are "write", "maintain", and "admin". AUTO_APPROVAL_LABELER is "sentry-internal-app[bot]", while AUTO_APPROVERS contains "getsantry[bot]" and "sentry-release-bot[bot]".src/modules/approval-authorizer.js, isAutoApprovedRepository() requires an actor in AUTO_APPROVERS, a valid parsed publish title, and an exact getsentry/${title.repo}${title.path || ""} entry in autoApprovedRepositories. authorizeApproval() rejects invalid titles, auto-approvers, and AUTO_APPROVAL_LABELER; otherwise it retrieves the actorβs permission on getsentry/${title.repo} and authorizes only an allowed role_name./home/byk/Code/getsentry/publish/src/modules/ci-poller-input.js; getCiPollerInput({ title, issueBody, labels, revision }) parses and validates the title repository/version/path, reads the current release revision, optionally rewrites the issue body with updateReleaseRevision(), and returns repo: "getsentry/${repo}", a digest of the resolved body, revision: currentRevision, and version, plus issueBody: resolvedIssueBody only when a replacement revision was supplied.getCiPollerInput() errors are specific: malformed title β Invalid publish issue title: '${title}'; invalid repository β Invalid publish issue repository: '${repo}'; invalid version β Invalid publish issue version: '${version}'; invalid path β Invalid publish issue path: '${path}'./home/byk/Code/getsentry/publish/src/modules/details-from-context.js; TARGETS_SECTION_PARSER_REGEX matches a contiguous checklist section, TARGETS_PARSER_REGEX captures all target IDs case-insensitively, and CHECKED_TARGETS_PARSER_REGEX captures checked targets.src/modules/details-from-context.js, parsePublishTitle() returns null if parsing or repository/version/path validation fails. detailsFromContext() throws "Issue context is not defined" without an issue payload, otherwise returns parsed title fields plus dry_run ("1" when labeled "dry-run", else ""), merge_target (empty when the issue says "(default)"), normalized path, and checked targets when present./home/byk/Code/getsentry/publish/src/modules/process-end-state.js; processEndState() validates status before any terminal transition, closes the issue only for "success", fetches workflow-run details, posts a result comment, and reports a Sentry session.src/modules/process-end-state.js, failure and cancellation comments link to ${workflowInfo.html_url}?check_suite_focus=true#step:8 and include a branch-deletion link for the encoded version; success posts Published successfully: [run#${run_id}](${workflowInfo.html_url}). Unknown statuses throw Unknown status: '${status}'.src/modules/process-end-state.js, Sentry initializes with SENTRY_DSN and release ${repo}@${version}, tags "repository", attaches release context containing issue_number and inputs, and maps outcomes as: failure β message Release failed: ${repo}, severity "error", session "crashed"; cancelled β Release cancelled: ${repo}, severity "warn", session "crashed"; success β Release succeeded: ${repo}, severity "info", session "ok"./home/byk/Code/getsentry/publish/src/modules/update-issue.js; updateTargets() is a no-op if stateFilePath is absent or nonexistent, otherwise concurrently fetches the issue and parses the Craft state JSON, updates listed target checkboxes from craftState.published, appends previously unlisted targets, and writes the transformed body.src/modules/update-issue.js, updateIssue() concurrently updates targets from CRAFT_STATE_FILE_PATH and removes the "accepted" label using Promise.allSettled(). A label-removal HTTP 404 is ignored; other label failures and all target-update failures are rethrown./home/byk/Code/getsentry/publish/src/modules/post-workflow-details.js; postWorkflowDetails() fetches the current workflow run and posts Publishing: [run#${run_id}](${workflowInfo.html_url}) to the publish issue./home/byk/Code/getsentry/publish/src/modules/publish-location.js; resolvePublishLocation() rejects invalid paths with "Invalid publish path." and invalid workspace lists with "Craft workspace discovery returned an invalid workspace list.". "." remains { path: "." }; a matching ./workspace becomes { path: ".", workspace }; a nonmatching safe path remains { path }.src/modules/publish-location.js, safe paths are "." or "./" followed by slash-separated safe workspace segments. Each segment must match /^[A-Za-z0-9_.-]+$/, must not equal ".", "..", or "__proto__", and must not start with "-"./home/byk/Code/getsentry/publish/src/modules/release-revision.js; valid release revisions match /^[0-9a-f]{40}$/. getReleaseRevisionDetails() requires exactly 1 CheckRunsLinkCount, parses ReleaseRevision, and requires the parsed repository to equal the expected repository.src/modules/release-revision.js, a non-singleton check-runs link count throws Expected exactly one View check runs link in Quick links for getsentry/${repo}.; a missing/mismatched link throws Expected a View check runs link for getsentry/${repo} in the publish issue body.; an invalid replacement revision throws "Release revision must be a lowercase 40-character SHA.". updateReleaseRevision() replaces only the parser-reported revision span./home/byk/Code/getsentry/publish/src/publish/authorize-approval.js; getAutoApprovedRepositories() reads nonempty lines from auto-approve-repos.txt into a Set.src/publish/authorize-approval.js, GitHub requests use Accept: application/vnd.github+json, bearer APPROVAL_TOKEN, and API version "2026-03-10". getIssueEvents() and getIssueComments() paginate with per_page=100&page=${page} until a page contains fewer than 100 records; permission, issue, event, and comment fetch failures include the returned HTTP status in their error.src/publish/authorize-approval.js requires GITHUB_OUTPUT, APPROVAL_TOKEN, APPROVAL_ISSUE_NUMBER, APPROVAL_ISSUE_REPOSITORY, and EXPECTED_REQUEST_DIGEST. For AUTO_APPROVAL_LABELER, authorization requires an open unchanged issue, an auto-approver requester, an allowlisted repository/path, the current accepted event being created by the labeler, and a matching auto-approval attestation authored by "github-actions[bot]".src/publish/authorize-approval.js requires repository permission authorization, then re-fetches the live issue/events and rejects if the issue is closed, title/digest changed, requester is absent, requester equals approver case-insensitively, no current accepted event exists, or the current accepted event actor differs from the approver. It writes authorized=false on rejection; on success it writes authorized=true and approval_attestation=${attestation} to GITHUB_OUTPUT./home/byk/Code/getsentry/publish/src/publish/current-accepted-event.js; getCurrentAcceptedEvent() retrieves all issue events and throws "The issue has no current accepted label event" when none is current. Its CLI requires APPROVAL_TOKEN, APPROVAL_ISSUE_NUMBER, and APPROVAL_ISSUE_REPOSITORY, then writes the event as JSON to stdout./home/byk/Code/getsentry/publish/src/publish/record-auto-approval-attestation.js; recordAutoApprovalAttestation() requires the live issue to remain open, retain the expected title and request digest, and have a requester login matching autoApprover case-insensitively, otherwise throwing "The automated approval request changed before approval". The CLI writes auto_approval_attestation=${attestation} to GITHUB_OUTPUT./home/byk/Code/getsentry/publish/src/publish/record-ci-ready-attestation.js; getAuthenticatedLogin() calls GitHubβs user endpoint and requires a string login. recordCiReadyAttestation() concurrently fetches the issue, issue events, and authenticated login, then requires an open unchanged issue carrying "accepted" and a current accepted event; when supplied, expectedAcceptedEvent must match both actor and event ID. Failure throws "The approval changed before CI could be marked ready".record-ci-ready-attestation.js CLI reads optional expected-event binding only when both EXPECTED_ACCEPTED_ACTOR and EXPECTED_ACCEPTED_EVENT_ID exist, and writes ci_ready_attestation=${attestation} to GITHUB_OUTPUT./home/byk/Code/getsentry/publish/src/publish/request-digest-from-event.js; requestDigestFromEvent(event) requires a non-null object at event.issue, otherwise throws "The GitHub event has no issue snapshot". main() requires GITHUB_EVENT_PATH and GITHUB_OUTPUT, parses the event JSON, and appends request_digest=${requestDigestFromEvent(event)}./home/byk/Code/getsentry/publish/src/publish/validate-approval-attestation.js; its GitHub client uses bearer APPROVAL_TOKEN, API version "2026-03-10", and generic getAllPages() pagination with per_page=100.validateApprovalAttestation(), approval is valid only when the issue is open, title and request digest match expected values, the "accepted" label is present, a current accepted event exists and optionally matches expectedAcceptedEvent, and a matching approval attestation by attestationAuthor exists.requireCiReadyAttestation is true, validateApprovalAttestation() additionally requires a current "ci-ready" event, the "ci-ready" label, and a matching CI-ready attestation bound to the current accepted and CI-ready events. The CLI enables this only for REQUIRE_CI_READY_ATTESTATION === "true" and throws "The current accepted label has no matching approval attestation" when validation fails./home/byk/Code/getsentry/publish/src/publish/inputs.js; it calls detailsFromContext({ context: github.context }) and sets GitHub Actions output "result" to the JSON-stringified result./home/byk/Code/getsentry/publish/src/publish/resolve-ci-poller-input.js; it reads the issue body from PUBLISH_ISSUE_BODY_FILE when set, otherwise from PUBLISH_ISSUE_BODY, parses PUBLISH_ISSUE_LABELS as JSON, takes title from PUBLISH_TITLE and optional revision from PUBLISH_REVISION, then writes JSON from getCiPollerInput() to stdout./home/byk/Code/getsentry/publish/src/publish/resolve-release-revision.js; it parses PUBLISH_ARGS || "{}", requires repo, reads the revision from PUBLISH_ISSUE_BODY || "", and sets GitHub Actions output "revision"./home/byk/Code/getsentry/publish/src/publish/discover-location.js; getWorkspaceNames() returns [] when ${repositoryDirectory}/.craft.yml does not exist, otherwise runs the pinned Craft image using docker run --rm --volume ${path.resolve(repositoryDirectory)}:/github/workspace/__repo__ --workdir /github/workspace/__repo__ ... workspace list, parses stdout as JSON, and requires an array.src/publish/discover-location.js, main() parses PUBLISH_ARGS || "{}", requires input.path or throws "Publish input must define a path.", defaults PUBLISH_REPOSITORY_DIRECTORY to "__repo__", calls discoverLocation(), and sets GitHub Actions output "result" to JSON.