DashboardpublishSession 0ScoBEWOQxXp

Session 0ScoBEWOQxXp

Full ID: 0ScoBEWOQxXpF1oXs
Messages: 56
Distillations: 0
Time range: 2026-09-10 01:52:13 — 2026-09-10 02:10:23

Conversation (56 messages)

READ ONLY. In /home/byk/Code/getsentry/publish, compare HEAD/current worktree/origin/main for JavaScript, parser, docs/package changes from origin/main commit 7c60ddb. Focus details-from-context, update-issue, inputs, all newly added parser/location/revision/ci-input files and tests, package/yarn lock. Explain exact origin/main behavior to integrate, conflicts with current dirty security worktree, file dependencies, and safest integration sequence. Do not edit files. Return concise file-specific guidance and risks.

gateway · 2026-09-10 01:52:13
result
No files found
gateway · 2026-09-10 01:52:35
result
## main...origin/main [behind 1]
 M .github/workflows/auto-approve.yml
 M .github/workflows/ci-poller.yml
 M .github/workflows/cocoapods-keepalive.yml
 M .github/workflows/publish.yml
 M .github/workflows/test.yml
 M AGENTS.md
 M README.md
 M docs/key_rotation_vault_changes.md
 M docs/rfc.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
?? .github/workflows/ci-poller-dispatch.yml
?? .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__/
?? ...
gateway · 2026-09-10 01:52:40
result
* 4f4b00f (origin/fix/root-location-bootstrap, fix/root-location-bootstrap) fix: bootstrap root publish locations
* 7c60ddb (origin/main, origin/HEAD) feat: accept workspace publish requests
| * fd1e115 (origin/feat/workspace-acceptance, feat/workspace-acceptance) fix: address publish workspace review feedback
| * a81ab03 feat: resolve workspace publish paths
| * 52ef600 feat: resolve compact workspace publish requests
| * b658ffa fix: validate publish workspace JSON
| * 33de3f1 feat: accept workspace publish requests
|/  
* 83d210b (HEAD -> main, opencode/tidy-orchid) build(deps): bump js-yaml from 4.3.0 to 4.3.1 (#9174)
* 5268a23 feat: Add Vercel creds
* 92eba3f feat: Add CLOUDFLARE_API_TOKEN (#9122)
* ada6147 build(deps): bump js-yaml from 4.1.1 to 4.3.0 (#8731)
* a4f961a fix: Add GITHUB_API_TOKEN for commit-on-git target (#9069)
* bf10f82 build(deps): bump postcss from 8.5.15 to 8.5.23 (#9017)
* 837edc0 docs: fix possessive apostrophe placement (#9015)
* 4da4a25 chore: replace ...
gateway · 2026-09-10 01:52:46
result
M	.github/workflows/auto-approve.yml
M	.github/workflows/ci-poller.yml
M	.github/workflows/cocoapods-keepalive.yml
M	.github/workflows/publish.yml
M	.github/workflows/test.yml
M	AGENTS.md
M	README.md
M	docs/key_rotation_vault_changes.md
M	docs/rfc.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
 .github/workflows/auto-approve.yml            |  26 +++-
 .github/workflows/ci-poller.yml               | 195 +++++++++++++++++++++++---
 .github/workflows/cocoapods-keepalive.yml     |   3 +-
 .github/workflows/publish.yml                 | 139 ++++++++++++++++--
 .github/workflows/test.yml                    |   6 +-
 AGENTS.md             ...
gateway · 2026-09-10 01:52:51
result
M	.github/workflows/ci-poller.yml
M	.github/workflows/cocoapods-keepalive.yml
M	.github/workflows/publish.yml
M	README.md
A	docs/publish-issue-format.md
M	package.json
A	scripts/generate-publish-issue-title-parser.js
A	src/modules/__tests__/ci-poller-input.js
A	src/modules/__tests__/ci-poller-workflow.js
M	src/modules/__tests__/details-from-context.js
A	src/modules/__tests__/generate-publish-issue-title-parser.js
A	src/modules/__tests__/publish-location.js
A	src/modules/__tests__/publish-workflow.js
A	src/modules/__tests__/release-revision.js
M	src/modules/__tests__/update-issue.js
A	src/modules/ci-poller-input.js
M	src/modules/details-from-context.js
A	src/modules/publish-issue-title.js
A	src/modules/publish-issue-title.peggy
A	src/modules/publish-issue-validation.js
A	src/modules/publish-location.js
A	src/modules/release-revision.js
M	src/modules/update-issue.js
A	src/publish/__tests__/discover-location.js
A	src/publish/__tests__/resolve-location.js
A	src/publish/__tests__/resolve-re...
gateway · 2026-09-10 01:53:13
result
/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__/workflow-action-pinning.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/...
gateway · 2026-09-10 01:53:40
result
diff --git a/src/modules/details-from-context.js b/src/modules/details-from-context.js
index e58b0be..9288b5f 100644
--- a/src/modules/details-from-context.js
+++ b/src/modules/details-from-context.js
@@ -1,64 +1,83 @@
+const { parse: parsePublishIssueTitle } = require("./publish-issue-title");
+const { getReleaseRevisionDetails } = require("./release-revision");
+const { isPublishPath } = require("./publish-location");
+const {
+  isPublishRepository,
+  isReleaseVersion,
+} = require("./publish-issue-validation");
+
 /**
  * Matches the entire "Targets" section of a github publish issue body.
  */
 const TARGETS_SECTION_PARSER_REGEX =
   /^(?!### Targets$\s)(?: *- \[[ xX]\] \S+\s*$(?:\r?\n)?)+/m;
 
 /**
  * Matches all targets of a github publish issue body in a section that was already matched and extracted with `TARGETS_PARSER_REGEX`.
  * The "id" of the targets is captured within a capture group. ...
gateway · 2026-09-10 01:53:47
result
diff --git a/src/modules/details-from-context.js b/src/modules/details-from-context.js
index e58b0be..bd2c123 100644
--- a/src/modules/details-from-context.js
+++ b/src/modules/details-from-context.js
@@ -1,64 +1,75 @@
 /**
  * Matches the entire "Targets" section of a github publish issue body.
  */
 const TARGETS_SECTION_PARSER_REGEX =
   /^(?!### Targets$\s)(?: *- \[[ xX]\] \S+\s*$(?:\r?\n)?)+/m;
 
 /**
  * Matches all targets of a github publish issue body in a section that was already matched and extracted with `TARGETS_PARSER_REGEX`.
  * The "id" of the targets is captured within a capture group.
  */
 const TARGETS_PARSER_REGEX = /^\s*- \[[ x]\] (\S+)/gim;
 
 /**
  * Matches checked targets of a github publish issue body in a section that was already matched and extracted with `TARGETS_PARSER_REGEX`.
  * The "id" of the targets is captured within a capture group.
  */
 const CHECKED_TARGETS_PARSER_REGEX = /^\s*- \[x\] (\S+)/gim;
+const PUBLISH_TITLE_REGEX =
+  /^publish: ...
gateway · 2026-09-10 01:53:55
result
Found 36 matches
/home/byk/Code/getsentry/publish/src/publish/inputs.js:
  Line 3: const { detailsFromContext } = require("../modules/details-from-context");

  Line 6:   const result = await detailsFromContext({


/home/byk/Code/getsentry/publish/src/modules/approval-authorizer.js:
  Line 2: const { parsePublishTitle } = require("./details-from-context.js");

  Line 16:   const title = parsePublishTitle(issueTitle);

  Line 28:   const title = parsePublishTitle(issueTitle);


/home/byk/Code/getsentry/publish/src/modules/details-from-context.js:
  Line 18: const PUBLISH_TITLE_REGEX =

  Line 21: function parsePublishTitle(title) {

  Line 22:   return title.match(PUBLISH_TITLE_REGEX)?.groups || null;

  Line 25: async function detailsFromContext({ context }) {

  Line 30:   const titleMatch = parsePublishTitle(context.payload.issue.title);

  Line 69:   detailsFromContext,

  Line 73:   parsePublishTitle,

  Line 74:   ...
gateway · 2026-09-10 01:54:06
result
<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({ actor, issueTitle, getPermission }) {
28:   const title = parsePublishTitle(issueTitle);
29: 
30:   if (!title) {
31:     return { authorized: ...
gateway · 2026-09-10 01:54:18
result
diff --git a/src/modules/ci-poller-input.js b/src/modules/ci-poller-input.js
new file mode 100644
index 0000000..ca08a72
--- /dev/null
+++ b/src/modules/ci-poller-input.js
@@ -0,0 +1,42 @@
+const { parse: parsePublishIssueTitle } = require("./publish-issue-title");
+const {
+  getReleaseRevision,
+  updateReleaseRevision,
+} = require("./release-revision");
+const {
+  isPublishRepository,
+  isReleaseVersion,
+} = require("./publish-issue-validation");
+const { isPublishPath } = require("./publish-location");
+
+function getCiPollerInput({ title, issueBody, revision }) {
+  let parsedTitle;
+  try {
+    parsedTitle = parsePublishIssueTitle(title);
+  } catch {
+    throw new Error(`Invalid publish issue title: '${title}'`);
+  }
+  const { repo, version } = parsedTitle;
+  if (!isPublishRepository(repo)) {
+    throw new Error(`Invalid publish issue repository: '${repo}'`);
+  }
+  if (!isReleaseVersion(version)) {
+    throw new Error(`Invalid publish issue version: ...
gateway · 2026-09-10 01:54:29
result
...output truncated...

Full output saved to: /home/byk/.local/share/opencode/tool-output/tool_0890607de001jDhs4SEuD6NBvd

     "@opentelemetry/instrumentation" "^0.208.0"
     "@opentelemetry/semantic-conventions" "^1.36.0"
 
 "@opentelemetry/instrumentation-lru-memoizer@0.53.0":
   version "0.53.0"
   resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.53.0.tgz#936c05263b719ee66999a9240b82fded044ebd2c"
   integrity sha512-LDwWz5cPkWWr0HBIuZUjslyvijljTwmwiItpMTHujaULZCxcYE9eU44Qf/pbVC8TulT0IhZi+RoGvHKXvNhysw==
   dependencies:
     "@opentelemetry/instrumentation" "^0.208.0"
 
 "@opentelemetry/instrumentation-mongodb@0.61.0":
   version "0.61.0"
   resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.61.0.tgz#4db130d537d630c3089115d2d214d29bcfb49f41"
   integrity sha512-OV3i2DSoY5M/pmLk+68xr5RvkHU8DRB3DKMzYJdwDdcxeLs62tLbkmRyqJZsYf3Ht7j11rq35pHOWLuLzXL7pQ==
   ...
gateway · 2026-09-10 01:54:37
result
diff --git a/package.json b/package.json
index d59a8b6..0790379 100644
--- a/package.json
+++ b/package.json
@@ -1,35 +1,38 @@
 {
   "name": "publish",
   "private": true,
   "version": "0.0.1",
   "description": "Approval-based publishing system for Sentry",
   "main": "index.js",
   "repository": "git@github.com:getsentry/publish.git",
   "author": "Sentry Open Source <oss@sentry.io>",
   "license": "Apache-2.0",
   "volta": {
     "node": "24.0.0",
     "yarn": "1.22.22"
   },
   "resolutions": {
     "undici": "^6.23.0"
   },
   "scripts": {
-    "test": "vitest run",
+    "generate": "node scripts/generate-publish-issue-title-parser.js",
+    "check:generated": "node scripts/generate-publish-issue-title-parser.js --check",
+    "test": "yarn check:generated && vitest run",
     "test:watch": "vitest",
     "lint": "eslint src .github --ignore-pattern '!.github'",
     "prettier": "prettier --write src"
   },
   "devDependencies": {
     "eslint": "^8.9.0",
     ...
gateway · 2026-09-10 01:54:44
result
diff --git a/yarn.lock b/yarn.lock
index 571d807..1fcddf4 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -535,6 +535,13 @@
   resolved "https://registry.yarnpkg.com/@oxc-project/types/-/types-0.133.0.tgz#2e282ef9e1d26e06b68ccd14b73f310a3b2cf7f8"
   integrity sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==
 
+"@peggyjs/from-mem@3.1.3":
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/@peggyjs/from-mem/-/from-mem-3.1.3.tgz#2956c3c68ec2e08e55178049a327009ee61541b8"
+  integrity sha512-LLlgtfXIaeYXoOYovOI0spLM8ZXaqkAlmcRRrLzHJzLMqkU6Sw0R4KMoCoHx1PjaP815pSCBlS+BN6aD8t1Jgg==
+  dependencies:
+    semver "7.7.4"
+
 "@prisma/instrumentation@6.19.0":
   version "6.19.0"
   resolved "https://registry.yarnpkg.com/@prisma/instrumentation/-/instrumentation-6.19.0.tgz#46d15adc8bc4a5a3167032eea6d0a7aa64fb7d93"
@@ -944,6 +951,11 @@ color-name@~1.1.4:
   resolved ...
gateway · 2026-09-10 01:54:53
result
diff --git a/src/publish/discover-location.js b/src/publish/discover-location.js
new file mode 100644
index 0000000..5885443
--- /dev/null
+++ b/src/publish/discover-location.js
@@ -0,0 +1,78 @@
+const { execFileSync } = require("child_process");
+const { existsSync } = require("fs");
+const path = require("path");
+
+const core = require("@actions/core");
+
+const { resolvePublishLocation } = require("../modules/publish-location");
+
+function getWorkspaceNames({
+  repositoryDirectory,
+  exists = existsSync,
+  execFile = execFileSync,
+}) {
+  if (!exists(path.join(repositoryDirectory, ".craft.yml"))) {
+    return [];
+  }
+
+  const output = execFile(
+    "docker",
+    [
+      "run",
+      "--rm",
+      "--volume",
+      `${path.resolve(repositoryDirectory)}:/github/workspace/__repo__`,
+      "--workdir",
+      "/github/workspace/__repo__",
+      "getsentry/craft:latest",
+      "workspace",
+      "list",
+    ],
+    { encoding: "utf8" }
+  );
+  let workspaceNames;
+ ...
gateway · 2026-09-10 01:55:05
result
diff --git a/src/modules/__tests__/ci-poller-input.js b/src/modules/__tests__/ci-poller-input.js
new file mode 100644
index 0000000..140ccef
--- /dev/null
+++ b/src/modules/__tests__/ci-poller-input.js
@@ -0,0 +1,97 @@
+import { readFileSync } from "fs";
+import { join } from "path";
+
+import { expect, test } from "vitest";
+
+const { getCiPollerInput } = require("../ci-poller-input.js");
+
+const REVISION = "7e5ca7ed5581552de066e2a8bc295b8306be38ac";
+const issueBody = `Requested by: @byk
+
+Merge target: (default)
+
+Quick links:
+- [View changes](https://github.com/getsentry/toolkit/compare/1.2.2...release/1.2.3)
+- [View check runs](https://github.com/getsentry/toolkit/commit/${REVISION}/checks/)`;
+
+test("parses root and full-path publish titles", () => {
+  expect(
+    getCiPollerInput({
+      issueBody,
+      title: "publish: getsentry/toolkit/cli@1.2.3",
+    })
+  ).toEqual({
+    repo: "getsentry/toolkit",
+    revision: REVISION,
+    version: "1.2.3",
+  });
+
+  ...
gateway · 2026-09-10 01:55:18
result
diff --git a/src/publish/__tests__/discover-location.js b/src/publish/__tests__/discover-location.js
new file mode 100644
index 0000000..7b522a6
--- /dev/null
+++ b/src/publish/__tests__/discover-location.js
@@ -0,0 +1,59 @@
+import { afterEach, expect, test, vi } from "vitest";
+
+const { discoverLocation, getWorkspaceNames } = require("../discover-location.js");
+
+afterEach(() => {
+  vi.restoreAllMocks();
+});
+
+test("retains checkout-path behavior when the root config is absent", () => {
+  expect(
+    discoverLocation({
+      input: { path: "./packages/cli" },
+      repositoryDirectory: "__repo__",
+      exists: () => false,
+    })
+  ).toEqual({ path: "./packages/cli" });
+});
+
+test("discovers exact workspace paths using the released Craft image", () => {
+  const execFile = vi.fn(() => '["packages/CLI"]');
+
+  expect(
+    discoverLocation({
+      input: { path: "./packages/CLI" },
+      repositoryDirectory: "__repo__",
+      exists: () => true,
+      execFile,
+  ...
gateway · 2026-09-10 01:55:25
result
diff --git a/src/modules/__tests__/details-from-context.js b/src/modules/__tests__/details-from-context.js
index e6411c5..aa1d199 100644
--- a/src/modules/__tests__/details-from-context.js
+++ b/src/modules/__tests__/details-from-context.js
@@ -1,107 +1,259 @@
 import { test, expect } from "vitest";
 
 const { detailsFromContext } = require("../details-from-context.js");
 
 const inputsArgs = {
   context: {
     repo: { owner: "getsentry", repo: "publish" },
     payload: {
       issue: {
         number: "223",
         title: "publish: getsentry/sentry@21.3.1",
-        body: `
-Requested by: @BYK
+        body: `Requested by: @BYK
 
 Merge target: custom-branch
 
 Quick links:
 - [View changes](https://github.com/getsentry/sentry/compare/21.3.0...refs/heads/releases/21.3.1)
 - [View check runs](https://github.com/getsentry/sentry/commit/7e5ca7ed5581552de066e2a8bc295b8306be38ac/checks/)
 
 Assign the **accepted** label to this issue to approve the release. ...
gateway · 2026-09-10 01:55:34
result
<path>/home/byk/Code/getsentry/publish/src/modules/__tests__/details-from-context.js</path>
<type>file</type>
<content>
1: import { test, expect } from "vitest";
2: 
3: const {
4:   detailsFromContext,
5:   parsePublishTitle,
6: } = require("../details-from-context.js");
7: 
8: const inputsArgs = {
9:   context: {
10:     repo: { owner: "getsentry", repo: "publish" },
11:     payload: {
12:       issue: {
13:         number: "223",
14:         title: "publish: getsentry/sentry@21.3.1",
15:         body: `
16: Requested by: @BYK
17: 
18: Merge target: custom-branch
19: 
20: Quick links:
21: - [View changes](https://github.com/getsentry/sentry/compare/21.3.0...refs/heads/releases/21.3.1)
22: - [View check runs](https://github.com/getsentry/sentry/commit/7e5ca7ed5581552de066e2a8bc295b8306be38ac/checks/)
23: 
24: Assign the **accepted** label to this issue to approve the release. ...
gateway · 2026-09-10 01:55:39
result
<path>/home/byk/Code/getsentry/publish/src/modules/__tests__/update-issue.js</path>
<type>file</type>
<content>
1: import { vi, describe, test, expect, beforeAll, beforeEach, it } from "vitest";
2: import fs from "fs";
3: 
4: const { updateIssue, transformIssueBody } = require("../update-issue.js");
5: 
6: let mockExistsSync;
7: 
8: const updateTargetsArgs = {
9:   inputs: { repo: "sentry", version: "21.3.1" },
10:   context: {
11:     runId: "1234",
12:     repo: { owner: "getsentry", repo: "publish" },
13:     payload: { issue: { number: "211" } },
14:   },
15:   octokit: {
16:     rest: {
17:       actions: {
18:         getWorkflowRun: async () => ({
19:           data: {
20:             html_url: "https://github.com/getsentry/sentry/actions/runs/1234",
21:           },
22:         }),
23:       },
24:       issues: {
25:         get: vi.fn(),
26:         update: vi.fn(),
27:         removeLabel: vi.fn(),
28:       },
29:     },
30:   },
31:   Sentry: {
32:     Scope: class Scope ...
gateway · 2026-09-10 01:55:49
result
diff --git a/.github/workflows/ci-poller.yml b/.github/workflows/ci-poller.yml
index a3db430..487e9da 100644
--- a/.github/workflows/ci-poller.yml
+++ b/.github/workflows/ci-poller.yml
@@ -11,275 +11,285 @@ on:
         default: "0"
 
 permissions:
   contents: read
   issues: read
 
 jobs:
   check-ci:
     runs-on: ubuntu-latest
     environment: production
     # Skip entirely (no runner provisioned) when there's nothing to check.
     # Set to "true" by ci-pending.yml, reset to "false" here when done.
     # Always allow workflow_dispatch for manual recovery.
     if: vars.CI_POLLER_HAS_PENDING == 'true' || github.event_name == 'workflow_dispatch'
     concurrency:
       group: ci-status-poller
       cancel-in-progress: false
     steps:
       # sentry-internal-app token for label changes on this repo.
       # This token is what triggers publish.yml downstream — see
       # https://github.com/getsentry/publish for why GITHUB_TOKEN can't. ...
gateway · 2026-09-10 01:55:55
result
<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:   repository_dispatch:
7:     types: [ci-poller]
8:   workflow_run:
9:     workflows: [Run CI Status Poller]
10:     types: [completed]
11: 
12: permissions:
13:   contents: read
14:   issues: write
15: 
16: jobs:
17:   check-ci:
18:     runs-on: ubuntu-latest
19:     environment: production
20:     # Skip entirely (no runner provisioned) when there's nothing to check.
21:     # Set to "true" by ci-pending.yml, reset to "false" here when done.
22:     # repository_dispatch and a successful main-branch run of the protected
23:     # workflow_dispatch relay are the trusted manual-recovery paths.
24:     if: >-
25:       vars.CI_POLLER_HAS_PENDING == 'true'
26:       || github.event_name == 'repository_dispatch'
27:       || (
28:       github.event_name == 'workflow_run'
29:       && ...
gateway · 2026-09-10 01:56:08
result
<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:     ...
gateway · 2026-09-10 01:56:23
result
<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 ...
gateway · 2026-09-10 01:56:44
result
diff --git a/README.md b/README.md
index b8e07f0..4cbc9b9 100644
--- a/README.md
+++ b/README.md
@@ -1,99 +1,104 @@
 # Sentry Publish 🏠
 
 This is a meta/control repository that implements the [Central Publish Repository](docs/rfc.md) RFC
 
 ## Quick Start
 
 [craft quick start](https://craft.sentry.dev/github-actions/)
 
 ## Release Flow
 
 ```mermaid
 flowchart TD
     A[Developer triggers release workflow] --> B["SDK Repo: craft prepare"]
     B --> C[Build artifacts & create release branch]
     C --> D[Upload artifacts to GitHub]
     D --> E["Create issue in getsentry/publish"]
     E --> F{Release Manager Review}
     F -->|"Add 'accepted' label"| G[Publish workflow triggers]
     G --> I[Download artifacts from GitHub]
     I --> J["craft publish to registries"]
     J --> K{Publish successful?}
     K -->|Yes| L[Issue closed - success]
     K -->|No| M[Issue updated with failure]
 ```
 
 ## Goals
 
 1.  We do not want employees to publish through their own accounts
 1. ...
gateway · 2026-09-10 01:56:51
result
diff --git a/AGENTS.md b/AGENTS.md
index cf473df..51bf3a8 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -236,40 +236,46 @@ expect(result).toMatchInlineSnapshot(`"expected output"`);
 await Promise.all([operation1(), operation2()]);
 ```
 
 ### Destructuring
 
 ```javascript
 const { repo, owner, issueNumber } = context;
 ```
 
 ### Spread Operator for Object Merging
 
 ```javascript
 const fullContext = { ...publishRepo, additionalProp: value };
 ```
 
 ### Template Literals
 
 ```javascript
 const message = `Release ${version} published successfully`;
 ```
 
 ### Regular Expressions with Named Groups
 
 ```javascript
 const PARSER_REGEX = /(?<name>\w+): (?<value>.+)/;
 const match = text.match(PARSER_REGEX);
 const { name, value } = match.groups;
 ```
 
 ## GitHub Actions Context
 
 This codebase interacts heavily with GitHub Actions. ...
gateway · 2026-09-10 01:57:04
result
diff --git a/src/modules/__tests__/ci-poller-workflow.js b/src/modules/__tests__/ci-poller-workflow.js
new file mode 100644
index 0000000..c859f5a
--- /dev/null
+++ b/src/modules/__tests__/ci-poller-workflow.js
@@ -0,0 +1,197 @@
+import {
+  mkdtempSync,
+  mkdirSync,
+  readFileSync,
+  readdirSync,
+  rmSync,
+  writeFileSync,
+} from "fs";
+import { tmpdir } from "os";
+import { join } from "path";
+import { spawnSync } from "child_process";
+
+import { afterEach, expect, test } from "vitest";
+
+const temporaryDirectories = [];
+const INITIAL_REVISION = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
+const UPDATED_REVISION = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
+
+function getPollerScript() {
+  const workflow = readFileSync(
+    join(__dirname, "../../../.github/workflows/ci-poller.yml"),
+    "utf8"
+  );
+  const section = workflow.match(
+    / {6}- name: Check CI status for ci-pending issues[\s\S]*? {8}run: \|\n(?<script>[\s\S]*?)\n {6}- name: Check for remaining pending ...
gateway · 2026-09-10 01:57:13
result
<path>/home/byk/Code/getsentry/publish/src/publish/__tests__/workflow-action-pinning.js</path>
<type>file</type>
<content>
1: import { readFileSync, readdirSync } from "fs";
2: import { describe, expect, test } from "vitest";
3: 
4: const workflowPaths = readdirSync(".github/workflows")
5:   .filter((path) => path.endsWith(".yml"))
6:   .map((path) => `.github/workflows/${path}`);
7: const workflows = workflowPaths.map((path) => ({
8:   path,
9:   contents: readFileSync(path, "utf8"),
10: }));
11: const SECRET_BEARING_JOBS = {
12:   ".github/workflows/auto-approve.yml": ["auto-approve"],
13:   ".github/workflows/ci-poller.yml": ["check-ci"],
14:   ".github/workflows/cocoapods-keepalive.yml": ["keepalive"],
15:   ".github/workflows/publish.yml": ["waiting-for-ci", "publish"],
16: };
17: 
18: function jobContents(workflow, jobName) {
19:   const header = `  ${jobName}:`;
20:   const jobStart = workflow.indexOf(header);
21:   const nextJobOffset = workflow
22:     .slice(jobStart + ...
gateway · 2026-09-10 01:57:18
result
<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>
gateway · 2026-09-10 01:57:23
result
<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: const manualDispatchWorkflow = readFileSync(
6:   ".github/workflows/ci-poller-dispatch.yml",
7:   "utf8"
8: );
9: 
10: describe("CI poller workflow", () => {
11:   test("checks out trusted code before requesting app tokens", () => {
12:     expect(workflow).toMatch(
13:       /- name: Get publish code\n\s+uses: actions\/checkout@[a-f0-9]{40}\n\s+with:\n(?:\s+#.*\n)*\s+ref: \$\{\{ github\.event\.repository\.default_branch \}\}/
14:     );
15: 
16:     expect(workflow.indexOf("Get publish code")).toBeLessThan(
17:       workflow.indexOf("Get auth token")
18:     );
19:   });
20: 
21:   test("uses a protected workflow dispatch relay for manual recovery", () => {
22:     ...
gateway · 2026-09-10 01:57:31
result
Found 16 matches
/home/byk/Code/getsentry/publish/src/publish/__tests__/publish-workflow.js:
  Line 10:     expect(fence).toBeGreaterThan(workflow.indexOf("Set targets"));


/home/byk/Code/getsentry/publish/src/publish/inputs.js:
  Line 3: const { detailsFromContext } = require("../modules/details-from-context");


/home/byk/Code/getsentry/publish/src/modules/approval-authorizer.js:
  Line 2: const { parsePublishTitle } = require("./details-from-context.js");

  Line 16:   const title = parsePublishTitle(issueTitle);

  Line 28:   const title = parsePublishTitle(issueTitle);


/home/byk/Code/getsentry/publish/src/modules/update-issue.js:
  Line 5: } = require("./details-from-context");

  Line 8:   const CRAFT_STATE_FILE_PATH = `${process.env.GITHUB_WORKSPACE}/__repo__/.craft-publish-${version}.json`;

  Line 10:   if (!fs.existsSync(CRAFT_STATE_FILE_PATH)) {

  Line 20:     .readFile(CRAFT_STATE_FILE_PATH, { encoding: "utf-8" ...
gateway · 2026-09-10 01:57:38
result
<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.each(["getsantry[bot]", "sentry-release-bot[bot]"])(
9:     "rejects direct approval by %s without a target repository lookup",
10:     async (actor) => {
11:       const getPermission = vi.fn();
12: 
13:       await expect(
14:         authorizeApproval({
15:           actor,
16:           issueTitle,
17:           getPermission,
18:           autoApprovedRepositories: new Set(["getsentry/sentry-javascript"]),
19:         })
20:       ).resolves.toEqual({
21:         authorized: false,
22:         repository: "getsentry/sentry-javascript",
23:       });
24: 
25:       ...
gateway · 2026-09-10 01:57:51
result
origin/main:.github/workflows/ci-poller.yml:106:              node .__publish__/src/publish/resolve-ci-poller-input.js); then
origin/main:.github/workflows/ci-poller.yml:153:                node .__publish__/src/publish/resolve-ci-poller-input.js > "$resolver_output_file"; then
origin/main:.github/workflows/publish.yml:139:        id: release-revision
origin/main:.github/workflows/publish.yml:143:        run: node .__publish__/src/publish/resolve-release-revision.js
origin/main:.github/workflows/publish.yml:146:        if: steps.inputs.outcome == 'success' && steps.release-revision.outcome == 'success'
origin/main:.github/workflows/publish.yml:164:          ref: ${{ steps.release-revision.outputs.revision }}
origin/main:.github/workflows/publish.yml:174:        run: node .__publish__/src/publish/discover-location.js
origin/main:.github/workflows/publish.yml:240:            exec craft publish ${{ fromJSON(steps.inputs.outputs.result).version }} --rev ${{ ...
gateway · 2026-09-10 01:58:03
result
<path>/home/byk/Code/getsentry/publish/src/publish/update-issue.js</path>
<type>file</type>
<content>
1: const { updateIssue } = require("../modules/update-issue.js");
2: const { getGitHubToken } = require("../libs/github");
3: const github = require("@actions/github");
4: 
5: async function main() {
6:   const context = github.context;
7:   const octokit = github.getOctokit(getGitHubToken());
8:   let inputs;
9: 
10:   if (process.env.PUBLISH_ARGS) {
11:     try {
12:       inputs = JSON.parse(process.env.PUBLISH_ARGS);
13:     } catch {
14:       console.warn("Could not parse publish inputs; skipping target update");
15:     }
16:   }
17: 
18:   await updateIssue({ context, octokit, inputs });
19: }
20: 
21: if (require.main === module) {
22:   main().catch((error) => {
23:     console.error(error);
24:     process.exitCode = 1;
25:   });
26: }
27: 
28: module.exports = { main };

(End of file - total 28 lines)
</content>
gateway · 2026-09-10 01:58:11
result
const { updateIssue } = require('../modules/update-issue.js');
const {getGitHubToken} = require('../libs/github');
const github = require('@actions/github');

const context = github.context;
const octokit = github.getOctokit(getGitHubToken());
const inputs = JSON.parse(process.env.PUBLISH_ARGS);

updateIssue({ context, octokit, inputs });
const { updateIssue } = require('../modules/update-issue.js');
const {getGitHubToken} = require('../libs/github');
const github = require('@actions/github');

const context = github.context;
const octokit = github.getOctokit(getGitHubToken());
const inputs = JSON.parse(process.env.PUBLISH_ARGS);

updateIssue({ context, octokit, inputs });
gateway · 2026-09-10 01:58:29
result
origin/main:src/modules/publish-issue-title.js:198:    PublishIssueTitle: peg$parsePublishIssueTitle,
origin/main:src/modules/publish-issue-title.js:199:    ReleaseRevision: peg$parseReleaseRevision,
origin/main:src/modules/publish-issue-title.js:200:    CheckRunsLinkCount: peg$parseCheckRunsLinkCount,
origin/main:src/modules/publish-issue-title.js:202:  let peg$startRuleFunction = peg$parsePublishIssueTitle;
origin/main:src/modules/publish-issue-title.js:503:  function peg$parsePublishIssueTitle() {
origin/main:src/modules/publish-issue-title.js:760:  function peg$parseReleaseRevision() {
origin/main:src/modules/publish-issue-title.js:1105:  function peg$parseCheckRunsLinkCount() {
origin/main:src/modules/publish-issue-title.js:1469:module.exports = {
origin/main:src/modules/publish-issue-title.js:1470:  StartRules: ["PublishIssueTitle", "ReleaseRevision", "CheckRunsLinkCount"],
gateway · 2026-09-10 01:58:36
result
diff --git a/- b/-
new file mode 100644
--- /dev/null
+++ b/-
@@ -0,0 +1,1473 @@
+/* eslint-disable */
+// @generated by Peggy 5.1.0.
+//
+// https://peggyjs.org/
+
+"use strict";
+
+class peg$SyntaxError extends SyntaxError {
+  constructor(message, expected, found, location) {
+    super(message);
+    this.expected = expected;
+    this.found = found;
+    this.location = location;
+    this.name = "SyntaxError";
+  }
+
+  format(sources) {
+    let str = "Error: " + this.message;
+    if (this.location) {
+      let src = null;
+      const st = sources.find((s) => s.source === this.location.source);
+      if (st) {
+        src = st.text.split(/\r\n|\n|\r/g);
+      }
+      const s = this.location.start;
+      const offset_s =
+        this.location.source &&
+        typeof this.location.source.offset === "function"
+          ? this.location.source.offset(s)
+          : s;
+      const loc =
+        this.location.source + ":" + offset_s.line + ":" + offset_s.column;
+      ...
gateway · 2026-09-10 01:58:49
result
Checking patch .github/workflows/ci-poller.yml...
Hunk #1 succeeded at 74 (offset 26 lines).
error: while searching for:

          # Check each issue's CI status
          echo "$issues" | jq -c '.[]' | while read -r issue; do
            number=$(echo "$issue" | jq -r '.number')
            title=$(echo "$issue" | jq -r '.title')
            body=$(echo "$issue" | jq -r '.body')

            # Parse repo and version from title: "publish: owner/repo[/path]@version"
            # Only take owner/repo (first two segments) — monorepos like
            # "getsentry/relay/py@0.9.26" have a path suffix that isn't part
            # of the GitHub repo name.
            repo=$(echo "$title" | sed -n 's|^publish: \([^/]*/[^/@]*\).*@.*|\1|p')
            version=$(echo "$title" | sed -n 's/^publish: .*@\(.*\)/\1/p')

            if [[ -z "$repo" || -z "$version" ]]; then
              echo "::warning::Could not parse repo/version from issue #${number}: ${title}"
              continue
         ...
gateway · 2026-09-10 01:58:56
result
<path>/home/byk/Code/getsentry/publish/.github/workflows/cocoapods-keepalive.yml</path>
<type>file</type>
<content>
1: name: CocoaPods Token Keep-Alive
2: 
3: on:
4:   schedule:
5:     # CocoaPods sessions currently expire after 3 days of inactivity (VALIDITY_LENGTH)
6:     # This runs every day at midnight UTC to keep the current token alive
7:     # See: https://github.com/CocoaPods/trunk.cocoapods.org/blob/a1869790e9ae2229b6985b6af4532da814202558/app/models/session.rb#L9
8:     - cron: "0 0 * * *" # Runs every day at midnight UTC
9: 
10: permissions:
11:   contents: read
12: 
13: jobs:
14:   keepalive:
15:     name: Refresh CocoaPods Session
16:     runs-on: macos-15
17:     environment: production
18:     env:
19:       COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }}
20: 
21:     steps:
22:       - name: Install CocoaPods
23:         run: gem install cocoapods
24:       - name: Print CocoaPods Version
25:         run: pod --version
26:       - name: Refresh CocoaPods ...
gateway · 2026-09-10 01:59:18
result
<path>/home/byk/Code/getsentry/publish/.github/workflows/ci-poller.yml</path>
<type>file</type>
<content>
431:             echo "All ci-pending issues resolved. Disabling poller."
432:             gh variable set CI_POLLER_HAS_PENDING -R "$GITHUB_REPOSITORY" -b "false"
433:           else
434:             echo "Still pending issues. Ensuring poller stays enabled."
435:             gh variable set CI_POLLER_HAS_PENDING -R "$GITHUB_REPOSITORY" -b "true"
436:           fi
437: 
438:       # Self-dispatch for fast re-checking when issues are still pending.
439:       # GitHub's cron is unreliable (*/5 can drift to 30-40 min under load).
440:       # Self-dispatch gives ~30-60s between checks via GHA startup latency.
441:       # The concurrency group prevents accumulation (1 running + 1 queued).
442:       # Cap at 60 attempts (~30 min). ...
gateway · 2026-09-10 02:01:09
result
Found 4 matches
/home/byk/Code/getsentry/publish/src/publish/post-result.js:
  Line 7: const inputs = JSON.parse(process.env.PUBLISH_ARGS);


/home/byk/Code/getsentry/publish/src/publish/inputs.js:
  Line 9:   core.setOutput("result", result);


/home/byk/Code/getsentry/publish/src/publish/update-issue.js:
  Line 10:   if (process.env.PUBLISH_ARGS) {

  Line 12:       inputs = JSON.parse(process.env.PUBLISH_ARGS);
gateway · 2026-09-10 02:01:16

Your module analysis returned empty. READ ONLY and return NON-EMPTY integration guidance now. Compare current worktree, HEAD, and origin/main commit 7c60ddb for package.json, yarn.lock, src/modules/details-from-context.js, src/modules/update-issue.js, src/publish/inputs.js, every newly added parser/location/revision/ci-input file and its tests, scripts/generate-publish-issue-title-parser.js, and docs/publish-issue-format.md. Provide: (1) dependency/order map, (2) exact upstream behavior per file with line evidence, (3) conflicts with current security changes, (4) safest integration sequence, and (5) tests that must remain/add. If blocked, state exact tool/error. Never edit files and never return empty.

gateway · 2026-09-10 02:02:25
result
<path>/home/byk/Code/getsentry/publish/package.json</path>
<type>file</type>
<content>
1: {
2:   "name": "publish",
3:   "private": true,
4:   "version": "0.0.1",
5:   "description": "Approval-based publishing system for Sentry",
6:   "main": "index.js",
7:   "repository": "git@github.com:getsentry/publish.git",
8:   "author": "Sentry Open Source <oss@sentry.io>",
9:   "license": "Apache-2.0",
10:   "volta": {
11:     "node": "24.0.0",
12:     "yarn": "1.22.22"
13:   },
14:   "resolutions": {
15:     "undici": "^6.23.0"
16:   },
17:   "scripts": {
18:     "test": "vitest run",
19:     "test:watch": "vitest",
20:     "lint": "eslint src .github --ignore-pattern '!.github'",
21:     "prettier": "prettier --write src"
22:   },
23:   "devDependencies": {
24:     "eslint": "^8.9.0",
25:     "eslint-config-prettier": "^8.3.0",
26:     "eslint-plugin-yml": "^0.13.0",
27:     "prettier": "^2.2.1",
28:     "vitest": "^4.1.0"
29:   },
30:   "dependencies": {
31:     "@actions/core": ...
gateway · 2026-09-10 02:02:33
result
origin/main:src/modules/ci-poller-input.js:1:const { parse: parsePublishIssueTitle } = require("./publish-issue-title");
origin/main:src/modules/ci-poller-input.js:2:const {
origin/main:src/modules/ci-poller-input.js:3:  getReleaseRevision,
origin/main:src/modules/ci-poller-input.js:4:  updateReleaseRevision,
origin/main:src/modules/ci-poller-input.js:5:} = require("./release-revision");
origin/main:src/modules/ci-poller-input.js:6:const {
origin/main:src/modules/ci-poller-input.js:7:  isPublishRepository,
origin/main:src/modules/ci-poller-input.js:8:  isReleaseVersion,
origin/main:src/modules/ci-poller-input.js:9:} = require("./publish-issue-validation");
origin/main:src/modules/ci-poller-input.js:10:const { isPublishPath } = require("./publish-location");
origin/main:src/modules/ci-poller-input.js:11:
origin/main:src/modules/ci-poller-input.js:12:function getCiPollerInput({ title, issueBody, revision }) {
origin/main:src/modules/ci-poller-input.js:13:  let ...
gateway · 2026-09-10 02:02:42
result
origin/main:docs/publish-issue-format.md:1:# Publish Issue Format
origin/main:docs/publish-issue-format.md:2:
origin/main:docs/publish-issue-format.md:3:The Publish workflow treats a publish issue as a release request. The title identifies
origin/main:docs/publish-issue-format.md:4:the release; the body supplies the merge target and selected targets. Do not edit the
origin/main:docs/publish-issue-format.md:5:title by hand unless it remains valid under this format.
origin/main:docs/publish-issue-format.md:6:
origin/main:docs/publish-issue-format.md:7:## Title
origin/main:docs/publish-issue-format.md:8:
origin/main:docs/publish-issue-format.md:9:Every title starts with `publish: `. The following canonical Peggy grammar is
origin/main:docs/publish-issue-format.md:10:generated from `src/modules/publish-issue-title.peggy`:
origin/main:docs/publish-issue-format.md:11:
origin/main:docs/publish-issue-format.md:12:<!-- BEGIN GENERATED TITLE GRAMMAR ...
gateway · 2026-09-10 02:02:49
result
origin/main:src/publish/discover-location.js:1:const { execFileSync } = require("child_process");
origin/main:src/publish/discover-location.js:2:const { existsSync } = require("fs");
origin/main:src/publish/discover-location.js:3:const path = require("path");
origin/main:src/publish/discover-location.js:4:
origin/main:src/publish/discover-location.js:5:const core = require("@actions/core");
origin/main:src/publish/discover-location.js:6:
origin/main:src/publish/discover-location.js:7:const { resolvePublishLocation } = require("../modules/publish-location");
origin/main:src/publish/discover-location.js:8:
origin/main:src/publish/discover-location.js:9:function getWorkspaceNames({
origin/main:src/publish/discover-location.js:10:  repositoryDirectory,
origin/main:src/publish/discover-location.js:11:  exists = existsSync,
origin/main:src/publish/discover-location.js:12:  execFile = execFileSync,
origin/main:src/publish/discover-location.js:13:}) ...
gateway · 2026-09-10 02:03:05
result
origin/main:src/modules/__tests__/ci-poller-input.js:17:test("parses root and full-path publish titles", () => {
origin/main:src/modules/__tests__/ci-poller-input.js:41:test("uses the shared resolver in the CI poller", () => {
origin/main:src/modules/__tests__/ci-poller-input.js:55:test.each(["-toolkit", ".", "..", "__proto__"])(
origin/main:src/modules/__tests__/ci-poller-input.js:67:test.each(["--config", "1.2"])(
origin/main:src/modules/__tests__/ci-poller-input.js:79:test.each(["publish: getsentry/@1.2.3", "publish: getsentry/toolkit$@1.2.3"])(
origin/main:src/modules/__tests__/ci-poller-input.js:88:test.each([
origin/main:src/modules/__tests__/ci-poller-workflow.js:152:test("skips an issue without editing it when the poller resolver fails", () => {
origin/main:src/modules/__tests__/ci-poller-workflow.js:160:test("skips an issue without editing it when the rewrite response has no body", () => {
origin/main:src/modules/__tests__/ci-poller-workflow.js:168:test("skips an issue ...
gateway · 2026-09-10 02:03:15
result
origin/main:src/modules/publish-issue-title.js:2:// @generated by Peggy 5.1.0.
origin/main:src/modules/publish-issue-title.js:197:  const peg$startRuleFunctions = {
origin/main:src/modules/publish-issue-title.js:337:    if (!(options.startRule in peg$startRuleFunctions)) {
origin/main:src/modules/publish-issue-title.js:343:    peg$startRuleFunction = peg$startRuleFunctions[options.startRule];
origin/main:src/modules/publish-issue-title.js:1469:module.exports = {
origin/main:src/modules/publish-issue-title.js:1470:  StartRules: ["PublishIssueTitle", "ReleaseRevision", "CheckRunsLinkCount"],
origin/main:src/modules/publish-issue-title.js:1471:  SyntaxError: peg$SyntaxError,
origin/main:src/modules/publish-issue-title.js:1472:  parse: peg$parse,
origin/main:yarn.lock:538:"@peggyjs/from-mem@3.1.3":
origin/main:yarn.lock:954:commander@^14.0.3:
origin/main:yarn.lock:1538:peggy@5.1.0:
origin/main:yarn.lock:1685:semver@7.7.4:
origin/main:yarn.lock:1707:source-map-generator@2.0.6:
gateway · 2026-09-10 02:03:49
result
No files found
gateway · 2026-09-10 02:03:55
result
Found 3 matches
/home/byk/Code/getsentry/publish/src/publish/update-issue.js:
  Line 14:       console.warn("Could not parse publish inputs; skipping target update");


/home/byk/Code/getsentry/publish/src/modules/__tests__/update-issue.js:
  Line 120:   test("remove label", async () => {

  Line 132: test("removes accepted without parsed publish inputs", async () => {
gateway · 2026-09-10 02:04:10
result
origin/main:.github/workflows/ci-poller.yml:51:      - name: Check out publish controller
origin/main:.github/workflows/ci-poller.yml:52:        uses: actions/checkout@v7
origin/main:.github/workflows/ci-poller.yml:99:            body_file=$(mktemp)
origin/main:.github/workflows/ci-poller.yml:101:            updated_body_file=""
origin/main:.github/workflows/ci-poller.yml:102:            trap 'for file in "${body_file:-}" "${resolver_output_file:-}" "${updated_body_file:-}"; do [[ -z "$file" ]] || rm -f "$file"; done' EXIT
origin/main:.github/workflows/ci-poller.yml:103:            printf '%s' "$issue" | jq -jr '.body' > "$body_file"
origin/main:.github/workflows/ci-poller.yml:105:            if ! publish_input=$(PUBLISH_ISSUE_BODY_FILE="$body_file" PUBLISH_TITLE="$title" \
origin/main:.github/workflows/ci-poller.yml:106:              node .__publish__/src/publish/resolve-ci-poller-input.js); then
origin/main:.github/workflows/ci-poller.yml:151:              ...
gateway · 2026-09-10 02:04:22
result
## main...origin/main [behind 1]
 M .github/workflows/auto-approve.yml
 M .github/workflows/ci-poller.yml
 M .github/workflows/cocoapods-keepalive.yml
 M .github/workflows/publish.yml
 M .github/workflows/test.yml
 M AGENTS.md
 M README.md
 M docs/key_rotation_vault_changes.md
 M docs/rfc.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
?? .github/workflows/ci-poller-dispatch.yml
?? .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__/
?? ...
gateway · 2026-09-10 02:06:12
result
<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: ...
gateway · 2026-09-10 02:07:28
result
origin/main:README.md:42:## Publish Issue Format
origin/main:README.md:45:[Publish Issue Format](docs/publish-issue-format.md) for the accepted syntax and fields.
README.md:97:This repo is read-only for everyone except release managers. Secret-bearing jobs use the protected `production` environment, which permits only `main` deployments and does not allow administrator bypass. This protects environment-scoped credentials from arbitrary workflow refs. See getsentry/sentry#21930 for an example.
README.md:99:`SENTRY_INTERNAL_APP_PRIVATE_KEY` and `SENTRY_RELEASE_BOT_PRIVATE_KEY` are organization-level secrets. Their availability to other repositories is an organization-wide security concern that this repository cannot narrow; Security must manage that boundary separately.
gateway · 2026-09-10 02:10:23

No blocker. I made no edits and ran no tests.

  • HEAD: 83d210b29553ea6f4d97508821724a834c43856e
  • origin/main: 7c60ddb7f43040fe8fbfea70efc833f689c04e75
  • HEAD is the merge base, so origin/main is exactly one commit ahead.
  • A read-only git apply --check found direct conflicts in both workflows, cocoapods-keepalive.yml, details-from-context.js, update-issue.js, and inputs.js. Package, lock, docs, and new files apply textually, but several tests conflict semantically with the security work.

1. Dependency and order map

package.json + yarn.lock
  └─ peggy 5.1.0
     └─ scripts/generate-publish-issue-title-parser.js
        ├─ src/modules/publish-issue-title.peggy
        ├─ src/modules/publish-issue-title.js      generated output
        └─ docs/publish-issue-format.md            generated title section

publish-issue-title.js
  ├─ release-revision.js
  │  ├─ details-from-context.js
  │  └─ resolve-release-revision.js
  └─ ci-poller-input.js
     └─ resolve-ci-poller-input.js

publish-issue-validation.js + publish-location.js
  ├─ details-from-context.js
  ├─ ci-poller-input.js
  ├─ discover-location.js
  └─ resolve-location.js

details-from-context.js target regexes
  └─ update-issue.js

publish workflow:
inputs → revision → exact-SHA checkout → workspace discovery
       → state-file output → Craft → failure cleanup

security-only current dependency:
approval-authorizer.js → details-from-context.js::parsePublishTitle

That last dependency breaks if upstream details-from-context.js replaces the current file unchanged.

2. Exact upstream behavior

Parser/build set

  • /home/byk/Code/getsentry/publish/package.json
    Adds generate, check:generated, makes every yarn test run the generated-file check, and pins Peggy 5.1.0 (origin/main:18-30).

  • /home/byk/Code/getsentry/publish/yarn.lock
    Adds Peggy and only its new dependency chain: @peggyjs/from-mem, commander, semver, and source-map-generator ...

gpt-5.6-sol · 2026-09-10 02:10:23 · ~3.7K tokens