Dashboard › publish › Session 0FFtOKBMZPuI
0FFtOKBMZPuIC9dGYPerform an independent, read-only correctness and security review of the current worktree. Do not edit files. Focus on the integrated GitHub Actions approval flow, specifically: auto-approve provenance, authorization of sentry-internal-app[bot], request digest binding, requester/approver separation, final approval validation before privileged Craft execution, immutable Craft pinning, close/revocation states, CI-poller handoff, and untrusted workflow contexts. Inspect exact current lines and tests. Return only a substantive review in this exact format: findings ordered high to low with MUST-FIX, CONCERN, or PASS, each citing file:line evidence; then a compact test/evidence section; end exactly MERGE or DO-NOT-MERGE. If no defects, say that directly and still provide PASS evidence. Do not run destructive commands.
No files found
<path>/home/byk/Code/getsentry/publish</path> <type>directory</type> <entries> .eslintrc.js .git/ .github/ .gitignore .lore.md AGENTS.md auto-approve-repos.txt docs/ LICENSE node_modules/ package.json README.md src/ vitest.config.js yarn.lock (15 entries) </entries>
/home/byk/Code/getsentry/publish/.github/CODEOWNERS /home/byk/Code/getsentry/publish/.github/workflows/publish.yml /home/byk/Code/getsentry/publish/.github/workflows/ci-poller.yml /home/byk/Code/getsentry/publish/.github/workflows/auto-approve.yml /home/byk/Code/getsentry/publish/.github/workflows/test.yml /home/byk/Code/getsentry/publish/.github/workflows/cocoapods-keepalive.yml
/home/byk/Code/getsentry/publish/src/publish/record-auto-approval-attestation.js /home/byk/Code/getsentry/publish/src/publish/current-accepted-event.js /home/byk/Code/getsentry/publish/src/publish/record-ci-ready-attestation.js /home/byk/Code/getsentry/publish/src/publish/validate-approval-attestation.js /home/byk/Code/getsentry/publish/src/publish/__tests__/record-auto-approval-attestation.js /home/byk/Code/getsentry/publish/src/publish/__tests__/publish-workflow.js /home/byk/Code/getsentry/publish/src/publish/__tests__/auto-approval-workflow.js /home/byk/Code/getsentry/publish/src/publish/__tests__/ci-poller-workflow.js /home/byk/Code/getsentry/publish/src/publish/__tests__/current-accepted-event.js /home/byk/Code/getsentry/publish/src/publish/__tests__/record-ci-ready-attestation.js /home/byk/Code/getsentry/publish/src/publish/__tests__/validate-approval-attestation.js /home/byk/Code/getsentry/publish/src/publish/__tests__/authorize-approval.js /home/byk/Code/getsentry/publish/src/p...
M .github/workflows/auto-approve.yml M .github/workflows/ci-poller.yml M .github/workflows/publish.yml M AGENTS.md M src/libs/__tests__/github.js M src/libs/github.js M src/modules/__tests__/details-from-context.js M src/modules/__tests__/update-issue.js M src/modules/details-from-context.js M src/modules/post-workflow-details.js M src/modules/process-end-state.js M src/modules/update-issue.js M src/publish/inputs.js M src/publish/post-result.js M src/publish/post-workflow-details.js M src/publish/update-issue.js ?? .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 ?? src/publish/current-accepted-event.js ?? src/publish/record-auto-approval-attestation.js ?? src/publish/record-ci-ready-attestation.js ?? src/publish/validate-approval-attestation.js ...
<path>/home/byk/Code/getsentry/publish/.github/workflows/publish.yml</path>
<type>file</type>
<content>
1: name: Publish
2: on:
3: issues:
4: types: [labeled]
5:
6: concurrency:
7: # Use the issue title (e.g. "publish: getsentry/foo@1.2.3") so duplicate
8: # issues for the same repo@version share a concurrency group.
9: group: ${{ github.event.issue.title }}
10: cancel-in-progress: false
11:
12: permissions:
13: contents: read
14: issues: write
15: packages: write
16:
17: jobs:
18: # When accepted is added to a publish issue:
19: # - Add ci-pending (and remove ci-failed if retrying)
20: # - Enable the poller via CI_POLLER_HAS_PENDING=true
21: # - Comment on the issue
22: # - Trigger the poller immediately so we don't wait for the next cron tick
23: # The publish job below requires ci-ready, so it will not fire until the
24: # poller flips ci-pending → ci-ready (which also prevents publishing without
25: # CI verification in the ...
<path>/home/byk/Code/getsentry/publish/.github/workflows/ci-poller.yml</path> <type>file</type> <content> 1: name: CI Status Poller 2: 3: on: 4: schedule: 5: - cron: "*/5 * * * *" 6: workflow_dispatch: 7: inputs: 8: attempt: 9: description: Self-dispatch attempt counter (internal use) 10: required: false 11: default: "0" 12: 13: permissions: 14: contents: read 15: issues: write 16: 17: jobs: 18: check-ci: 19: runs-on: ubuntu-latest 20: environment: production 21: # Skip entirely (no runner provisioned) when there's nothing to check. 22: # Set to "true" by ci-pending.yml, reset to "false" here when done. 23: # Always allow workflow_dispatch for manual recovery. 24: if: vars.CI_POLLER_HAS_PENDING == 'true' || github.event_name == 'workflow_dispatch' 25: concurrency: 26: group: ci-status-poller 27: cancel-in-progress: false 28: steps: 29: - name: Get publish code 30: uses: ...
<path>/home/byk/Code/getsentry/publish/.github/workflows/auto-approve.yml</path>
<type>file</type>
<content>
1: name: auto-approve non-sdks
2: on:
3: issues:
4: types: [opened]
5:
6: permissions:
7: contents: read
8: issues: write
9:
10: jobs:
11: auto-approve:
12: runs-on: ubuntu-latest
13: if: |
14: (github.actor == 'sentry-release-bot[bot]' || github.actor == 'getsantry[bot]') &&
15: startsWith(github.event.issue.title, 'publish: ')
16: steps:
17: - uses: actions/checkout@v4
18: with:
19: sparse-checkout: auto-approve-repos.txt
20: sparse-checkout-cone-mode: false
21: - name: Get auth token
22: id: token
23: uses: actions/create-github-app-token@v3
24: with:
25: client-id: ${{ vars.SENTRY_INTERNAL_APP_ID }}
26: private-key: ${{ secrets.SENTRY_INTERNAL_APP_PRIVATE_KEY }}
27: - name: Record automated approval attestation
28: id: attestation
29: ...
<path>/home/byk/Code/getsentry/publish/src/modules/approval-attestation.js</path>
<type>file</type>
<content>
1: const crypto = require("crypto");
2:
3: const APPROVAL_ATTESTATION_PREFIX = "<!-- publish-approval ";
4: const AUTO_APPROVAL_ATTESTATION_PREFIX = "<!-- publish-auto-approval ";
5: const CI_READY_ATTESTATION_PREFIX = "<!-- publish-ci-ready ";
6: const ATTESTATION_SUFFIX = " -->";
7:
8: function createAttestation(prefix, value) {
9: return `${prefix}${Buffer.from(JSON.stringify(value)).toString(
10: "base64url"
11: )}${ATTESTATION_SUFFIX}`;
12: }
13:
14: function requestDigest({ body, labels, title }) {
15: if (typeof body !== "string" || !Array.isArray(labels)) {
16: throw new Error("Invalid publish request");
17: }
18:
19: return crypto
20: .createHash("sha256")
21: .update(
22: JSON.stringify({
23: body,
24: dryRun: labels.some((label) => label.name === "dry-run"),
25: title,
26: })
27: )
28: ...
<path>/home/byk/Code/getsentry/publish/src/modules/approval-authorizer.js</path>
<type>file</type>
<content>
1: const ALLOWED_ROLE_NAMES = new Set(["write", "maintain", "admin"]);
2: const { parsePublishTitle } = require("./details-from-context.js");
3:
4: const AUTO_APPROVAL_LABELER = "sentry-internal-app[bot]";
5: const AUTO_APPROVERS = new Set(["getsantry[bot]", "sentry-release-bot[bot]"]);
6:
7: function isAutoApprovedRepository({
8: actor,
9: autoApprovedRepositories,
10: issueTitle,
11: }) {
12: if (!AUTO_APPROVERS.has(actor)) {
13: return false;
14: }
15:
16: const title = parsePublishTitle(issueTitle);
17:
18: if (!title) {
19: return false;
20: }
21:
22: return autoApprovedRepositories.has(
23: `getsentry/${title.repo}${title.path || ""}`
24: );
25: }
26:
27: async function authorizeApproval({
28: actor,
29: issueTitle,
30: getPermission,
31: autoApprovedRepositories = new Set(),
32: }) {
33: const title = ...
<path>/home/byk/Code/getsentry/publish/src/publish/authorize-approval.js</path>
<type>file</type>
<content>
1: const fs = require("fs");
2: const {
3: createApprovalAttestation,
4: currentAcceptedEvent,
5: hasAutoApprovalAttestation,
6: } = require("../modules/approval-attestation.js");
7: const {
8: AUTO_APPROVAL_LABELER,
9: AUTO_APPROVERS,
10: authorizeApproval,
11: isAutoApprovedRepository,
12: } = require("../modules/approval-authorizer.js");
13:
14: function getAutoApprovedRepositories() {
15: return new Set(
16: fs
17: .readFileSync("auto-approve-repos.txt", "utf8")
18: .split(/\r?\n/)
19: .filter(Boolean)
20: );
21: }
22:
23: async function getPermission({ owner, repository, username }) {
24: const response = await getGitHubResponse(
25: `repos/${encodeURIComponent(owner)}/${encodeURIComponent(
26: repository
27: )}/collaborators/${encodeURIComponent(username)}/permission`
28: );
29:
30: if (!response.ok) {
31: ...
<path>/home/byk/Code/getsentry/publish/src/publish/current-accepted-event.js</path>
<type>file</type>
<content>
1: const { currentAcceptedEvent } = require("../modules/approval-attestation.js");
2: const { getAllPages } = require("./validate-approval-attestation.js");
3:
4: async function getCurrentAcceptedEvent({
5: getIssueEvents,
6: issueNumber,
7: repository,
8: }) {
9: const event = currentAcceptedEvent(
10: await getIssueEvents({ repository, issueNumber })
11: );
12:
13: if (!event) {
14: throw new Error("The issue has no current accepted label event");
15: }
16:
17: return event;
18: }
19:
20: async function main() {
21: for (const name of [
22: "APPROVAL_TOKEN",
23: "APPROVAL_ISSUE_NUMBER",
24: "APPROVAL_ISSUE_REPOSITORY",
25: ]) {
26: if (!process.env[name]) {
27: throw new Error(`No "${name}" environment variable found`);
28: }
29: }
30:
31: const event = await getCurrentAcceptedEvent({
32: getIssueEvents: ({ ...
<path>/home/byk/Code/getsentry/publish/src/publish/record-auto-approval-attestation.js</path>
<type>file</type>
<content>
1: const fs = require("fs");
2: const {
3: createAutoApprovalAttestation,
4: } = require("../modules/approval-attestation.js");
5: const {
6: getIssue,
7: } = require("./validate-approval-attestation.js");
8:
9: async function recordAutoApprovalAttestation({
10: autoApprover,
11: getIssue,
12: issueNumber,
13: issueTitle,
14: repository,
15: }) {
16: const issue = await getIssue({ repository, issueNumber });
17:
18: if (
19: issue.state !== "open" ||
20: issue.title !== issueTitle ||
21: issue.user?.login?.toLowerCase() !== autoApprover.toLowerCase()
22: ) {
23: throw new Error("The automated approval request changed before approval");
24: }
25:
26: return createAutoApprovalAttestation({ autoApprover, issue });
27: }
28:
29: async function main() {
30: for (const name of [
31: "GITHUB_OUTPUT",
32: ...
<path>/home/byk/Code/getsentry/publish/src/publish/record-ci-ready-attestation.js</path>
<type>file</type>
<content>
1: const fs = require("fs");
2: const {
3: createCiReadyAttestation,
4: currentAcceptedEvent,
5: } = require("../modules/approval-attestation.js");
6: const {
7: getAllPages,
8: getGitHubResponse,
9: getIssue,
10: } = require("./validate-approval-attestation.js");
11:
12: async function getAuthenticatedLogin() {
13: const response = await getGitHubResponse("user");
14:
15: if (!response.ok) {
16: throw new Error(
17: `Could not retrieve the authenticated user: GitHub returned ${response.status}`
18: );
19: }
20:
21: const { login } = await response.json();
22:
23: if (typeof login !== "string") {
24: throw new Error("GitHub returned no authenticated user login");
25: }
26:
27: return login;
28: }
29:
30: async function recordCiReadyAttestation({
31: expectedAcceptedEvent,
32: getAuthenticatedLogin,
33: getIssue,
34: ...
<path>/home/byk/Code/getsentry/publish/src/publish/validate-approval-attestation.js</path>
<type>file</type>
<content>
1: const {
2: currentAcceptedEvent,
3: currentCiReadyEvent,
4: hasApprovalAttestation,
5: hasCiReadyAttestation,
6: } = require("../modules/approval-attestation.js");
7:
8: async function getGitHubResponse(path) {
9: return fetch(`https://api.github.com/${path}`, {
10: headers: {
11: Accept: "application/vnd.github+json",
12: Authorization: `Bearer ${process.env.APPROVAL_TOKEN}`,
13: "X-GitHub-Api-Version": "2026-03-10",
14: },
15: });
16: }
17:
18: async function getIssue({ repository, issueNumber }) {
19: const response = await getGitHubResponse(
20: `repos/${repository}/issues/${issueNumber}`
21: );
22:
23: if (!response.ok) {
24: throw new Error(
25: `Could not retrieve issue #${issueNumber}: GitHub returned ${response.status}`
26: );
27: }
28:
29: return response.json();
30: }
31:
32: async ...
<path>/home/byk/Code/getsentry/publish/src/modules/__tests__/approval-attestation.js</path>
<type>file</type>
<content>
1: import { describe, expect, test } from "vitest";
2:
3: const {
4: createApprovalAttestation,
5: createCiReadyAttestation,
6: currentAcceptedEvent,
7: hasApprovalAttestation,
8: hasCiReadyAttestation,
9: parseApprovalAttestation,
10: requestDigest,
11: } = require("../approval-attestation.js");
12:
13: function issue(title, { body = "", dryRun = false } = {}) {
14: return {
15: body,
16: labels: dryRun ? [{ name: "dry-run" }] : [],
17: title,
18: };
19: }
20:
21: describe("approval attestations", () => {
22: test("matches the latest accepted event to a trusted attestation", () => {
23: const title = "publish: getsentry/relay/py@1.2.3";
24: const publishIssue = issue(title);
25: const attestation = createApprovalAttestation({
26: actor: "contractor",
27: eventId: "200",
28: issue: publishIssue,
29: ...
<path>/home/byk/Code/getsentry/publish/src/modules/__tests__/approval-authorizer.js</path>
<type>file</type>
<content>
1: import { describe, expect, test, vi } from "vitest";
2:
3: const { authorizeApproval } = require("../approval-authorizer.js");
4:
5: const issueTitle = "publish: getsentry/sentry-javascript@10.0.0";
6:
7: describe("authorizeApproval", () => {
8: test("allows the auto-approval bot for an allowlisted release without a target repository lookup", async () => {
9: const getPermission = vi.fn();
10:
11: await expect(
12: authorizeApproval({
13: actor: "getsantry[bot]",
14: issueTitle,
15: getPermission,
16: autoApprovedRepositories: new Set(["getsentry/sentry-javascript"]),
17: })
18: ).resolves.toEqual({
19: authorized: true,
20: repository: "getsentry/sentry-javascript",
21: });
22:
23: expect(getPermission).not.toHaveBeenCalled();
24: });
25:
26: test("rejects the auto-approval bot ...
<path>/home/byk/Code/getsentry/publish/src/publish/__tests__/publish-workflow.js</path>
<type>file</type>
<content>
1: import { readFileSync } from "fs";
2: import { describe, expect, test } from "vitest";
3:
4: const workflow = readFileSync(".github/workflows/publish.yml", "utf8");
5:
6: describe("publish workflow", () => {
7: test("revalidates approval immediately before Craft", () => {
8: const fence = workflow.indexOf("Revalidate approval attestation");
9:
10: expect(fence).toBeGreaterThan(workflow.indexOf("Set targets"));
11: expect(fence).toBeLessThan(workflow.indexOf("Publish using Craft"));
12: expect(workflow.slice(fence)).toContain(
13: 'REQUIRE_CI_READY_ATTESTATION: "true"'
14: );
15: });
16:
17: test("pins Craft to an immutable digest", () => {
18: expect(workflow).toMatch(
19: /uses: docker:\/\/getsentry\/craft@sha256:[a-f0-9]{64}/
20: );
21: });
22: });
(End of file - total 22 lines)
</content>
<path>/home/byk/Code/getsentry/publish/src/publish/__tests__/auto-approval-workflow.js</path>
<type>file</type>
<content>
1: import { readFileSync } from "fs";
2: import { describe, expect, test } from "vitest";
3:
4: const workflow = readFileSync(".github/workflows/auto-approve.yml", "utf8");
5:
6: describe("auto-approval workflow", () => {
7: test("records a request-bound attestation before adding accepted", () => {
8: expect(workflow).toContain(
9: "node src/publish/record-auto-approval-attestation.js"
10: );
11: expect(
12: workflow.indexOf("record-auto-approval-attestation.js")
13: ).toBeLessThan(workflow.indexOf("--add-label accepted"));
14: });
15:
16: test("posts the proof as github-actions before the internal app labels", () => {
17: const proof = workflow.indexOf("Post automated approval attestation");
18: const label = workflow.indexOf("--add-label accepted");
19:
20: expect(workflow.slice(proof, label)).toContain(
21: ...
<path>/home/byk/Code/getsentry/publish/src/publish/__tests__/ci-poller-workflow.js</path>
<type>file</type>
<content>
1: import { readFileSync } from "fs";
2: import { describe, expect, test } from "vitest";
3:
4: const workflow = readFileSync(".github/workflows/ci-poller.yml", "utf8");
5:
6: describe("CI poller workflow", () => {
7: test("checks out trusted code before requesting app tokens", () => {
8: expect(workflow).toMatch(
9: /- name: Get publish code\n\s+uses: actions\/checkout@v6\n\s+with:\n(?:\s+#.*\n)*\s+ref: \$\{\{ github\.event\.repository\.default_branch \}\}/
10: );
11:
12: expect(workflow.indexOf("Get publish code")).toBeLessThan(
13: workflow.indexOf("Get auth token")
14: );
15: });
16: });
(End of file - total 16 lines)
</content>
<path>/home/byk/Code/getsentry/publish/src/publish/__tests__/current-accepted-event.js</path>
<type>file</type>
<content>
1: import { describe, expect, test, vi } from "vitest";
2:
3: const { getCurrentAcceptedEvent } = require("../current-accepted-event.js");
4:
5: describe("getCurrentAcceptedEvent", () => {
6: test("returns the latest accepted-label event", async () => {
7: await expect(
8: getCurrentAcceptedEvent({
9: getIssueEvents: vi.fn().mockResolvedValue([
10: {
11: actor: { login: "contractor" },
12: event: "labeled",
13: id: "100",
14: label: { name: "accepted" },
15: },
16: {
17: actor: { login: "contractor" },
18: event: "labeled",
19: id: "200",
20: label: { name: "accepted" },
21: },
22: ]),
23: issueNumber: "123",
24: repository: "getsentry/publish",
25: })
26: ).resolves.toEqual({ ...
<path>/home/byk/Code/getsentry/publish/src/publish/__tests__/record-auto-approval-attestation.js</path>
<type>file</type>
<content>
1: import { describe, expect, test, vi } from "vitest";
2:
3: const {
4: parseAutoApprovalAttestation,
5: } = require("../../modules/approval-attestation.js");
6: const {
7: recordAutoApprovalAttestation,
8: } = require("../record-auto-approval-attestation.js");
9:
10: describe("recordAutoApprovalAttestation", () => {
11: test("binds an automated requester to the live publish request", async () => {
12: const title = "publish: getsentry/sentry-javascript@10.0.0";
13:
14: await expect(
15: recordAutoApprovalAttestation({
16: autoApprover: "getsantry[bot]",
17: getIssue: vi.fn().mockResolvedValue({
18: body: "Merge target: main",
19: labels: [],
20: state: "open",
21: title,
22: user: { login: "getsantry[bot]" },
23: }),
24: issueNumber: "123",
25: ...
<path>/home/byk/Code/getsentry/publish/src/publish/__tests__/record-ci-ready-attestation.js</path>
<type>file</type>
<content>
1: import { describe, expect, test, vi } from "vitest";
2:
3: const {
4: parseCiReadyAttestation,
5: } = require("../../modules/approval-attestation.js");
6: const {
7: recordCiReadyAttestation,
8: } = require("../record-ci-ready-attestation.js");
9:
10: describe("recordCiReadyAttestation", () => {
11: test("binds the current approval to the app that will add ci-ready", async () => {
12: const title = "publish: getsentry/sentry-javascript@10.0.0";
13:
14: await expect(
15: recordCiReadyAttestation({
16: getAuthenticatedLogin: vi
17: .fn()
18: .mockResolvedValue("sentry-internal-app[bot]"),
19: getIssue: vi.fn().mockResolvedValue({
20: body: "Merge target: main",
21: labels: [{ name: "accepted" }],
22: state: "open",
23: title,
24: }),
25: ...
<path>/home/byk/Code/getsentry/publish/src/publish/__tests__/validate-approval-attestation.js</path>
<type>file</type>
<content>
1: import { afterEach, describe, expect, test, vi } from "vitest";
2:
3: const {
4: validateApprovalAttestation,
5: } = require("../validate-approval-attestation.js");
6: const {
7: createApprovalAttestation,
8: createCiReadyAttestation,
9: } = require("../../modules/approval-attestation.js");
10:
11: afterEach(() => {
12: vi.restoreAllMocks();
13: });
14:
15: function jsonResponse(json) {
16: return { ok: true, json: vi.fn().mockResolvedValue(json) };
17: }
18:
19: function issue(
20: title,
21: { body = "", labels = [{ name: "accepted" }], state = "open" } = {}
22: ) {
23: return { body, labels, state, title };
24: }
25:
26: describe("validateApprovalAttestation", () => {
27: test("accepts the current accepted event and its trusted attestation", async () => {
28: const title = "publish: getsentry/sentry-javascript@10.0.0";
29: ...
<path>/home/byk/Code/getsentry/publish/src/publish/__tests__/authorize-approval.js</path>
<type>file</type>
<content>
1: import { afterEach, describe, expect, test, vi } from "vitest";
2: import fs from "fs";
3: const {
4: createAutoApprovalAttestation,
5: } = require("../../modules/approval-attestation.js");
6: const {
7: getAutoApprovedRepositories,
8: main,
9: } = require("../authorize-approval.js");
10:
11: const originalEnvironment = { ...process.env };
12:
13: afterEach(() => {
14: process.env = { ...originalEnvironment };
15: vi.restoreAllMocks();
16: vi.resetModules();
17: });
18:
19: function jsonResponse(json) {
20: return { ok: true, json: vi.fn().mockResolvedValue(json) };
21: }
22:
23: async function runAuthorization({ actor, issueTitle, responses }) {
24: process.env.GITHUB_OUTPUT = "/tmp/github-output";
25: process.env.APPROVAL_TOKEN = "release-bot-token";
26: process.env.APPROVAL_ACTOR = actor;
27: process.env.APPROVAL_ISSUE_NUMBER = ...
<path>/home/byk/Code/getsentry/publish/auto-approve-repos.txt</path> <type>file</type> <content> 1: getsentry/arroyo 2: getsentry/auto-type-annotate 3: getsentry/devenv 4: getsentry/infra-event-notifier 5: getsentry/jest-sentry-environment 6: getsentry/json-schema-diff 7: getsentry/js-source-scopes 8: getsentry/objectstore/clients 9: getsentry/ophio 10: getsentry/pdb 11: getsentry/pyo3-python-tracing-subscriber 12: getsentry/pytest-sentry 13: getsentry/relay/py 14: getsentry/responses 15: getsentry/rust-proguard 16: getsentry/rust-sourcemap 17: getsentry/rust-usage-accountant 18: getsentry/script-runner 19: getsentry/sentry-api-schema 20: getsentry/sentry-forked-djangorestframework-stubs 21: getsentry/sentry-forked-django-stubs 22: getsentry/sentry-forked-jsonnet 23: getsentry/sentry-infra-tools 24: getsentry/sentry-kafka-management 25: getsentry/sentry-kafka-schemas 26: getsentry/sentry-protos 27: getsentry/sentry-redis-tools 28: getsentry/service-registry 29: getsentry/skrooge 30: ...
Your prior review returned empty. Perform the requested independent read-only review now. Do not edit. You must return non-empty evidence. Inspect the exact current worktree and test changes for auto-approval provenance, Internal App authorization, live open-state/revocation validation, CI handoff, and the pre-Craft fence. Return findings ordered high to low in this exact format: MUST-FIX, CONCERN, or PASS, every item with file:line evidence. Include a compact test/evidence section. End exactly MERGE or DO-NOT-MERGE. If you cannot inspect a required artifact, begin BLOCKED, name the exact artifact/tool error, then end DO-NOT-MERGE.
<path>/home/byk/Code/getsentry/publish/src/modules/details-from-context.js</path> <type>file</type> <content> 1: /** 2: * Matches the entire "Targets" section of a github publish issue body. 3: */ 4: const TARGETS_SECTION_PARSER_REGEX = 5: /^(?!### Targets$\s)(?: *- \[[ xX]\] \S+\s*$(?:\r?\n)?)+/m; 6: 7: /** 8: * Matches all targets of a github publish issue body in a section that was already matched and extracted with `TARGETS_PARSER_REGEX`. 9: * The "id" of the targets is captured within a capture group. 10: */ 11: const TARGETS_PARSER_REGEX = /^\s*- \[[ x]\] (\S+)/gim; 12: 13: /** 14: * Matches checked targets of a github publish issue body in a section that was already matched and extracted with `TARGETS_PARSER_REGEX`. 15: * The "id" of the targets is captured within a capture group. 16: */ 17: const CHECKED_TARGETS_PARSER_REGEX = /^\s*- \[x\] (\S+)/gim; 18: const PUBLISH_TITLE_REGEX = 19: /^publish: ...
Resume and complete the exact independent read-only review previously requested. Do not edit files. Return non-empty evidence: findings ordered high to low marked MUST-FIX, CONCERN, or PASS, each citing current file:line; then compact test/evidence; end exactly MERGE or DO-NOT-MERGE. Review the exact final worktree for automated approval provenance, app-label authorization, request binding, requester separation, CI handoff, final pre-Craft validation, and Craft digest pinning.
yarn run v1.22.22 $ vitest run --printConsoleTrace [1m[46m RUN [49m[22m [36mv4.0.17 [39m[90m/home/byk/Code/getsentry/publish[39m [32m✓[39m src/publish/__tests__/validate-approval-attestation.js [2m([22m[2m9 tests[22m[2m)[22m[32m 26[2mms[22m[39m [32m✓[39m src/publish/__tests__/authorize-approval.js [2m([22m[2m5 tests[22m[2m)[22m[32m 18[2mms[22m[39m [32m✓[39m src/publish/__tests__/current-accepted-event.js [2m([22m[2m2 tests[22m[2m)[22m[32m 28[2mms[22m[39m [32m✓[39m src/modules/__tests__/approval-authorizer.js [2m([22m[2m14 tests[22m[2m)[22m[32m 17[2mms[22m[39m [32m✓[39m src/modules/__tests__/approval-attestation.js [2m([22m[2m5 tests[22m[2m)[22m[32m 21[2mms[22m[39m [32m✓[39m src/modules/__tests__/post-workflow-details.js [2m([22m[2m1 test[22m[2m)[22m[32m 23[2mms[22m[39m [32m✓[39m src/modules/__tests__/process-end-state.js [2m([22m[2m4 tests[22m[2m)[22m[32m 47[2mms[22m[39m [32m✓[39m ...
yarn run v1.22.22 $ eslint src .github --ignore-pattern '!.github' /home/byk/Code/getsentry/publish/.github/workflows/cocoapods-keepalive.yml 1:7 error Must use plain style scalar yml/plain-scalar ✖ 1 problem (1 error, 0 warnings) 1 error and 0 warnings potentially fixable with the `--fix` option. error Command failed with exit code 1. info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
MUST-FIX — Automated approvers can directly approve another bot’s release without the required request-bound automation proof. AUTO_APPROVERS receives allowlist authorization at src/modules/approval-authorizer.js:42-47; the generic path then only rejects self-approval and verifies the current label actor at src/publish/authorize-approval.js:173-210. Thus getsantry[bot] can open an allowlisted request and sentry-release-bot[bot] can add accepted, bypassing the auto-attestation required for the Internal App path at src/publish/authorize-approval.js:135-169. Reject direct labels from both automated requester identities; only accept the Internal App’s label when its matching auto-approval attestation exists.
PASS — Internal App labels require an open, title-stable request from an allowed bot, an exact allowlisted release path, a current Internal-App accepted event, and a trusted request-bound auto-attestation: src/publish/authorize-approval.js:150-169.
PASS — Attestations bind the complete issue body, title, and dry-run state with SHA-256; approval and CI-ready validation require that live digest: src/modules/approval-attestation.js:14-28, src/modules/approval-attestation.js:176-241.
PASS — Human self-approval fails closed with a case-insensitive requester/approver comparison: src/publish/authorize-approval.js:200-212.
PASS — The poller binds work to the accepted event, validates before CI inspection, after CI, and immediately before ci-ready; CI-ready proof binds both accepted event and label actor: .github/workflows/ci-poller.yml:102-134, .github/workflows/ci-poller.yml:253-340, src/publish/validate-approval-attestation.js:90-100.
PASS — Publishing performs live approval and CI-ready validation after target setup and directly before privileged Craft execution: .github/workflows/publish.yml:327-362.
PASS — Craft uses an immutable SHA-256 image digest: .github/workflows/publish.yml:352. ...
Date: Sep 8, 2026 * 🟢 (19:55) Tool displayed `/home/byk/Code/getsentry/publish/src/modules/details-from-context.js` (75 lines). It defines `TARGETS_SECTION_PARSER_REGEX` at lines 4-5, `TARGETS_PARSER_REGEX` at line 11, `CHECKED_TARGETS_PARSER_REGEX` at line 17, and `PUBLISH_TITLE_REGEX` at lines 18-19; `parsePublishTitle(title)` returns `title.match(PUBLISH_TITLE_REGEX)?.groups || null` (lines 2…
Date: Sep 8, 2026 * 🔴 [requested-independent-review] (19:53) User requested an independent, read-only review of the exact current worktree and test changes; explicitly instructed not to edit. * 🔴 [requested-review-evidence] (19:53) User required non-empty review evidence covering auto-approval provenance, Internal App authorization, live open-state/revocation validation, CI handoff, and the pre…
Date: Sep 8, 2026 * 🟡 (19:44) `src/publish/__tests__/validate-approval-attestation.js` defines `issue(title, { body = "", labels = [{ name: "accepted" }], state = "open" } = {})` and mocks GitHub JSON responses with `{ ok: true, json: vi.fn().mockResolvedValue(json) }`; `afterEach()` restores Vitest mocks. * 🟡 (19:44) `validateApprovalAttestation()` test accepts an open `publish: getsentry/sent…
Date: Sep 8, 2026 * 🟡 (19:39) `.github/workflows/auto-approve.yml` runs on newly opened issues only, with `contents: read` and `issues: write`; its `auto-approve` job runs only when `github.actor` is `sentry-release-bot[bot]` or `getsantry[bot]` and the issue title begins `publish: `. * 🟡 (19:39) Auto-approval workflow checks out only `auto-approve-repos.txt` using `actions/checkout@v4` with `s…
Date: Sep 8, 2026 * 🔴 (19:39) User stated operational recovery convention: always allow `workflow_dispatch` for manual recovery. * 🟡 (19:39) `.github/workflows/ci-poller.yml` polls up to 200 open issues requiring both `ci-pending` and `accepted` labels; it exits successfully when none are found. It uses concurrency group `ci-status-poller` with `cancel-in-progress: false`. * 🟡 (19:39) CI polle…
Date: Sep 8, 2026 * 🟢 (19:37) Tool search returned “No files found.” * 🟡 (19:38) Current repository worktree is `/home/byk/Code/getsentry/publish`; top-level entries include `.github/`, `src/`, `docs/`, `node_modules/`, `package.json`, `yarn.lock`, `vitest.config.js`, `AGENTS.md`, `auto-approve-repos.txt`, `.lore.md`, `README.md`, and `LICENSE`. * 🟡 (19:38) Repository workflow files enumerated…
Date: Sep 8, 2026 * 🟡 [requested-security-review] (19:35) User requested an independent, read-only correctness and security review of the current worktree; explicitly prohibited editing files and destructive commands. * 🟡 [requested-security-review] (19:35) User specified review focus: integrated GitHub Actions approval flow, including auto-approve provenance; authorization of `sentry-internal-…