Dashboard › craft › Session 1YRxdVOOo2Lq
1YRxdVOOo2Lq5wyFoPerform a strict read-only adversarial review of the uncommitted compact workspace publish-contract changes in BOTH worktrees:
Do not modify files. Inspect the full diffs and relevant source/tests/workflows. Focus on: exact CI-approved revision binding from the canonical Quick links check-runs URL through checkout, discovery, state path, and craft publish --rev; compact title grammar and legacy JSON title compatibility; workspace/path mutual exclusivity; raw case-sensitive workspace identity; CI poller behavior and title parsing; state filename parity and no repository-local fallback; GitHub Actions expression/YAML validity; parser generation freshness; action input validation/order; security/path traversal/injection; and commit hygiene (including untracked pnpm-lock.yaml).
Return only prioritized findings with exact file:line citations and PASS/FAIL assessments, ending with MERGE or DO-NOT-MERGE. If no blockers, explicitly state that. Do not rely on previous review results.
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation</path> <type>directory</type> <entries> .craft.yml .dockerignore .eslintcache .git .github/ .gitignore .lore.md .nojekyll .npmignore .prettierignore .prettierrc.yml .tsbuildinfo .vscode/ action.yml AGENTS.md blog-post-draft.md build.mjs CHANGELOG.md CONTRIBUTING.md dist/ Dockerfile docs/ eslint.config.mjs img/ LICENSE node_modules/ package.json pnpm-lock.yaml README.md src/ tsconfig.build.json tsconfig.json vitest.config.mts (33 entries) </entries>
<path>/home/byk/Code/getsentry/publish-workspace-acceptance</path> <type>directory</type> <entries> .eslintrc.js .git .github/ .gitignore AGENTS.md auto-approve-repos.txt docs/ LICENSE node_modules/ package.json pnpm-lock.yaml README.md scripts/ src/ vitest.config.js yarn.lock (16 entries) </entries>
M action.yml M docs/src/content/docs/targets/github.md M src/__tests__/action.test.ts M src/__tests__/config.test.ts M src/commands/__tests__/publish.test.ts M src/commands/publish.ts M src/config.ts M src/index.ts M src/schemas/project_config.ts ?? src/commands/workspace.ts ?? src/commands/workspace_cmds/ feat/workspace-action-propagation action.yml | 18 ++--- docs/src/content/docs/targets/github.md | 36 ++++++++- src/__tests__/action.test.ts | 125 +++++++++++++++++++++++++++--- src/__tests__/config.test.ts | 130 ++++++++++++++++++++++---------- src/commands/__tests__/publish.test.ts | 27 +++++++ src/commands/publish.ts | 18 ++++- src/config.ts | 48 ++++++++---- src/index.ts | 2 + src/schemas/project_config.ts | 63 +++++++++++----- 9 files changed, 370 insertions(+), 97 deletions(-) diff --git a/action.yml b/action.yml index ...
M .github/workflows/publish.yml M docs/publish-issue-format.md M package.json M src/modules/__tests__/details-from-context.js M src/modules/__tests__/publish-workflow.js M src/modules/details-from-context.js M src/modules/post-workflow-details.js M src/publish/inputs.js M yarn.lock ?? pnpm-lock.yaml ?? scripts/ ?? src/modules/__tests__/publish-location.js ?? src/modules/__tests__/release-revision.js ?? src/modules/publish-issue-title.js ?? src/modules/publish-issue-title.peggy ?? src/modules/publish-location.js ?? src/modules/release-revision.js ?? src/publish/resolve-location.js ?? src/publish/resolve-release-revision.js feat/workspace-acceptance .github/workflows/publish.yml | 59 ++++++++++++++------------- docs/publish-issue-format.md | 55 +++++++++++++++---------- package.json | 5 ++- src/modules/__tests__/details-from-context.js | 20 +++++++++ src/modules/__tests__/publish-workflow.js | 36 ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/workspace.ts</path>
<type>file</type>
<content>
1: import { Argv, CommandBuilder } from 'yargs';
2:
3: import * as list from './workspace_cmds/list';
4:
5: export const command = ['workspace <command>'];
6: export const description = 'Manage release workspaces';
7:
8: export const builder: CommandBuilder = (yargs: Argv) =>
9: yargs.demandCommand().command(list);
10:
11: export const handler = (): void => {
12: /* pass */
13: };
(End of file - total 13 lines)
</content>
<system-reminder>
Instructions from: /home/byk/Code/getsentry/craft-workspace-action-propagation/AGENTS.md
# AGENTS.md
This file provides guidance for AI coding assistants working with the Craft codebase.
## Package Management
- **Always use `pnpm`** for package management. Never use `npm` or `yarn`.
- Node.js version is managed by [Volta](https://volta.sh/) (currently v22.12.0). ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/workspace_cmds/list.ts</path>
<type>file</type>
<content>
1: import { getWorkspaceNames } from '../../config';
2: import { formatJson } from '../../utils/strings';
3:
4: export const command = ['list'];
5: export const description = 'List defined release workspaces as a JSON array';
6:
7: export function handler(): void {
8: console.log(formatJson(getWorkspaceNames()));
9: }
(End of file - total 9 lines)
</content>
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/workspace_cmds/__tests__/list.test.ts</path>
<type>file</type>
<content>
1: import { describe, expect, test, vi } from 'vitest';
2:
3: vi.mock('../../../config', () => ({
4: getWorkspaceNames: vi.fn(),
5: }));
6: vi.mock('../../../utils/strings', () => ({
7: formatJson: vi.fn(value => JSON.stringify(value)),
8: }));
9:
10: import { getWorkspaceNames } from '../../../config';
11: import { handler } from '../list';
12:
13: describe('workspace list command', () => {
14: test('prints exact configured workspace names', () => {
15: vi.mocked(getWorkspaceNames).mockReturnValue(['cli', 'mcp.v2']);
16: const log = vi.spyOn(console, 'log').mockImplementation(() => {});
17:
18: handler();
19:
20: expect(log).toHaveBeenCalledWith('["cli","mcp.v2"]');
21: });
22:
23: test('prints an empty array when no workspaces are configured', () => {
24: ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-issue-title.peggy</path>
<type>file</type>
<content>
1: {
2: function join(characters) {
3: return characters.join("");
4: }
5: }
6:
7: // Canonical grammar for publish issue titles. A path suffix is syntactic only:
8: // the controller resolves a single segment as a workspace after checking out
9: // the CI-approved revision.
10: PublishIssueTitle
11: = "publish: " "getsentry/"? repo:Repository path:Path? workspace:LegacyWorkspace? "@" version:Version !. {
12: return {
13: repo,
14: path: path || "",
15: version,
16: ...(workspace ? { workspace } : {}),
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: = ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-issue-title.js</path>
<type>file</type>
<content>
1: /* eslint-disable */
2: // @generated by Peggy 5.1.0.
3: //
4: // https://peggyjs.org/
5:
6: "use strict";
7:
8: class peg$SyntaxError extends SyntaxError {
9: constructor(message, expected, found, location) {
10: super(message);
11: this.expected = expected;
12: this.found = found;
13: this.location = location;
14: this.name = "SyntaxError";
15: }
16:
17: format(sources) {
18: let str = "Error: " + this.message;
19: if (this.location) {
20: let src = null;
21: const st = sources.find((s) => s.source === this.location.source);
22: if (st) {
23: src = st.text.split(/\r\n|\n|\r/g);
24: }
25: const s = this.location.start;
26: const offset_s =
27: this.location.source &&
28: typeof this.location.source.offset === "function"
29: ? ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-location.js</path>
<type>file</type>
<content>
1: function needsWorkspaceDiscovery({ path, workspace }) {
2: return !workspace && /^\.\/[A-Za-z0-9_.-]+$/.test(path);
3: }
4:
5: function resolvePublishLocation({ path, workspace, workspaceNames }) {
6: if (workspace) {
7: if (path !== ".") {
8: throw new Error("A publish workspace must use the repository root path.");
9: }
10: return { path, workspace };
11: }
12:
13: if (!needsWorkspaceDiscovery({ path, workspace })) {
14: return { path };
15: }
16:
17: if (!workspaceNames.every(isWorkspaceName)) {
18: throw new Error(
19: "Craft workspace discovery returned an invalid workspace list."
20: );
21: }
22:
23: const segments = path.slice(2).split("/");
24: if (
25: path.startsWith("./") &&
26: segments.length === 1 &&
27: workspaceNames.includes(segments[0])
28: ) {
29: return { path: ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/publish/resolve-location.js</path>
<type>file</type>
<content>
1: const core = require("@actions/core");
2: const {
3: needsWorkspaceDiscovery,
4: resolvePublishLocation,
5: } = require("../modules/publish-location");
6:
7: function resolveLocation() {
8: const input = JSON.parse(process.env.PUBLISH_ARGS || "");
9: const workspaceNames = needsWorkspaceDiscovery(input)
10: ? JSON.parse(process.env.CRAFT_WORKSPACE_NAMES || "")
11: : [];
12:
13: if (!Array.isArray(workspaceNames)) {
14: throw new Error(
15: "Craft workspace discovery returned an invalid workspace list."
16: );
17: }
18:
19: core.setOutput(
20: "result",
21: resolvePublishLocation({
22: path: input.path,
23: workspace: input.workspace,
24: workspaceNames,
25: })
26: );
27: }
28:
29: resolveLocation();
(End of file - total 29 lines)
</content>
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/release-revision.js</path>
<type>file</type>
<content>
1: const CHECK_RUNS_LINK =
2: /^- \[View check runs\]\(https:\/\/github\.com\/getsentry\/(?<repo>[A-Za-z0-9_.-]+)\/commit\/(?<revision>[0-9a-f]{40})\/checks\/?\)\r?$/m;
3:
4: function getReleaseRevision({ issueBody, repo }) {
5: const match = issueBody.match(CHECK_RUNS_LINK);
6: if (!match?.groups || match.groups.repo !== repo) {
7: throw new Error(
8: `Expected a View check runs link for getsentry/${repo} in the publish issue body.`
9: );
10: }
11:
12: return match.groups.revision;
13: }
14:
15: module.exports = { getReleaseRevision };
(End of file - total 15 lines)
</content>
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/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: core.setOutput(
7: "revision",
8: getReleaseRevision({
9: issueBody: process.env.PUBLISH_ISSUE_BODY || "",
10: repo,
11: })
12: );
13: }
14:
15: resolveReleaseRevision();
(End of file - total 15 lines)
</content>
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/scripts/generate-publish-issue-title-parser.js</path>
<type>file</type>
<content>
1: const fs = require("fs");
2: const path = require("path");
3: const peggy = require("peggy");
4: const prettier = require("prettier");
5:
6: const grammarPath = path.join(
7: __dirname,
8: "..",
9: "src",
10: "modules",
11: "publish-issue-title.peggy"
12: );
13: const outputPath = path.join(
14: __dirname,
15: "..",
16: "src",
17: "modules",
18: "publish-issue-title.js"
19: );
20: const grammar = fs.readFileSync(grammarPath, "utf8");
21: const parser = peggy.generate(grammar, {
22: format: "commonjs",
23: grammarSource: "publish-issue-title.peggy",
24: output: "source",
25: });
26:
27: const generatedParser = prettier.format(`/* eslint-disable */\n${parser}`, {
28: filepath: outputPath,
29: });
30:
31: if (process.argv.includes("--check")) {
32: const currentParser = fs.readFileSync(outputPath, "utf8");
33: ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/AGENTS.md</path> <type>file</type> <content> 1: # AGENTS.md - Coding Agent Guidelines 2: 3: This document provides guidelines for AI coding agents working in this repository. 4: 5: ## Project Overview 6: 7: This is the **Sentry Publish Repository** - a GitHub Actions-based approval system for publishing releases across multiple Sentry projects using [Craft](https://github.com/getsentry/craft). It's a pure JavaScript (Node.js 24.0.0) project using CommonJS modules. 8: 9: ## Build/Lint/Test Commands 10: 11: ### Package Manager 12: 13: - **Yarn 1.22.22** (classic) - Do NOT use npm 14: - Version management via Volta (pinned in package.json) 15: 16: ### Commands 17: 18: ```bash 19: # Install dependencies 20: yarn install 21: 22: # Run all tests 23: yarn test 24: 25: # Run tests in watch mode 26: yarn test:watch 27: 28: # Run a single test file 29: yarn test src/modules/__tests__/update-issue.js 30: 31: # Run tests ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/.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 ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/publish-location.js</path>
<type>file</type>
<content>
1: import { expect, test } from "vitest";
2:
3: const {
4: needsWorkspaceDiscovery,
5: resolvePublishLocation,
6: } = require("../publish-location.js");
7:
8: test.each([
9: [{ path: "./cli" }, true],
10: [{ path: "./packages/cli" }, false],
11: [{ path: "." }, false],
12: [{ path: "./cli", workspace: "cli" }, false],
13: ])("workspace discovery is %s for %j", (input, expected) => {
14: expect(needsWorkspaceDiscovery(input)).toBe(expected);
15: });
16:
17: test("classifies an exact one-segment workspace without normalizing it", () => {
18: expect(
19: resolvePublishLocation({
20: path: "./CLI",
21: workspaceNames: ["cli", "CLI"],
22: })
23: ).toStrictEqual({ path: ".", workspace: "CLI" });
24: });
25:
26: test("keeps a non-workspace suffix as a checkout path", () => {
27: expect(
28: ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/release-revision.js</path>
<type>file</type>
<content>
1: import { expect, test } from "vitest";
2:
3: const { getReleaseRevision } = require("../release-revision.js");
4:
5: const REVISION = "7e5ca7ed5581552de066e2a8bc295b8306be38ac";
6:
7: test("gets the CI-approved revision for the publish repository", () => {
8: expect(
9: getReleaseRevision({
10: repo: "toolkit",
11: issueBody: `Quick links:
12: - [View check runs](https://github.com/getsentry/toolkit/commit/${REVISION}/checks/)`,
13: })
14: ).toBe(REVISION);
15: });
16:
17: test("gets the CI-approved revision from a CRLF issue body", () => {
18: expect(
19: getReleaseRevision({
20: repo: "toolkit",
21: issueBody: `Quick links:\r\n- [View check runs](https://github.com/getsentry/toolkit/commit/${REVISION}/checks/)\r\n`,
22: })
23: ).toBe(REVISION);
24: });
25:
26: test("rejects a check-runs link ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/details-from-context.js</path>
<type>file</type>
<content>
1: const { parse: parsePublishIssueTitle } = require("./publish-issue-title");
2:
3: /**
4: * Matches the entire "Targets" section of a github publish issue body.
5: */
6: const TARGETS_SECTION_PARSER_REGEX =
7: /^(?!### Targets$\s)(?: *- \[[ xX]\] \S+\s*$(?:\r?\n)?)+/m;
8:
9: /**
10: * Matches all targets of a github publish issue body in a section that was already matched and extracted with `TARGETS_PARSER_REGEX`.
11: * The "id" of the targets is captured within a capture group.
12: */
13: const TARGETS_PARSER_REGEX = /^\s*- \[[ x]\] (\S+)/gim;
14:
15: /**
16: * Matches checked 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. ...
No files found
Found 44 matches
/home/byk/Code/getsentry/publish-workspace-acceptance/src/publish/inputs.js:
Line 3: const { detailsFromContext } = require('../modules/details-from-context');
Line 6: const result = await detailsFromContext({
/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/release-revision.js:
Line 2: /^- \[View check runs\]\(https:\/\/github\.com\/getsentry\/(?<repo>[A-Za-z0-9_.-]+)\/commit\/(?<revision>[0-9a-f]{40})\/checks\/?\)\r?$/m;
Line 8: `Expected a View check runs link for getsentry/${repo} in the publish issue body.`
/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/details-from-context.js:
Line 21: async function detailsFromContext({ context }) {
Line 90: detailsFromContext,
/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/release-revision.js:
Line 11: issueBody: `Quick links:
Line 12: - [View check ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/.github/workflows</path> <type>directory</type> <entries> auto-approve.yml ci-poller.yml cocoapods-keepalive.yml publish.yml test.yml (5 entries) </entries>
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/.github/workflows/ci-poller.yml</path> <type>file</type> <content> 1: name: CI Status Poller 2: 3: on: 4: schedule: 5: - cron: "*/5 * * * *" 6: workflow_dispatch: 7: inputs: 8: attempt: 9: description: Self-dispatch attempt counter (internal use) 10: required: false 11: default: "0" 12: 13: permissions: 14: contents: read 15: issues: read 16: 17: jobs: 18: check-ci: 19: runs-on: ubuntu-latest 20: environment: production 21: # Skip entirely (no runner provisioned) when there's nothing to check. 22: # Set to "true" by ci-pending.yml, reset to "false" here when done. 23: # Always allow workflow_dispatch for manual recovery. 24: if: vars.CI_POLLER_HAS_PENDING == 'true' || github.event_name == 'workflow_dispatch' 25: concurrency: 26: group: ci-status-poller 27: cancel-in-progress: false 28: steps: 29: # sentry-internal-app token ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/publish.ts</path>
<type>file</type>
<content>
590: logger.info(`Running the post-release command...`);
591: await spawnProcess(sysCommand as string, args as string[], {
592: env: buildReleaseCommandEnv({ CRAFT_RELEASED_VERSION: newVersion }),
593: });
594: return true;
595: }
596:
597: /**
598: * Body of 'publish' command
599: *
600: * @param argv Command-line arguments
601: */
602: export async function publishMain(argv: PublishOptions): Promise<any> {
603: // Get publishing configuration
604: const config = getConfiguration() || {};
605:
606: const newVersion = argv.newVersion;
607:
608: logger.info(`Publishing version: "${newVersion}"`);
609:
610: const git = await getGitClient();
611:
612: // Check for dirty repository state before any git operations
613: if (argv.noGitChecks) {
614: logger.info('Not checking the status of the local repository');
615: } else ...
Found 17 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/publish.ts:
Line 630: let checkoutTarget;
Line 631: let branchName;
Line 634: branchName = await getRevisionBranchName(git, rev);
Line 635: checkoutTarget = branchName || rev;
Line 636: logger.debug('Checking out revision', checkoutTarget);
Line 637: await git.checkout(checkoutTarget);
Line 640: branchName = `${branchPrefix}/${newVersion}`;
Line 641: checkoutTarget = branchName;
Line 644: logger.debug('Checking out release branch', branchName);
Line 645: await git.checkout(checkoutTarget);
Line 653: `Could not find the release branch "${branchName}".\n\n` +
Line 833: } else if (!branchName) {
Line 847: branchName,
Line 858: `Failed to merge release branch "${branchName}" into the target branch.`,
Line 878: ` 2. Delete the release branch: git push ${argv.remote} --delete ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/publish.ts</path>
<type>file</type>
<content>
810: }, !argv.keepDownloads);
811:
812: logger.info(' ');
813: }
814:
815: // Check both CLI flag and config option for noMerge
816: const noMergeConfig = getNoMergeConfig();
817: const noMerge = argv.noMerge || noMergeConfig.noMerge;
818:
819: // Warn if --merge-target was specified but noMerge is true from config
820: if (noMergeConfig.noMerge && !argv.noMerge && argv.mergeTarget) {
821: logger.warn(
822: `The --merge-target option will be ignored because noMerge is enabled via ${noMergeConfig.source === 'auto-detected' ? 'auto-detection (compiled GitHub Action)' : 'config'}.`,
823: );
824: }
825:
826: if (noMerge) {
827: const source = argv.noMerge
828: ? 'CLI option'
829: : noMergeConfig.source === 'auto-detected'
830: ? ...
Found 66 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/env.ts:
Line 83: sanitizeDynamicLinkerEnv,
Line 84: sanitizeSpawnEnv,
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/system.ts:
Line 14: import { sanitizeSpawnEnv } from './dynamicLinkerEnv';
Line 173: // Startup already sanitises process.env, so the only way one of
Line 177: // propagate them. See sanitizeSpawnEnv() in ../utils/env.ts.
Line 178: options.env = sanitizeSpawnEnv(options.env ?? process.env);
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/dynamicLinkerEnv.ts:
Line 42: * `process.env`, {@link sanitizeDynamicLinkerEnv} and
Line 43: * {@link sanitizeSpawnEnv} become no-ops.
Line 54: export function sanitizeDynamicLinkerEnv(): void {
Line 93: * @param env Environment variables bag to sanitise. The input is
Line 99: export function sanitizeSpawnEnv(
Line 108: const sanitized: ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/publishState.ts</path> <type>file</type> <content> 1: /** 2: * Helpers for locating Craft's publish-state file in a path that is NOT 3: * writable by the repository being published. 4: * 5: * Background: `craft publish` writes a small JSON file listing targets 6: * that have completed so a resumed run can skip them. Before this module 7: * existed, the file lived at `.craft-publish-<version>.json` in the 8: * project's cwd. That path is inside the repository checkout, so any 9: * committed file at the same path (or any earlier CI step) could 10: * pre-populate the "published" set and trick Craft into silently 11: * skipping targets. 12: * 13: * The file now lives under `$XDG_STATE_HOME/craft/` (falling back to 14: * `$HOME/.local/state/craft/`). The filename is keyed on 15: * owner, repo, a hash of cwd (to disambiguate monorepo subpaths), and 16: * the version being published. ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/action.yml</path>
<type>file</type>
<content>
1: name: 'Craft Prepare Release'
2: description: 'Prepare a new release using Craft'
3:
4: inputs:
5: version:
6: description: >
7: Version to release. Can be a semver string (e.g., "1.2.3"),
8: a bump type ("major", "minor", "patch"), or "auto" for automatic detection.
9: required: false
10: merge_target:
11: description: Target branch to merge into. Uses the default branch as a fallback. ...
Found 4 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/config.ts:
Line 67: export const WORKSPACES_MIN_VERSION = '2.29.0';
Line 186: if (!isVersionGteMinVersion(config.minVersion, WORKSPACES_MIN_VERSION)) {
Line 188: `Using "workspaces" requires minVersion >= ${WORKSPACES_MIN_VERSION} ` +
Line 210: * enforces the `WORKSPACES_MIN_VERSION` gate.
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/config.ts</path> <type>file</type> <content> 40: } from './status_providers/base'; 41: 42: // TODO support multiple configuration files (one per configuration) 43: export const CONFIG_FILE_NAME = '.craft.yml'; 44: 45: /** 46: * The default prefix for the release branch. 47: */ 48: export const DEFAULT_RELEASE_BRANCH_NAME = 'release'; 49: 50: /** 51: * Cached path to the configuration file 52: */ 53: let _configPathCache: string; 54: 55: /** 56: * Cached configuration 57: */ 58: let _configCache: CraftProjectConfig; 59: 60: /** 61: * The minimum craft version required to use the top-level `workspaces` config. 62: * 63: * This is the release the workspaces feature ships in. A dev build of that 64: * release (e.g. `2.29.0-dev.0`) satisfies it via the pre-release relaxation in 65: * `checkMinimalConfigVersion`. 66: */ 67: export const WORKSPACES_MIN_VERSION = '2.29.0'; 68: 69: /** 70: * The name of ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/package.json</path>
<type>file</type>
<content>
1: {
2: "name": "@sentry/craft",
3: "version": "2.31.0-dev.0",
4: "description": "The universal sentry workflow CLI",
5: "main": "dist/craft",
6: "repository": "https://github.com/getsentry/craft",
7: "author": "Sentry",
8: "license": "MIT",
9: "bin": {
10: "craft": "dist/craft",
11: "sentry-craft": "dist/craft"
12: },
13: "devDependencies": {
14: "@aws-sdk/client-lambda": "^3.984.0",
15: "@eslint/js": "^9.17.0",
16: "@google-cloud/storage": "^7.14.0",
17: "@octokit/plugin-retry": "^7.1.2",
18: "@octokit/request-error": "^7.0.0",
19: "@octokit/rest": "^21.0.2",
20: "@sentry/esbuild-plugin": "^2.23.0",
21: "@sentry/node": "^10.27.0",
22: "@types/async": "^3.0.1",
23: "@types/aws4": "^1.5.1",
24: "@types/cli-table": "^0.3.0",
25: "@types/git-url-parse": "^9.0.0",
26: "@types/is-ci": "^2.0.0",
27: ...
RUN v4.1.8 /home/byk/Code/getsentry/craft-workspace-action-propagation
Test Files 4 passed (4)
Tests 81 passed (81)
Start at 11:18:03
Duration 2.38s (transform 1.19s, setup 0ms, import 3.72s, tests 633ms, environment 1ms)
> @sentry/craft@2.31.0-dev.0 lint /home/byk/Code/getsentry/craft-workspace-action-propagation
> eslint --cache --cache-strategy content
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/publish.ts
482:14 warning '_abortError' is defined but never used @typescript-eslint/no-unused-vars
499:14 warning '_abortError' is defined but never used @typescript-eslint/no-unused-vars
515:16 warning '_statusError' is defined but never used @typescript-eslint/no-unused-vars
521:18 warning '_diffError' is defined but never used @typescript-eslint/no-unused-vars
527:16 warning '_abortError' is defined but never used ...
yarn run v1.22.22 $ node scripts/generate-publish-issue-title-parser.js --check Done in 0.47s. yarn run v1.22.22 $ /home/byk/Code/getsentry/publish-workspace-acceptance/node_modules/.bin/vitest run src/modules/__tests__/details-from-context.js src/modules/__tests__/publish-workflow.js src/modules/__tests__/publish-location.js src/modules/__tests__/release-revision.js [1m[46m RUN [49m[22m [36mv4.1.0 [39m[90m/home/byk/Code/getsentry/publish-workspace-acceptance[39m [2m Test Files [22m [1m[32m4 passed[39m[22m[90m (4)[39m [2m Tests [22m [1m[32m32 passed[39m[22m[90m (32)[39m [2m Start at [22m 11:18:15 [2m Duration [22m 571ms[2m (transform 114ms, setup 0ms, import 222ms, tests 147ms, environment 1ms)[22m Done in 1.14s. yarn run v1.22.22 $ eslint src .github --ignore-pattern '!.github' Done in 0.99s.
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/pnpm-lock.yaml</path> <type>file</type> <content> 1: lockfileVersion: '9.0' 2: 3: settings: 4: autoInstallPeers: true 5: excludeLinksFromLockfile: false 6: 7: overrides: 8: undici: ^6.23.0 9: 10: importers: 11: 12: .: 13: dependencies: 14: '@actions/core': 15: specifier: ^2.0.0 16: version: 2.0.3 17: '@actions/github': 18: specifier: ^7.0.0 19: version: 7.0.0 20: '@sentry/node': 21: specifier: ^10.0.0 22: version: 10.71.0(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1)) 23: devDependencies: 24: eslint: 25: specifier: ^8.9.0 26: version: 8.57.1 27: eslint-config-prettier: 28: specifier: ^8.3.0 29: version: 8.10.2(eslint@8.57.1) 30: eslint-plugin-yml: 31: specifier: ^0.13.0 32: version: 0.13.0(eslint@8.57.1) 33: peggy: 34: specifier: 5.1.0 35: version: ...
P0 — FAIL: CI poller cannot process supported legacy titles.
.github/workflows/ci-poller.yml:99 requires owner/repo, although the canonical parser accepts optional getsentry/ (src/modules/publish-issue-title.peggy:11). It also treats JSON workspace titles containing / as part of the repository. Such accepted legacy issues never reach CI approval. Add parser-backed title parsing to the poller and regression coverage.
P1 — FAIL: Untracked pnpm lockfile violates the repository’s Yarn-only lockfile contract.
pnpm-lock.yaml:1 is untracked while the workflow installs with Yarn (.github/workflows/publish.yml:131-132) and the dependency change updates yarn.lock. Remove pnpm-lock.yaml before committing.
PASS: Exact revision flows from the canonical View check runs URL (src/modules/release-revision.js:1-13) through target checkout (.github/workflows/publish.yml:159-167) and craft publish --rev (.github/workflows/publish.yml:239-249). Detached SHAs remain supported (src/commands/publish.ts:632-637,930-943).
PASS: Compact workspace resolution preserves case-sensitive identity and rejects workspace/path coexistence (src/modules/publish-location.js:5-40; action.yml:74-87). Legacy JSON workspace/path coexistence also fails (src/modules/details-from-context.js:53-59).
PASS: Publish-state filename construction matches Craft’s owner/repo, cwd hash, base64url workspace prefix, and version sanitisation contract (.github/workflows/publish.yml:217-235; src/utils/publishState.ts:51-99), with no repository-local state fallback (src/commands/publish.ts:734-746).
PASS: Generated parser is current (scripts/generate-publish-issue-title-parser.js:20-37; src/modules/publish-issue-title.js:1-4). Focused tests, generated-parser check, and YAML lint pass.
DO-NOT-MERGE
Date: Sep 2, 2026 * 🔴 [requested-review] (11:14) User requested a strict read-only adversarial review of uncommitted compact workspace publish-contract changes in both worktrees: `/home/byk/Code/getsentry/craft-workspace-action-propagation` on branch `feat/workspace-action-propagation`, and `/home/byk/Code/getsentry/publish-workspace-acceptance` on branch `feat/workspace-acceptance`. * 🔴 [enfor…