Dashboard › publish › Session 1MdXRLzzwzI6
1MdXRLzzwzI66jNKDPerform a read-only adversarial correctness/security audit of the current uncommitted tree in /home/byk/Code/getsentry/publish, scoped to the release input, revision, target checkout, publish location, Craft invocation, and state-file identity paths. Inspect relevant workflows, source, and tests. Do not edit files. Look for mismatches between immutable event state, live state, parsed inputs, checked-out repo/revision, discovered path/workspace, state key, and actual Craft arguments/environment; shell/expression injection; path traversal; mutable references; credentials exposure. The snapshot-digest hardening currently being implemented is intentional. Return only substantive findings ordered by severity with exact file:line evidence, exploit/failure sequence, and a concrete fix; if none, state PASS and list residual test gaps. End exactly MERGE or DO-NOT-MERGE.
<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 scripts/ src/ vitest.config.js yarn.lock (16 entries) </entries>
[
{
"content": "Identify every changed and untracked file in the exact worktree",
"status": "in_progress",
"priority": "high"
},
{
"content": "Trace release input through revision, checkout, path/workspace, state identity, and Craft execution",
"status": "pending",
"priority": "high"
},
{
"content": "Inspect adversarial regression coverage and run read-only validation",
"status": "pending",
"priority": "high"
},
{
"content": "Verify the worktree snapshot remained unchanged and issue a binary verdict",
"status": "pending",
"priority": "high"
}
]
MM .github/workflows/auto-approve.yml MM .github/workflows/ci-poller.yml M .github/workflows/cocoapods-keepalive.yml MM .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__/ci-poller-input.js M src/modules/__tests__/ci-poller-workflow.js MM src/modules/__tests__/details-from-context.js M src/modules/__tests__/publish-location.js M src/modules/__tests__/publish-workflow.js M src/modules/__tests__/release-revision.js M src/modules/__tests__/update-issue.js M src/modules/ci-poller-input.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/publish-location.js M src/modules/release-revision.js M src/publish/__tests__/discover-location.js M src/publish/__tests__/resolve-release-revision.js M src/publish/discover-location.js M ...
.github/workflows/auto-approve.yml | 30 +++- .github/workflows/ci-poller.yml | 202 +++++++++++++++++++--- .github/workflows/cocoapods-keepalive.yml | 1 - .github/workflows/publish.yml | 152 +++++++++++++++- .github/workflows/test.yml | 6 +- AGENTS.md | 6 + README.md | 4 +- docs/key_rotation_vault_changes.md | 40 ++--- docs/rfc.md | 9 +- src/libs/__tests__/github.js | 2 +- src/libs/github.js | 6 +- src/modules/__tests__/ci-poller-input.js | 22 ++- src/modules/__tests__/ci-poller-workflow.js | 93 ++++++---- src/modules/__tests__/details-from-context.js | 22 ++- src/modules/__tests__/publish-location.js | 19 +- src/modules/__tests__/publish-workflow.js | ...
Your previous review returned an empty result, which is invalid. Resume the read-only adversarial audit of the exact unchanged tree in /home/byk/Code/getsentry/publish, scoped to release input, revision, target checkout, publish location, Craft invocation, and state-file identity. You MUST return a non-empty result. Inspect every relevant changed file and tests. For each area classify PASS, CONCERN, or MUST-FIX with exact current file:line evidence and reasoning. Any finding must include a concrete exploit/failure sequence and fix. If tool access blocks you, begin BLOCKED and name the exact tool/error. Do not edit files. End exactly MERGE or DO-NOT-MERGE.
<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 ...
Found 100 matches (more matches available)
/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);
/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/resolve-release-revision.js:
Line 2: const { getReleaseRevision } = require("../modules/release-revision");
Line 5: const { repo } = JSON.parse(process.env.PUBLISH_ARGS || "{}");
/home/byk/Code/getsentry/publish/src/publish/resolve-location.js:
Line 5: const input = JSON.parse(process.env.PUBLISH_ARGS || "{}");
Line 6: const workspaceNames = JSON.parse(process.env.CRAFT_WORKSPACE_NAMES || "[]");
Line 8: if (!Array.isArray(workspaceNames)) {
Line 10: "Craft workspace discovery returned an invalid workspace list."
Line 19: ...
<path>/home/byk/Code/getsentry/publish/src/modules/publish-issue-validation.js</path>
<type>file</type>
<content>
1: function isPublishRepository(name) {
2: return (
3: /^[A-Za-z0-9_.-]+$/.test(name) &&
4: name !== "." &&
5: name !== ".." &&
6: name !== "__proto__" &&
7: !name.startsWith("-")
8: );
9: }
10:
11: function isReleaseVersion(version) {
12: return /^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(?:-?([0-9a-z-]+(?:\.[0-9a-z-]+)*))?(?:\+([0-9a-z-]+(?:\.[0-9a-z-]+)*))?$/i.test(
13: version
14: );
15: }
16:
17: module.exports = { isPublishRepository, isReleaseVersion };
(End of file - total 17 lines)
</content>
<path>/home/byk/Code/getsentry/publish/src/modules/details-from-context.js</path>
<type>file</type>
<content>
1: const { parse: parsePublishIssueTitle } = require("./publish-issue-title");
2: const { getReleaseRevisionDetails } = require("./release-revision");
3: const { isPublishPath } = require("./publish-location");
4: const {
5: isPublishRepository,
6: isReleaseVersion,
7: } = require("./publish-issue-validation");
8:
9: /**
10: * Matches the entire "Targets" section of a github publish issue body.
11: */
12: const TARGETS_SECTION_PARSER_REGEX =
13: /^(?!### Targets$\s)(?: *- \[[ xX]\] \S+\s*$(?:\r?\n)?)+/m;
14:
15: /**
16: * Matches all targets of a github publish issue body in a section that was already matched and extracted with `TARGETS_PARSER_REGEX`.
17: * The "id" of the targets is captured within a capture group. ...
<path>/home/byk/Code/getsentry/publish/src/modules/publish-location.js</path>
<type>file</type>
<content>
1: function resolvePublishLocation({ path, workspaceNames }) {
2: if (!isPublishPath(path)) {
3: throw new Error("Invalid publish path.");
4: }
5:
6: if (!workspaceNames.every(isWorkspaceName)) {
7: throw new Error(
8: "Craft workspace discovery returned an invalid workspace list."
9: );
10: }
11:
12: if (path === ".") {
13: return { path };
14: }
15:
16: const workspace = path.slice(2);
17: if (workspaceNames.includes(workspace)) {
18: return { path: ".", workspace };
19: }
20:
21: return { path };
22: }
23:
24: function isPublishPath(path) {
25: return (
26: typeof path === "string" &&
27: (path === "." ||
28: (path.startsWith("./") &&
29: path.slice(2).split("/").every(isSafeWorkspaceSegment)))
30: );
31: }
32:
33: function isWorkspaceName(name) {
34: return (
35: typeof name === "string" && ...
<path>/home/byk/Code/getsentry/publish/src/modules/release-revision.js</path>
<type>file</type>
<content>
1: const { parse } = require("./publish-issue-title");
2:
3: function isRevision(revision) {
4: return /^[0-9a-f]{40}$/.test(revision);
5: }
6:
7: function getReleaseRevisionDetails({ issueBody, repo }) {
8: if (parse(issueBody, { startRule: "CheckRunsLinkCount" }) !== 1) {
9: throw new Error(
10: `Expected exactly one View check runs link in Quick links for getsentry/${repo}.`
11: );
12: }
13:
14: let details;
15: try {
16: details = parse(issueBody, { startRule: "ReleaseRevision" });
17: } catch {
18: throw new Error(
19: `Expected a View check runs link for getsentry/${repo} in the publish issue body.`
20: );
21: }
22:
23: if (details.repo !== repo) {
24: throw new Error(
25: `Expected a View check runs link for getsentry/${repo} in the publish issue body.`
26: );
27: }
28:
29: return details;
30: }
31:
32: ...
<path>/home/byk/Code/getsentry/publish/src/modules/ci-poller-input.js</path>
<type>file</type>
<content>
1: const { parse: parsePublishIssueTitle } = require("./publish-issue-title");
2: const {
3: getReleaseRevision,
4: updateReleaseRevision,
5: } = require("./release-revision");
6: const {
7: isPublishRepository,
8: isReleaseVersion,
9: } = require("./publish-issue-validation");
10: const { isPublishPath } = require("./publish-location");
11: const { requestDigest } = require("./approval-attestation");
12:
13: function getCiPollerInput({ title, issueBody, labels, revision }) {
14: let parsedTitle;
15: try {
16: parsedTitle = parsePublishIssueTitle(title);
17: } catch {
18: throw new Error(`Invalid publish issue title: '${title}'`);
19: }
20: const { repo, version } = parsedTitle;
21: if (!isPublishRepository(repo)) {
22: throw new Error(`Invalid publish issue repository: '${repo}'`);
23: }
24: if (!isReleaseVersion(version)) {
25: throw new ...
<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: && ...
Found 12 matches
/home/byk/Code/getsentry/publish/src/modules/publish-issue-title.js:
Line 199: ReleaseRevision: peg$parseReleaseRevision,
Line 200: CheckRunsLinkCount: peg$parseCheckRunsLinkCount,
Line 305: function peg$f7(mergeTarget, repo, revision) {
Line 306: return { repo, mergeTarget, revision };
Line 760: function peg$parseReleaseRevision() {
Line 922: s23 = peg$parseCheckRunsRevision();
Line 1046: function peg$parseCheckRunsRevision() {
Line 1105: function peg$parseCheckRunsLinkCount() {
Line 1110: s2 = peg$parseCheckRunsLink();
Line 1130: s2 = peg$parseCheckRunsLink();
Line 1178: function peg$parseCheckRunsLink() {
Line 1470: StartRules: ["PublishIssueTitle", "ReleaseRevision", "CheckRunsLinkCount"],
<path>/home/byk/Code/getsentry/publish/src/modules/publish-issue-title.js</path>
<type>file</type>
<content>
260: ["a", "f"],
261: ],
262: false,
263: false,
264: false
265: );
266: const peg$e16 = peg$literalExpectation("- [View check runs](", false);
267: const peg$e17 = peg$classExpectation([" ", "\t"], false, false, false);
268: const peg$e18 = peg$literalExpectation("\r\n", false);
269: const peg$e19 = peg$literalExpectation("\n", false);
270: const peg$e20 = peg$classExpectation(["\r", "\n"], true, false, false);
271: const peg$e21 = peg$literalExpectation("(default)", false);
272: const peg$e22 = peg$classExpectation(
273: [["A", "Z"], ["a", "z"], ["0", "9"], "_", ".", "/", "-"],
274: false,
275: false,
276: false
277: );
278: const peg$e23 = peg$classExpectation(["\r", "\n", ")"], true, false, false);
279:
280: function peg$f0(repo, path, version) {
281: return {
282: repo,
283: path: path || "",
284: ...
<path>/home/byk/Code/getsentry/publish/src/modules/publish-issue-title.js</path>
<type>file</type>
<content>
740: peg$currPos++;
741: } else {
742: s2 = peg$FAILED;
743: if (peg$silentFails === 0) {
744: peg$fail(peg$e6);
745: }
746: }
747: }
748: } else {
749: s1 = peg$FAILED;
750: }
751: if (s1 !== peg$FAILED) {
752: peg$savedPos = s0;
753: s1 = peg$f5(s1);
754: }
755: s0 = s1;
756:
757: return s0;
758: }
759:
760: function peg$parseReleaseRevision() {
761: let s0, s1, s2, s3;
762:
763: s0 = peg$currPos;
764: s1 = peg$parseRequestHeader();
765: if (s1 !== peg$FAILED) {
766: s2 = [];
767: if (input.length > peg$currPos) {
768: s3 = input.charAt(peg$currPos);
769: peg$currPos++;
770: } else {
771: s3 = peg$FAILED;
772: if (peg$silentFails === 0) {
773: peg$fail(peg$e3);
774: }
775: ...
<path>/home/byk/Code/getsentry/publish/src/modules/publish-issue-title.js</path>
<type>file</type>
<content>
180:
181: return (
182: "Expected " +
183: describeExpected(expected) +
184: " but " +
185: describeFound(found) +
186: " found."
187: );
188: }
189: }
190:
191: function peg$parse(input, options) {
192: options = options !== undefined ? options : {};
193:
194: const peg$FAILED = {};
195: const peg$source = options.grammarSource;
196:
197: const peg$startRuleFunctions = {
198: PublishIssueTitle: peg$parsePublishIssueTitle,
199: ReleaseRevision: peg$parseReleaseRevision,
200: CheckRunsLinkCount: peg$parseCheckRunsLinkCount,
201: };
202: let peg$startRuleFunction = peg$parsePublishIssueTitle;
203:
204: const peg$c0 = "publish: ";
205: const peg$c1 = "getsentry/";
206: const peg$c2 = "@";
207: const peg$c3 = "/";
208: const peg$c4 = "Requested by: @";
209: const peg$c5 = "Merge target: ";
210: const ...
/home/byk/Code/getsentry/publish/scripts/generate-publish-issue-title-parser.js /home/byk/Code/getsentry/publish/src/modules/publish-issue-title.peggy /home/byk/Code/getsentry/publish/src/modules/publish-issue-title.js /home/byk/Code/getsentry/publish/src/modules/__tests__/generate-publish-issue-title-parser.js
<path>/home/byk/Code/getsentry/publish/src/publish/inputs.js</path>
<type>file</type>
<content>
1: const github = require("@actions/github");
2: const core = require("@actions/core");
3: const { detailsFromContext } = require("../modules/details-from-context");
4:
5: async function inputs() {
6: const result = await detailsFromContext({
7: context: github.context,
8: });
9: core.setOutput("result", JSON.stringify(result));
10: }
11: inputs();
(End of file - total 11 lines)
</content>
<path>/home/byk/Code/getsentry/publish/src/publish/resolve-release-revision.js</path>
<type>file</type>
<content>
1: const core = require("@actions/core");
2: const { getReleaseRevision } = require("../modules/release-revision");
3:
4: function resolveReleaseRevision() {
5: const { repo } = JSON.parse(process.env.PUBLISH_ARGS || "{}");
6: if (!repo) {
7: throw new Error("Publish input must define a repository.");
8: }
9:
10: core.setOutput(
11: "revision",
12: getReleaseRevision({
13: issueBody: process.env.PUBLISH_ISSUE_BODY || "",
14: repo,
15: })
16: );
17: }
18:
19: resolveReleaseRevision();
(End of file - total 19 lines)
</content>
<path>/home/byk/Code/getsentry/publish/src/publish/discover-location.js</path>
<type>file</type>
<content>
1: const { execFileSync } = require("child_process");
2: const { existsSync } = require("fs");
3: const path = require("path");
4:
5: const core = require("@actions/core");
6:
7: const { resolvePublishLocation } = require("../modules/publish-location");
8:
9: const CRAFT_IMAGE =
10: "getsentry/craft@sha256:9a4a5d5efa44a00c2215078ead39800d4aaa5a97908b94f45a64d7d506d6e14b";
11:
12: function getWorkspaceNames({
13: repositoryDirectory,
14: exists = existsSync,
15: execFile = execFileSync,
16: }) {
17: if (!exists(path.join(repositoryDirectory, ".craft.yml"))) {
18: return [];
19: }
20:
21: const output = execFile(
22: "docker",
23: [
24: "run",
25: "--rm",
26: "--volume",
27: `${path.resolve(repositoryDirectory)}:/github/workspace/__repo__`,
28: "--workdir",
29: "/github/workspace/__repo__",
30: ...
<path>/home/byk/Code/getsentry/publish/src/publish/resolve-ci-poller-input.js</path>
<type>file</type>
<content>
1: const { getCiPollerInput } = require("../modules/ci-poller-input");
2: const { readFileSync } = require("fs");
3:
4: const issueBody = process.env.PUBLISH_ISSUE_BODY_FILE
5: ? readFileSync(process.env.PUBLISH_ISSUE_BODY_FILE, "utf8")
6: : process.env.PUBLISH_ISSUE_BODY || "";
7:
8: process.stdout.write(
9: JSON.stringify(
10: getCiPollerInput({
11: issueBody,
12: labels: JSON.parse(process.env.PUBLISH_ISSUE_LABELS || ""),
13: title: process.env.PUBLISH_TITLE || "",
14: revision: process.env.PUBLISH_REVISION || "",
15: })
16: )
17: );
(End of file - total 17 lines)
</content>
<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>
<path>/home/byk/Code/getsentry/publish/src/modules/update-issue.js</path>
<type>file</type>
<content>
1: const fs = require("fs");
2: const {
3: TARGETS_SECTION_PARSER_REGEX,
4: TARGETS_PARSER_REGEX,
5: } = require("./details-from-context");
6:
7: async function updateTargets({
8: octokit,
9: stateFilePath,
10: publishRepo,
11: issue_number,
12: }) {
13: if (!stateFilePath || !fs.existsSync(stateFilePath)) {
14: return;
15: }
16:
17: const issueRequest = octokit.rest.issues.get({
18: ...publishRepo,
19: issue_number,
20: });
21:
22: const craftStateRequest = fs.promises
23: .readFile(stateFilePath, { encoding: "utf-8" })
24: .then((data) => JSON.parse(data));
25:
26: const [{ data: issue }, craftState] = await Promise.all([
27: issueRequest,
28: craftStateRequest,
29: ]);
30:
31: const newIssueBody = transformIssueBody(craftState, issue.body);
32:
33: await octokit.rest.issues.update({
34: ...publishRepo,
35: ...
<path>/home/byk/Code/getsentry/publish/src/modules/__tests__/publish-workflow.js</path>
<type>file</type>
<content>
1: import { spawnSync } from "child_process";
2: import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "fs";
3: import { tmpdir } from "os";
4: import { join } from "path";
5:
6: import { afterEach, expect, test } from "vitest";
7:
8: const { resolvePublishLocation } = require("../publish-location.js");
9:
10: const temporaryDirectories = [];
11:
12: function getWorkflow() {
13: return readFileSync(
14: join(__dirname, "../../../.github/workflows/publish.yml"),
15: "utf8"
16: );
17: }
18:
19: function getSetTargetsScript() {
20: const workflow = getWorkflow();
21: const section = workflow.match(
22: / {6}- name: Set targets[\s\S]*? {8}run: \|\n(?<script>[\s\S]*?)\n {6}- name: Revalidate approval attestation/
23: );
24: if (!section?.groups?.script) {
25: throw new Error("Missing Set targets workflow script");
26: }
27: ...
<path>/home/byk/Code/getsentry/publish/src/modules/__tests__/publish-location.js</path>
<type>file</type>
<content>
1: import { expect, test } from "vitest";
2:
3: const { resolvePublishLocation } = require("../publish-location.js");
4:
5: test("classifies an exact full workspace path without normalizing it", () => {
6: expect(
7: resolvePublishLocation({
8: path: "./packages/CLI",
9: workspaceNames: ["packages/cli", "packages/CLI"],
10: })
11: ).toStrictEqual({ path: ".", workspace: "packages/CLI" });
12: });
13:
14: test("keeps a non-workspace suffix as a checkout path", () => {
15: expect(
16: resolvePublishLocation({
17: path: "./packages",
18: workspaceNames: ["cli"],
19: })
20: ).toStrictEqual({ path: "./packages" });
21: });
22:
23: test("keeps a multi-segment suffix that is not an exact workspace path", () => {
24: expect(
25: resolvePublishLocation({
26: path: "./packages/cli",
27: workspaceNames: ...
<path>/home/byk/Code/getsentry/publish/src/modules/__tests__/release-revision.js</path>
<type>file</type>
<content>
1: import { expect, test } from "vitest";
2:
3: const {
4: getReleaseRevision,
5: updateReleaseRevision,
6: } = require("../release-revision.js");
7:
8: const REVISION = "7e5ca7ed5581552de066e2a8bc295b8306be38ac";
9:
10: function requestBody(quickLinks) {
11: return `Requested by: @byk
12:
13: Merge target: (default)
14:
15: Quick links:
16: ${quickLinks}`;
17: }
18:
19: function canonicalQuickLinks(revision = REVISION) {
20: return `- [View changes](https://github.com/getsentry/toolkit/compare/1.2.2...release/1.2.3)
21: - [View check runs](https://github.com/getsentry/toolkit/commit/${revision}/checks/)`;
22: }
23:
24: test("gets the CI-approved revision from the request header", () => {
25: expect(
26: getReleaseRevision({
27: repo: "toolkit",
28: issueBody: requestBody(canonicalQuickLinks()),
29: })
30: ).toBe(REVISION);
31: ...
<path>/home/byk/Code/getsentry/publish/src/modules/__tests__/ci-poller-input.js</path>
<type>file</type>
<content>
1: import { readFileSync } from "fs";
2: import { join } from "path";
3:
4: import { expect, test } from "vitest";
5:
6: const { getCiPollerInput } = require("../ci-poller-input.js");
7: const { requestDigest } = require("../approval-attestation.js");
8:
9: const REVISION = "7e5ca7ed5581552de066e2a8bc295b8306be38ac";
10: const labels = [{ name: "accepted" }, { name: "dry-run" }];
11: const issueBody = `Requested by: @byk
12:
13: Merge target: (default)
14:
15: Quick links:
16: - [View changes](https://github.com/getsentry/toolkit/compare/1.2.2...release/1.2.3)
17: - [View check runs](https://github.com/getsentry/toolkit/commit/${REVISION}/checks/)`;
18:
19: test("parses root and full-path publish titles", () => {
20: const rootTitle = "publish: getsentry/toolkit/cli@1.2.3";
21: expect(
22: getCiPollerInput({
23: issueBody,
24: labels,
25: ...
<path>/home/byk/Code/getsentry/publish/src/publish/__tests__/discover-location.js</path>
<type>file</type>
<content>
1: import { afterEach, expect, test, vi } from "vitest";
2:
3: const {
4: discoverLocation,
5: getWorkspaceNames,
6: } = require("../discover-location.js");
7:
8: afterEach(() => {
9: vi.restoreAllMocks();
10: });
11:
12: test("retains checkout-path behavior when the root config is absent", () => {
13: expect(
14: discoverLocation({
15: input: { path: "./packages/cli" },
16: repositoryDirectory: "__repo__",
17: exists: () => false,
18: })
19: ).toEqual({ path: "./packages/cli" });
20: });
21:
22: test("discovers exact workspace paths using the released Craft image", () => {
23: const execFile = vi.fn(() => '["packages/CLI"]');
24:
25: expect(
26: discoverLocation({
27: input: { path: "./packages/CLI" },
28: repositoryDirectory: "__repo__",
29: exists: () => true,
30: execFile,
31: })
32: ...
<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: environment: production
14: if: |
15: (github.actor == 'sentry-release-bot[bot]' || github.actor == 'getsantry[bot]') &&
16: startsWith(github.event.issue.title, 'publish: ')
17: steps:
18: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262
19: with:
20: persist-credentials: false
21: - name: Get auth token
22: id: token
23: uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1
24: with:
25: client-id: ${{ vars.SENTRY_INTERNAL_APP_ID }}
26: private-key: ${{ secrets.SENTRY_INTERNAL_APP_PRIVATE_KEY }}
27: - name: Bind request snapshot
28: ...
<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 (
16: typeof body !== "string" ||
17: !Array.isArray(labels) ||
18: typeof title !== "string"
19: ) {
20: throw new Error("Invalid publish request");
21: }
22:
23: return crypto
24: .createHash("sha256")
25: .update(
26: JSON.stringify({
27: body,
28: dryRun: labels.some((label) => label.name === ...
<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: requestDigest,
7: } = require("../modules/approval-attestation.js");
8:
9: async function getGitHubResponse(path) {
10: return fetch(`https://api.github.com/${path}`, {
11: headers: {
12: Accept: "application/vnd.github+json",
13: Authorization: `Bearer ${process.env.APPROVAL_TOKEN}`,
14: "X-GitHub-Api-Version": "2026-03-10",
15: },
16: });
17: }
18:
19: async function getIssue({ repository, issueNumber }) {
20: const response = await getGitHubResponse(
21: `repos/${repository}/issues/${issueNumber}`
22: );
23:
24: if (!response.ok) {
25: throw new Error(
26: `Could not retrieve issue #${issueNumber}: GitHub returned ${response.status}`
27: );
28: }
29:
30: return ...
<path>/home/byk/Code/getsentry/publish/src/publish/request-digest-from-event.js</path>
<type>file</type>
<content>
1: const fs = require("fs");
2: const { requestDigest } = require("../modules/approval-attestation.js");
3:
4: function requestDigestFromEvent(event) {
5: if (!event || typeof event.issue !== "object" || event.issue === null) {
6: throw new Error("The GitHub event has no issue snapshot");
7: }
8:
9: return requestDigest(event.issue);
10: }
11:
12: function main() {
13: if (!process.env.GITHUB_EVENT_PATH) {
14: throw new Error('No "GITHUB_EVENT_PATH" environment variable found');
15: }
16:
17: if (!process.env.GITHUB_OUTPUT) {
18: throw new Error('No "GITHUB_OUTPUT" environment variable found');
19: }
20:
21: const event = JSON.parse(
22: fs.readFileSync(process.env.GITHUB_EVENT_PATH, "utf8")
23: );
24: fs.appendFileSync(
25: process.env.GITHUB_OUTPUT,
26: `request_digest=${requestDigestFromEvent(event)}\n`
27: );
28: ...
[
{
"Id": "sha256:fc314146c06b14791ab860e80e73043c7c5fa06f61bce3081a34a8a227305a5a",
"RepoTags": [
"getsentry/craft:2.30.1"
],
"RepoDigests": [
"getsentry/craft@sha256:9a4a5d5efa44a00c2215078ead39800d4aaa5a97908b94f45a64d7d506d6e14b"
],
"Comment": "buildkit.dockerfile.v0",
"Created": "2026-08-08T00:37:30.542283842Z",
"Config": {
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/.cargo/bin:/opt/flutter/bin:/venv/bin",
"NODE_VERSION=24.18.0",
"YARN_VERSION=1.22.22",
"DEBIAN_FRONTEND=noninteractive",
"DOTNET_CLI_TELEMETRY_OPTOUT=1",
"COCOAPODS_ALLOW_ROOT=1",
"CARGO_HOME=/root/.cargo",
"RUSTUP_HOME=/root/.rustup",
"CRAFT_BUILD_SHA=cd1e8294061fd970b40d98b77aaa109cb1e00e78"
],
"Entrypoint": ...
/usr/local/bin/craft
/usr/local/bin/craft: Node.js script executable, ASCII text, with very long lines (438)
#!/usr/bin/env node
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __esm = (fn, res, err) => function __init() {
if (err) throw err[0];
try {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
} catch (e4) {
throw err = [e4], e4;
}
};
var __commonJS = (cb, mod) => function __require() {
try {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
} catch (e4) {
throw mod = 0, e4;
}
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to2, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of ...
PATTERN /publish-state-/
241195: function shortCwdHash(cwd) {
241196: return (0, import_crypto6.createHash)("sha1").update(cwd).digest("hex").slice(0, 12);
241197: }
241198: function getPublishStateFilename(version2, githubConfig, cwd = process.cwd()) {
241199: const safeVersion = sanitiseForFilename(version2);
241200: if (githubConfig) {
241201: const owner = sanitiseForFilename(githubConfig.owner);
241202: const repo = sanitiseForFilename(githubConfig.repo);
241203: return `publish-state-${owner}-${repo}-${shortCwdHash(cwd)}-${safeVersion}.json`;
241204: }
241205: const cwdDigest = (0, import_crypto6.createHash)("sha256").update(cwd).digest("hex").slice(0, 16);
241206: return `publish-state-${cwdDigest}-${safeVersion}.json`;
241207: }
241208: function getPublishStatePath(version2, githubConfig, cwd = process.cwd()) {
241209: return (0, import_path29.join)(
241210: getCraftStateDir(),
241211: getPublishStateFilename(version2, githubConfig, ...
241196: return (0, import_crypto6.createHash)("sha1").update(cwd).digest("hex").slice(0, 12);
241197: }
241198: function getPublishStateFilename(version2, githubConfig, cwd = process.cwd()) {
241199: const safeVersion = sanitiseForFilename(version2);
241200: if (githubConfig) {
241201: const owner = sanitiseForFilename(githubConfig.owner);
241202: const repo = sanitiseForFilename(githubConfig.repo);
241203: return `publish-state-${owner}-${repo}-${shortCwdHash(cwd)}-${safeVersion}.json`;
241204: }
241205: const cwdDigest = (0, import_crypto6.createHash)("sha256").update(cwd).digest("hex").slice(0, 16);
241206: return `publish-state-${cwdDigest}-${safeVersion}.json`;
241207: }
241208: function getPublishStatePath(version2, githubConfig, cwd = process.cwd()) {
241209: return (0, import_path29.join)(
241210: getCraftStateDir(),
241211: getPublishStateFilename(version2, githubConfig, cwd)
241212: );
241213: }
241214:
241215: // ...
...output truncated...
Full output saved to: /home/byk/.local/share/opencode/tool-output/tool_08949908a001Oeg86d0g4h6DfX
120698: async getReleaseByTag(tag2) {
120699: try {
120700: const { data: data2 } = await this.github.repos.getReleaseByTag({
120701: owner: this.githubConfig.owner,
120702: repo: this.githubConfig.repo,
120703: tag: tag2
120704: });
120705: return data2;
120706: } catch (error3) {
120707: if (error3.status === 404) {
120708: return void 0;
120709: }
120710: throw error3;
120711: }
120712: }
120713: /**
120714: * Fetches a list of all assets for the given release
120715: *
120716: * The result includes unfinished asset uploads. ...
NEEDLE async function getGlobalGitHubConfig INDEX 232496
232476: function requiresMinVersion(requiredVersion) {
232477: const config3 = getConfiguration();
232478: const minVersionRaw = config3.minVersion;
232479: if (!minVersionRaw) {
232480: return false;
232481: }
232482: const configuredMinVersion = parseVersion(minVersionRaw);
232483: const required = parseVersion(requiredVersion);
232484: if (!configuredMinVersion || !required) {
232485: return false;
232486: }
232487: return versionGreaterOrEqualThan(configuredMinVersion, required);
232488: }
232489: function getVersioningPolicy() {
232490: const config3 = getConfiguration();
232491: if (config3.versioning?.policy) {
232492: return config3.versioning.policy;
232493: }
232494: return requiresMinVersion(AUTO_VERSION_MIN_VERSION) ? "auto" /* Auto */ : "manual" /* Manual */;
232495: }
232496: async function getGlobalGitHubConfig(clearCache = false) {
232497: if (!clearCache && ...
<path>/home/byk/Code/getsentry/publish/src/modules/publish-issue-title.peggy</path>
<type>file</type>
<content>
1: {
2: function join(characters) {
3: return characters.join("");
4: }
5: }
6:
7: // BEGIN TITLE GRAMMAR
8: // Canonical grammar for publish issue titles. A path suffix is syntactic only:
9: // the controller resolves the complete suffix as a workspace after checking
10: // out the CI-approved revision.
11: PublishIssueTitle
12: = "publish: " "getsentry/"? repo:Repository path:Path? "@" version:Version !. {
13: return {
14: repo,
15: path: path || "",
16: version,
17: };
18: }
19:
20: Repository
21: = characters:RepositoryCharacter+ { return join(characters); }
22:
23: RepositoryCharacter
24: = [A-Za-z0-9_.-]
25:
26: Path
27: = segments:("/" segment:PathSegment { return `/${segment}`; })+ { return join(segments); }
28:
29: PathSegment
30: = characters:RepositoryCharacter+ { return join(characters); }
31:
32: ...
NEEDLE CRAFT_MERGE_TARGET
NEEDLE CRAFT_WORKSPACE
NEEDLE CRAFT_DRY_RUN
242617: "no-input": {
242618: coerce: envToBool2,
242619: default: import_is_ci2.default,
242620: describe: "Suppresses all user prompts",
242621: global: true
242622: },
242623: "dry-run": {
242624: coerce: envToBool2,
242625: // TODO(byk): Deprecate this in favor of CRAFT_DRY_RUN
242626: default: process.env.DRY_RUN,
242627: global: true,
242628: describe: "Dry run mode: no file writes, commits, pushes, or API mutations"
242629: }
242630: };
242631: function fixGlobalBooleanFlags(argv) {
242632: const result = [];
242633: for (const arg of argv) {
242634: result.push(arg);
242635: if (arg.slice(2) in GLOBAL_BOOLEAN_FLAGS) {
242636: result.push("1");
242637: }
242638: }
242639: return result;
NEEDLE .env("CRAFT
HITS 1
242643: printVersion();
242644: warnIfCraftEnvFileExists();
242645: const argv = fixGlobalBooleanFlags(process.argv.slice(2));
242646: await yargs_default().parserConfiguration({
242647: "boolean-negation": false
242648: }).env("CRAFT").command(prepare_exports).command(publish_exports).command(targets_exports2).command(config_exports).command(artifacts_exports).command(changelog_exports).demandCommand().version(getPackageVersion()).alias("v", "version").help().alias("h", "help").options(GLOBAL_BOOLEAN_FLAGS).option("log-level", {
242649: default: "Info",
242650: choices: Object.keys(import_consola.LogLevel).filter((level) => isNaN(Number(level))),
242651: coerce: (level) => level[0].toUpperCase() + level.slice(1).toLowerCase(),
242652: describe: "Logging level",
242653: global: true
242654: }).strictCommands().showHelpOnFail(true).middleware(setGlobals).parse(argv);
242655: }
242656: withTracing(main, { name: "craft.cli", ...
.option("workspace": 0
argv.workspace: 0
process.env.CRAFT_PUBLISH_STATE_GITHUB_REPO: 0
TERM workspace list: 0 TERM CRAFT_WORKSPACE: 0 TERM selected workspace: 0 TERM Unknown workspace: 0 TERM workspace config: 0 TERM Workspace name: 0
hits 131
232641: return {
232642: noMerge: false,
232643: source: "default"
232644: };
232645: }
232646: function isExpandableTarget(targetClass) {
232647: return typeof targetClass === "function" && "expand" in targetClass && typeof targetClass.expand === "function";
232648: }
232649: async function expandWorkspaceTargets(targets) {
232650: const { getTargetByName: getTargetByName2 } = (init_targets(), __toCommonJS(targets_exports));
232651: const rootDir = getConfigFileDir() || process.cwd();
232652: const expandedTargets = [];
232653: for (const target of targets) {
232654: const targetClass = getTargetByName2(target.name);
232655: if (targetClass && isExpandableTarget(targetClass)) {
232656: const expanded = await targetClass.expand(target, rootDir);
232657: expandedTargets.push(...expanded);
232658: } else {
232659: expandedTargets.push(target);
232660: }
232661: }
232662: return expandedTargets;
232663: }
232664: var ...
craft <command>
Commands:
craft prepare [NEW-VERSION] 🚢 Prepare a new release branch
[aliases: p, prerelease, prepublish, prepare, release]
craft publish NEW-VERSION 🛫 Publish artifacts [aliases: pp, publish]
craft targets List defined targets as JSON array
craft config Print the parsed, processed, and validated Craft
config for the current project in pretty-JSON.
craft artifacts <command> 📦 Manage artifacts [aliases: a, artifact]
craft changelog Generate changelog from git history
Options:
--no-input Suppresses all user prompts [default: false]
--dry-run Dry run mode: no file writes, commits, pushes, or API
mutations
--log-level Logging level
[choices: "Fatal", "Error", "Warn", "Log", "Info", "Success", "Debug",
"Trace", "Silent", "Verbose"] ...
craft <command>
Commands:
craft prepare [NEW-VERSION] 🚢 Prepare a new release branch
[aliases: p, prerelease, prepublish, prepare, release]
craft publish NEW-VERSION 🛫 Publish artifacts [aliases: pp, publish]
craft targets List defined targets as JSON array
craft config Print the parsed, processed, and validated Craft
config for the current project in pretty-JSON.
craft artifacts <command> 📦 Manage artifacts [aliases: a, artifact]
craft changelog Generate changelog from git history
Options:
--no-input Suppresses all user prompts [default: false]
--dry-run Dry run mode: no file writes, commits, pushes, or API
mutations
--log-level Logging level
[choices: "Fatal", "Error", "Warn", "Log", "Info", "Success", "Debug",
"Trace", "Silent", "Verbose"] ...
<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: `Requested by: @BYK
16:
17: Merge target: custom-branch
18:
19: Quick links:
20: - [View changes](https://github.com/getsentry/sentry/compare/21.3.0...refs/heads/releases/21.3.1)
21: - [View check runs](https://github.com/getsentry/sentry/commit/7e5ca7ed5581552de066e2a8bc295b8306be38ac/checks/)
22:
23: Assign the **accepted** label to this issue to approve the release. ...
<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("binds approval and publication to the triggering issue snapshot", () => {
8: expect(workflow).toContain("node src/publish/request-digest-from-event.js");
9: expect(
10: workflow.match(
11: /EXPECTED_REQUEST_DIGEST: \$\{\{ steps\.request-digest\.outputs\.request_digest \}\}/g
12: )
13: ).toHaveLength(4);
14: });
15:
16: test("revalidates approval immediately before Craft", () => {
17: const fence = workflow.indexOf("Revalidate approval attestation");
18:
19: expect(fence).toBeGreaterThan(workflow.indexOf("Set targets"));
20: expect(fence).toBeLessThan(workflow.indexOf("Publish using Craft"));
21: ...
<path>/home/byk/Code/getsentry/publish/src/modules/__tests__/ci-poller-workflow.js</path>
<type>file</type>
<content>
1: import {
2: mkdtempSync,
3: mkdirSync,
4: readFileSync,
5: readdirSync,
6: rmSync,
7: writeFileSync,
8: } from "fs";
9: import { tmpdir } from "os";
10: import { join } from "path";
11: import { spawnSync } from "child_process";
12:
13: import { afterEach, expect, test } from "vitest";
14:
15: const temporaryDirectories = [];
16: const INITIAL_REVISION = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
17: const UPDATED_REVISION = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
18:
19: function getPollerScript() {
20: const workflow = readFileSync(
21: join(__dirname, "../../../.github/workflows/ci-poller.yml"),
22: "utf8"
23: );
24: const section = workflow.match(
25: / {6}- name: Check CI status for ci-pending issues[\s\S]*? {8}run: \|\n(?<script>[\s\S]*?)\n {6}- name: Check for remaining pending issues/
26: );
27:
28: if ...
<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: ...
<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: requestDigest,
5: } = require("../modules/approval-attestation.js");
6: const { getIssue } = require("./validate-approval-attestation.js");
7:
8: async function recordAutoApprovalAttestation({
9: autoApprover,
10: expectedRequestDigest,
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: requestDigest(issue) !== expectedRequestDigest ||
22: issue.user?.login?.toLowerCase() !== autoApprover.toLowerCase()
23: ) {
24: throw new Error("The automated approval request changed before approval");
25: }
26:
27: return createAutoApprovalAttestation({ autoApprover, issue });
28: }
29:
30: ...
<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: ...
<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: requestDigest,
7: } = require("../modules/approval-attestation.js");
8: const {
9: AUTO_APPROVAL_LABELER,
10: AUTO_APPROVERS,
11: authorizeApproval,
12: isAutoApprovedRepository,
13: } = require("../modules/approval-authorizer.js");
14:
15: function getAutoApprovedRepositories() {
16: return new Set(
17: fs
18: .readFileSync("auto-approve-repos.txt", "utf8")
19: .split(/\r?\n/)
20: .filter(Boolean)
21: );
22: }
23:
24: async function getPermission({ owner, repository, username }) {
25: const response = await getGitHubResponse(
26: `repos/${encodeURIComponent(owner)}/${encodeURIComponent(
27: repository
28: )}/collaborators/${encodeURIComponent(username)}/permission`
29: );
30:
31: if ...
<path>/home/byk/Code/getsentry/publish/src/modules/post-workflow-details.js</path>
<type>file</type>
<content>
1: async function postWorkflowDetails({ context, octokit }) {
2: const { repo: publishRepo, runId: run_id } = context;
3: const { number: issue_number } = context.payload.issue;
4:
5: const workflowInfo = (
6: await octokit.rest.actions.getWorkflowRun({
7: ...publishRepo,
8: run_id,
9: })
10: ).data;
11:
12: return octokit.rest.issues.createComment({
13: ...publishRepo,
14: issue_number,
15: body: `Publishing: [run#${run_id}](${workflowInfo.html_url})`,
16: });
17: }
18:
19: module.exports = postWorkflowDetails;
(End of file - total 19 lines)
</content>
<path>/home/byk/Code/getsentry/publish/src/modules/process-end-state.js</path>
<type>file</type>
<content>
1: const Sentry = require("@sentry/node");
2:
3: async function processEndState({ context, octokit, inputs, status }) {
4: const { repo, version } = inputs;
5: const { repo: publishRepo, runId: run_id } = context;
6: const { number: issue_number } = context.payload.issue;
7: const workflowInfo = (
8: await octokit.rest.actions.getWorkflowRun({
9: ...publishRepo,
10: run_id,
11: })
12: ).data;
13:
14: const details = {
15: repo,
16: version,
17: publishRepo,
18: run_id,
19: issue_number,
20: workflowInfo,
21: status,
22: };
23:
24: await postIssueComment({
25: octokit,
26: details,
27: });
28:
29: if (status === "success") {
30: await octokit.rest.issues.update({
31: ...publishRepo,
32: issue_number,
33: state: "closed",
34: });
35: }
36:
37: await reportSession({ ...
<path>/home/byk/Code/getsentry/publish/src/publish/post-workflow-details.js</path>
<type>file</type>
<content>
1: const postWorkflowDetails = require("../modules/post-workflow-details.js");
2: const { getGitHubToken } = require("../libs/github");
3: const github = require("@actions/github");
4:
5: const context = github.context;
6: const octokit = github.getOctokit(getGitHubToken());
7:
8: postWorkflowDetails({ context, octokit });
(End of file - total 8 lines)
</content>
<path>/home/byk/Code/getsentry/publish/src/publish/post-result.js</path>
<type>file</type>
<content>
1: const processEndState = require("../modules/process-end-state");
2: const { getGitHubToken } = require("../libs/github");
3: const github = require("@actions/github");
4:
5: const context = github.context;
6: const octokit = github.getOctokit(getGitHubToken());
7: const inputs = JSON.parse(process.env.PUBLISH_ARGS);
8: const args = process.argv.slice(2);
9: const status = args[0];
10:
11: processEndState({ context, octokit, inputs, status });
(End of file - total 11 lines)
</content>
<path>/home/byk/Code/getsentry/publish/src/libs/github.js</path>
<type>file</type>
<content>
1: /**
2: * This is a helper function that throws a useful error message if the
3: * workflow environment is not configured correctly.
4: *
5: * @returns string
6: */
7: function getGitHubToken() {
8: if (!process.env.GITHUB_TOKEN) {
9: throw new Error(
10: 'No "GITHUB_TOKEN" environment variable found. ' +
11: "Please ensure the workflow is configured correctly"
12: );
13: }
14: return process.env.GITHUB_TOKEN;
15: }
16:
17: module.exports = {
18: getGitHubToken,
19: };
(End of file - total 19 lines)
</content>
<path>/home/byk/Code/getsentry/publish/src/publish/__tests__/resolve-release-revision.js</path>
<type>file</type>
<content>
1: import { spawnSync } from "child_process";
2: import { expect, test } from "vitest";
3:
4: const script = new URL("../resolve-release-revision.js", import.meta.url)
5: .pathname;
6:
7: test("rejects missing workflow input through release revision validation", () => {
8: const result = spawnSync(process.execPath, [script], {
9: encoding: "utf8",
10: env: {
11: ...process.env,
12: PUBLISH_ARGS: "",
13: PUBLISH_ISSUE_BODY: "",
14: },
15: });
16:
17: expect(result.status).not.toBe(0);
18: expect(result.stderr).toContain("Publish input must define a repository.");
19: expect(result.stderr).not.toContain("Unexpected end of JSON input");
20: });
(End of file - total 20 lines)
</content>
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 2635ea6..69210d1 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -32,13 +32,90 @@ jobs:
&& github.event.issue.state == 'open'
&& startsWith(github.event.issue.title, 'publish: ')
steps:
+ - name: Get publish code
+ uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803
+ with:
+ persist-credentials: false
+
- name: Get auth token
id: token
- uses: actions/create-github-app-token@v3
+ uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1
with:
client-id: ${{ vars.SENTRY_INTERNAL_APP_ID }}
private-key: ${{ secrets.SENTRY_INTERNAL_APP_PRIVATE_KEY }}
+ - name: Bind request snapshot
+ id: request-digest
+ run: node src/publish/request-digest-from-event.js
+
+ # The release bot sees all target repositories. ...
<path>/home/byk/Code/getsentry/publish/src/publish/resolve-location.js</path>
<type>file</type>
<content>
1: const core = require("@actions/core");
2: const { resolvePublishLocation } = require("../modules/publish-location");
3:
4: function resolveLocation() {
5: const input = JSON.parse(process.env.PUBLISH_ARGS || "{}");
6: const workspaceNames = JSON.parse(process.env.CRAFT_WORKSPACE_NAMES || "[]");
7:
8: if (!Array.isArray(workspaceNames)) {
9: throw new Error(
10: "Craft workspace discovery returned an invalid workspace list."
11: );
12: }
13:
14: core.setOutput(
15: "result",
16: JSON.stringify(
17: resolvePublishLocation({
18: path: input.path,
19: workspaceNames,
20: })
21: )
22: );
23: }
24:
25: resolveLocation();
(End of file - total 25 lines)
</content>
rg: regex parse error:
(?:uses:\s+[^\s]+@(?![0-9a-f]{40}(?:\s|$)|sha256:[0-9a-f]{64}(?:\s|$)))
^^^
error: look-around, including look-ahead and look-behind, is not supported
Consider enabling PCRE2 with the --pcre2 flag, which can handle backreferences
and look-around.
Found 18 matches /home/byk/Code/getsentry/publish/.github/workflows/test.yml: Line 19: - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 Line 20: - uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 Line 23: - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 /home/byk/Code/getsentry/publish/.github/workflows/publish.yml: Line 36: uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 Line 42: uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 Line 56: uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 Line 155: uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 Line 198: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 Line 234: uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 Line 262: uses: ...
241216: var DEFAULT_POST_RELEASE_SCRIPT_PATH = (0, import_path30.join)("scripts", "post-release.sh");
241217: var command2 = ["publish NEW-VERSION"];
241218: var aliases = ["pp", "publish"];
241219: var description = "\u{1F6EB} Publish artifacts";
241220: var builder = (yargs) => {
241221: const definedTargets = getConfiguration().targets || [];
241222: const possibleTargetNames = new Set(getAllTargetNames());
241223: const allowedTargetNames = definedTargets.filter((target) => target.name && possibleTargetNames.has(target.name)).map(BaseTarget.getId);
241224: return yargs.positional("NEW-VERSION", {
241225: description: "Version to publish",
241226: type: "string"
241227: }).option("target", {
241228: alias: "t",
241229: choices: allowedTargetNames.concat([
241230: "all" /* All */,
241231: "none" /* None */
241232: ]),
241233: default: "all" /* All */,
241234: description: "Publish to this target",
241235: type: "string"
241236: ...
process.env.GITHUB_TOKEN: 3
74598: });
74599:
74600: // src/utils/githubApi.ts
74601: function getGitHubApiToken() {
74602: const githubApiToken = process.env.GITHUB_TOKEN || process.env.GITHUB_API_TOKEN;
74603: if (!githubApiToken) {
74604: throw new ConfigurationError(
74605: "GITHUB_TOKEN not found. This is required to fetch PR information from GitHub.\nTip: Run `gh auth token` if you have GitHub CLI installed."
74606: );
74607: }
74608: return githubApiToken;
74609: }
74610: function getGitHubClient(token = "") {
74611: const githubApiToken = token || getGitHubApiToken();
117422: }
117423: if (!username || !password) {
117424: if (registry === "ghcr.io") {
117425: username = username || process.env.GITHUB_ACTOR || "x-access-token";
117426: password = password || process.env.GITHUB_TOKEN || process.env.GITHUB_API_TOKEN;
117427: }
117428: }
117429: if ...
function getGitClient: 1
57062: function stripRemoteName(branch, remoteName) {
57063: const branchName = branch || "";
57064: const remotePrefix = `${remoteName}/`;
57065: if (branchName.startsWith(remotePrefix)) {
57066: return branchName.slice(remotePrefix.length);
57067: }
57068: return branchName;
57069: }
57070: async function getGitClient() {
57071: const configFileDir = getConfigFileDir() || ".";
57072: process.chdir(configFileDir);
57073: logger.debug("Working directory:", process.cwd());
57074: const git = esm_default2(configFileDir);
57075: const isRepo = await git.checkIsRepo();
57076: if (!isRepo) {
57077: throw new ConfigurationError("Not in a git repository!");
57078: }
57079: return createDryRunGit(git);
57080: }
57081: function createGitClient(directory) {
57082: return createDryRunGit(esm_default2(directory));
57083: }
57084: async function cloneRepo(url2, targetDirectory, options) {
async function getGitClient: 1
57062: function ...
Date: Sep 10, 2026 * 🔴 (03:19) User’s compiled-bundle search found exactly 1 `function getGitClient`, 1 `async function getGitClient`, and 4 occurrences of `getGitClient()`. * 🔴 (03:19) `getGitClient()` at lines 57070–57079 uses `getConfigFileDir() || "."`, calls `process.chdir(configFileDir)`, logs `"Working directory:"` with `process.cwd()`, creates the client via `esm_default2(configFileDir)…
Date: Sep 10, 2026 * 🔴 (03:18) User provided a compiled JavaScript excerpt defining `DEFAULT_POST_RELEASE_SCRIPT_PATH = join("scripts", "post-release.sh")`, publish command `["publish NEW-VERSION"]`, aliases `["pp", "publish"]`, and description `"🛫 Publish artifacts"`. * 🔴 (03:18) The compiled publish command builder derives `allowedTargetNames` by filtering configured targets against `new Set…
2026-09-10 - 🔴 (03:14) User provided `/home/byk/Code/getsentry/publish/src/modules/__tests__/ci-poller-workflow.js`, a 228-line Vitest integration-style test file that extracts the shell script between `"Check CI status for ci-pending issues"` and `"Check for remaining pending issues"` from `.github/workflows/ci-poller.yml`. - 🔴 (03:14) `/home/byk/Code/getsentry/publish/src/modules/__tests__/ci…
2026-09-10 - 🔴 (03:10) User provided bundled Craft code at lines 232476-232525: `requiresMinVersion(requiredVersion)` parses configured and required versions and returns `versionGreaterOrEqualThan(configuredMinVersion, required)`; `getVersioningPolicy()` uses configured `versioning.policy`, otherwise selects `"auto"` when `requiresMinVersion(AUTO_VERSION_MIN_VERSION)` is true and `"manual"` othe…
2026-09-10 - 🔴 (03:08) User provided the full truncated tool-output path `/home/byk/.local/share/opencode/tool-output/tool_08949908a001Oeg86d0g4h6DfX`. - 🔴 (03:08) User stated that Craft resolves a floating tag pattern by replacing placeholders with version components. - 🟡 (03:08) Bundled Craft code at lines 120698-120711 defines `getReleaseByTag(tag2)`, which calls `this.github.repos.getRelea…
2026-09-10 - 🔴 (03:05) User provided the file path `/home/byk/Code/getsentry/publish/src/modules/__tests__/publish-workflow.js`. - 🟡 (03:05) Inspected `/home/byk/Code/getsentry/publish/src/modules/__tests__/publish-workflow.js` (170 lines): `getSetTargetsScript()` extracts the Bash body between workflow steps `Set targets` and `Revalidate approval attestation`; `runSetTargets()` executes it wit…
2026-09-10 - 🔴 (03:04) [enforced-workflow] User required `workflow_dispatch` to check out and run trusted code from `${{ github.event.repository.default_branch }}`, rather than code from an arbitrary dispatch ref; workflow comment at line 51 states: “workflow_dispatch can target any ref. Always run trusted code.” - 🔴 (03:04) [enforced-workflow] User required that a release never be moved to `ci…
2026-09-10 - 🔴 (03:03) User stated the previous review returned an empty result and that an empty result is invalid. - 🟡 (03:03) User requested resumption of a read-only adversarial audit of the exact unchanged tree at `/home/byk/Code/getsentry/publish`; scope is release input, revision, target checkout, publish location, Craft invocation, and state-file identity. - 🔴 (03:03) [enforced-workflo…