Dashboard › craft › Session 0052pO1BGKrk
0052pO1BGKrkditf3Perform a strict final READ-ONLY adversarial audit of the complete CURRENT uncommitted diff and relevant source in BOTH worktrees. Do not edit files, format, generate, run mutating commands, or change git state.
Worktrees:
IMPORTANT CONTRACT: getsentry/craft:latest in Publish is intentional and REQUIRED. It must remain floating so the controller always uses the latest released Craft workspace behavior; the CI-approved revision passed to checkout and craft publish --rev binds all repository operations. Do not report latest as a concern or recommend pinning it.
Audit these invariants with exact current file:line citations:
getReleaseRevision or any API-oriented work; root allowed; traversal/dot/prototype/option-like segments rejected.getPublishStateFilename and Publish workflow’s Set targets use identical, lossless state-version encoding. Safe lowercase existing names remain unchanged; altered valid versions encode raw bytes base64url; case/punctuation variants cannot collide.packages/{cli,mcp} expand. Unsafe brace alternatives (relative traversal, absolute path, malformed syntax) fail before globSync. Verify safe character/negated classes, globstar, concrete literals, POSIX / paths, overlap failure, and physical symlink containment. Ensure schema/runtime checks are equivalent or safely layered.CRAFT_WORKSPACE, preserves full exact workspace titles and mutual exclusion..craft.yml to checkout-path behavior, validates discovery output, gates only on fresh accepted ci-ready, ...<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>
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/prepare.ts M src/config.ts M src/schemas/project_config.ts M src/utils/__tests__/publishState.test.ts M src/utils/publishState.ts action.yml | 11 +- docs/src/content/docs/targets/github.md | 20 ++- src/__tests__/action.test.ts | 101 +++++++++--- src/__tests__/config.test.ts | 176 ++++++++++++++++++++- src/commands/prepare.ts | 3 +- src/config.ts | 255 +++++++++++++++++++++++++++++-- src/schemas/project_config.ts | 90 +++++++++++ src/utils/__tests__/publishState.test.ts | 15 ++ src/utils/publishState.ts | 9 +- 9 files changed, 636 insertions(+), 44 ...
M .github/workflows/publish.yml M docs/publish-issue-format.md M src/modules/__tests__/ci-poller-input.js M src/modules/__tests__/details-from-context.js M src/modules/__tests__/publish-location.js M src/modules/__tests__/publish-workflow.js M src/modules/ci-poller-input.js M src/modules/details-from-context.js M src/modules/publish-issue-title.js M src/modules/publish-issue-title.peggy M src/modules/publish-location.js M src/publish/resolve-location.js ?? src/modules/publish-issue-validation.js .github/workflows/publish.yml | 14 +- docs/publish-issue-format.md | 56 +++---- src/modules/__tests__/ci-poller-input.js | 48 +++++- src/modules/__tests__/details-from-context.js | 219 ++++++------------------- src/modules/__tests__/publish-location.js | 94 +++++------ src/modules/__tests__/publish-workflow.js | 35 +++- src/modules/ci-poller-input.js | 15 ++ src/modules/details-from-context.js | ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/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>
Found 73 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/workspaces.ts:
Line 317: // If version is the default regex pattern, use it as-is; otherwise escape it
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/version.ts:
Line 42: * SemVer is a parsed semantic version.
Line 44: export interface SemVer {
Line 63: export function parseVersion(text: string): SemVer | null {
Line 81: export function versionGreaterOrEqualThan(v1: SemVer, v2: SemVer): boolean {
Line 164: * Returns the stringified version of the passed SemVer object.
Line 166: export function semVerToString(s: SemVer) {
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/targets/brew.ts:
Line 13: import { isPreviewRelease, parseVersion } from '../utils/version';
Line 144: const parsedVersion = parseVersion(version);
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/targets/awsLambdaLayer.ts:
Line 26: import { ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/version.ts</path>
<type>file</type>
<content>
1: import { getGitTagPrefix } from '../config';
2:
3: /**
4: * Regular expression for matching semver versions.
5: *
6: * Modified to match version components
7: * Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
8: * @see https://github.com/sindresorhus/semver-regex
9: */
10: const semverRegex = () =>
11: /\bv?(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(?:-?([\da-z-]+(?:\.[\da-z-]+)*))?(?:\+([\da-z-]+(?:\.[\da-z-]+)*))?\b/gi;
12:
13: /**
14: * Extracts a version number from the given text.
15: *
16: * In case the version contains a leading "v", it is stripped from the result.
17: * All semantic versions are supported. See {@link http://semver.org/} for
18: * more information. ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/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:
12: function getCiPollerInput({ title, issueBody, revision }) {
13: let parsedTitle;
14: try {
15: parsedTitle = parsePublishIssueTitle(title);
16: } catch {
17: throw new Error(`Invalid publish issue title: '${title}'`);
18: }
19: const { repo, version } = parsedTitle;
20: if (!isPublishRepository(repo)) {
21: throw new Error(`Invalid publish issue repository: '${repo}'`);
22: }
23: if (!isReleaseVersion(version)) {
24: throw new Error(`Invalid publish issue version: ...
Found 11 matches
/home/byk/Code/getsentry/publish-workspace-acceptance/src/publish/resolve-ci-poller-input.js:
Line 1: const { getCiPollerInput } = require("../modules/ci-poller-input");
Line 5: getCiPollerInput({
/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/ci-poller-input.js:
Line 12: function getCiPollerInput({ title, issueBody, revision }) {
Line 42: module.exports = { getCiPollerInput };
/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/ci-poller-input.js:
Line 6: const { getCiPollerInput } = require("../ci-poller-input.js");
Line 19: getCiPollerInput({
Line 30: getCiPollerInput({
Line 59: getCiPollerInput({
Line 71: getCiPollerInput({
Line 82: expect(() => getCiPollerInput({ issueBody, title })).toThrow(
Line 94: expect(() => getCiPollerInput({ issueBody, title })).toThrow(
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/publish/resolve-ci-poller-input.js</path>
<type>file</type>
<content>
1: const { getCiPollerInput } = require("../modules/ci-poller-input");
2:
3: process.stdout.write(
4: JSON.stringify(
5: getCiPollerInput({
6: issueBody: process.env.PUBLISH_ISSUE_BODY || "",
7: title: process.env.PUBLISH_TITLE || "",
8: revision: process.env.PUBLISH_REVISION || "",
9: })
10: )
11: );
(End of file - total 11 lines)
</content>
Found 4 matches
/home/byk/Code/getsentry/publish-workspace-acceptance/.github/workflows/publish.yml:
Line 96: gh workflow run ci-poller.yml -R "$GITHUB_REPOSITORY"
/home/byk/Code/getsentry/publish-workspace-acceptance/.github/workflows/ci-poller.yml:
Line 101: node .__publish__/src/publish/resolve-ci-poller-input.js); then
Line 146: node .__publish__/src/publish/resolve-ci-poller-input.js | jq -r '.issueBody')
Line 308: gh workflow run ci-poller.yml -R "$GITHUB_REPOSITORY" -f attempt="${attempt}"
<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/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/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', result);
10: }
11:
12:
13: inputs();
(End of file - total 13 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/src/modules/release-revision.js</path>
<type>file</type>
<content>
1: const CHECK_RUNS_LINK =
2: /^Requested by: @[^\r\n]+\r?\n(?:[ \t]*\r?\n)?[ \t]*Merge target: [^\r\n]+\r?\n(?:[ \t]*\r?\n)?[ \t]*Quick links:\r?\n(?:[ \t]*\r?\n)?[ \t]*- \[View changes\]\([^\r\n]+\)\r?\n[ \t]*- \[View check runs\]\(https:\/\/github\.com\/getsentry\/(?<repo>[A-Za-z0-9_.-]+)\/commit\/(?<revision>[0-9a-f]{40})\/checks\/?\)(?=\r?\n|$)/;
3: const CHECK_RUNS_LINK_COUNT = /^[ \t]*- \[View check runs\]\(/gm;
4:
5: function getReleaseRevision({ issueBody, repo }) {
6: if ((issueBody.match(CHECK_RUNS_LINK_COUNT) || []).length !== 1) {
7: throw new Error(
8: `Expected exactly one View check runs link in Quick links for getsentry/${repo}.`
9: );
10: }
11:
12: const match = issueBody.match(CHECK_RUNS_LINK);
13: if (!match?.groups || match.groups.repo !== repo) {
14: throw new Error(
15: `Expected a View check runs link ...
<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/src/config.ts</path>
<type>file</type>
<content>
150: };
151: // Only adopt the merged github if it is complete (has owner + repo).
152: // Otherwise leave `github` unset so getGlobalGitHubConfig() can still
153: // fall back to git-remote detection instead of seeing a truthy-but-
154: // incomplete object and skipping the fallback.
155: if (mergedGithub.owner && mergedGithub.repo) {
156: resolved.github = mergedGithub as GitHubGlobalConfig;
157: } else {
158: delete (resolved as { github?: unknown }).github;
159: }
160: } else {
161: (resolved as Record<string, unknown>)[key] = value;
162: }
163: }
164:
165: return resolved;
166: }
167:
168: function getWorkspaceConfig(
169: workspaces: NonNullable<CraftProjectConfig['workspaces']>,
170: workspaceName: string,
171: workspaceDirectory: string,
172: ): Workspace | undefined {
173: ...
No files found
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/node_modules/glob</path> <type>directory</type> <entries> dist/ LICENSE.md package.json README.md (4 entries) </entries>
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/node_modules/glob/dist</path> <type>directory</type> <entries> commonjs/ esm/ (2 entries) </entries>
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/node_modules/glob/dist/commonjs</path> <type>directory</type> <entries> glob.d.ts glob.d.ts.map glob.js glob.js.map has-magic.d.ts has-magic.d.ts.map has-magic.js has-magic.js.map ignore.d.ts ignore.d.ts.map ignore.js ignore.js.map index.d.ts index.d.ts.map index.js index.js.map package.json pattern.d.ts pattern.d.ts.map pattern.js pattern.js.map processor.d.ts processor.d.ts.map processor.js processor.js.map walker.d.ts walker.d.ts.map walker.js walker.js.map (29 entries) </entries>
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/node_modules/glob/dist/commonjs/has-magic.js</path>
<type>file</type>
<content>
1: "use strict";
2: Object.defineProperty(exports, "__esModule", { value: true });
3: exports.hasMagic = void 0;
4: const minimatch_1 = require("minimatch");
5: /**
6: * Return true if the patterns provided contain any magic glob characters,
7: * given the options provided.
8: *
9: * Brace expansion is not considered "magic" unless the `magicalBraces` option
10: * is set, as brace expansion just turns one string into an array of strings.
11: * So a pattern like `'x{a,b}y'` would return `false`, because `'xay'` and
12: * `'xby'` both do not contain any magic glob characters, and it's treated the
13: * same as if you had called it on `['xay', 'xby']`. When `magicalBraces:true`
14: * is in the options, brace expansion _is_ treated as a pattern having magic.
15: */
16: const hasMagic = (pattern, options = {}) => {
17: if ...
No files found
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/node_modules/glob/package.json</path>
<type>file</type>
<content>
1: {
2: "author": "Isaac Z. Schlueter <i@izs.me> (https://blog.izs.me/)",
3: "name": "glob",
4: "description": "the most correct and second fastest glob implementation in JavaScript",
5: "version": "11.1.0",
6: "type": "module",
7: "tshy": {
8: "main": true,
9: "exports": {
10: "./package.json": "./package.json",
11: ".": "./src/index.ts"
12: }
13: },
14: "bin": "./dist/esm/bin.mjs",
15: "main": "./dist/commonjs/index.js",
16: "types": "./dist/commonjs/index.d.ts",
17: "exports": {
18: "./package.json": "./package.json",
19: ".": {
20: "import": {
21: "types": "./dist/esm/index.d.ts",
22: "default": "./dist/esm/index.js"
23: },
24: "require": {
25: "types": "./dist/commonjs/index.d.ts",
26: "default": "./dist/commonjs/index.js"
27: }
28: }
29: ...
No files found
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/node_modules/.pnpm</path> <type>directory</type> <entries> @apm-js-collab+code-transformer@0.8.2/ @apm-js-collab+tracing-hooks@0.3.1/ @aws-crypto+crc32@5.2.0/ @aws-crypto+sha256-browser@5.2.0/ @aws-crypto+sha256-js@5.2.0/ @aws-crypto+supports-web-crypto@5.2.0/ @aws-crypto+util@5.2.0/ @aws-sdk+client-lambda@3.984.0/ @aws-sdk+client-sso@3.982.0/ @aws-sdk+core@3.973.6/ @aws-sdk+credential-provider-env@3.972.4/ @aws-sdk+credential-provider-http@3.972.6/ @aws-sdk+credential-provider-ini@3.972.4/ @aws-sdk+credential-provider-login@3.972.4/ @aws-sdk+credential-provider-node@3.972.5/ @aws-sdk+credential-provider-process@3.972.4/ @aws-sdk+credential-provider-sso@3.972.4/ @aws-sdk+credential-provider-web-identity@3.972.4/ @aws-sdk+middleware-host-header@3.972.3/ @aws-sdk+middleware-logger@3.972.3/ @aws-sdk+middleware-recursion-detection@3.972.3/ @aws-sdk+middleware-user-agent@3.972.6/ @aws-sdk+nested-clients@3.982.0/ @aws-sdk+regi...
File not found: /home/byk/Code/getsentry/craft-workspace-action-propagation/node_modules/.pnpm/minimatch@10.1.1/node_modules/minimatch/dist/commonjs/index.js
No files found
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/node_modules/.pnpm</path> <type>directory</type> <entries> callsites@3.1.0/ caniuse-lite@1.0.30001763/ chai@6.2.2/ chalk@4.1.1/ chokidar@3.6.0/ chownr@1.1.4/ chownr@3.0.0/ ci-info@2.0.0/ ci-info@4.3.1/ cjs-module-lexer@1.2.3/ cjs-module-lexer@1.4.3/ cli-cursor@3.1.0/ cli-spinners@2.9.2/ cli-table@0.3.1/ cliui@9.0.1/ clone@1.0.4/ color-convert@2.0.1/ color-name@1.1.4/ colors@1.0.3/ combined-stream@1.0.8/ commander@12.1.0/ consola@2.15.3/ convert-source-map@2.0.0/ cookie@0.7.2/ core-util-is@1.0.3/ cross-spawn@7.0.6/ debug@4.4.3/ deep-is@0.1.4/ defaults@1.0.4/ define-data-property@1.1.4/ delayed-stream@1.0.0/ dotenv@16.6.1/ dunder-proto@1.0.1/ duplexify@4.1.3/ eastasianwidth@0.2.0/ ecdsa-sig-formatter@1.0.11/ electron-to-chromium@1.5.267/ emoji-regex@10.6.0/ emoji-regex@8.0.0/ emoji-regex@9.2.2/ end-of-stream@1.4.5/ es-define-property@1.0.1/ es-errors@1.3.0/ es-module-lexer@1.5.0/ es-module-lexer@2.1.0/ es-object-atoms@1.1....
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/node_modules/.pnpm</path> <type>directory</type> <entries> get-tsconfig@4.14.0/ git-up@8.1.1/ git-url-parse@16.1.0/ glob-parent@5.1.2/ glob-parent@6.0.2/ glob@11.1.0/ glob@9.3.5/ globals@14.0.0/ google-auth-library@9.15.1/ google-logging-utils@0.0.2/ gopd@1.2.0/ graceful-fs@4.2.11/ gtoken@7.1.0/ has-flag@4.0.0/ has-property-descriptors@1.0.2/ has-symbols@1.1.0/ has-tostringtag@1.0.2/ hasown@2.0.4/ html-entities@2.6.0/ http-proxy-agent@5.0.0/ http-proxy@1.18.1/ https-proxy-agent@5.0.1/ https-proxy-agent@7.0.6/ ieee754@1.2.1/ ignore@4.0.6/ ignore@5.3.2/ ignore@7.0.5/ import-fresh@3.3.1/ import-in-the-middle@2.0.0/ imurmurhash@0.1.4/ inherits@2.0.4/ is-binary-path@2.1.0/ is-callable@1.2.7/ is-ci@2.0.0/ is-extglob@2.1.1/ is-fullwidth-code-point@3.0.0/ is-glob@4.0.3/ is-interactive@1.0.0/ is-number@7.0.0/ is-ssh@1.4.1/ is-stream@2.0.1/ is-typed-array@1.1.15/ is-unicode-supported@0.1.0/ isarray@1.0.0/ isarray@2.0.5/ isexe@2.0....
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/node_modules/.pnpm/minimatch@10.2.6/node_modules/minimatch/dist/commonjs/index.js</path>
<type>file</type>
<content>
1: "use strict";
2: Object.defineProperty(exports, "__esModule", { value: true });
3: exports.unescape = exports.escape = exports.AST = exports.Minimatch = exports.match = exports.makeRe = exports.braceExpand = exports.defaults = exports.filter = exports.GLOBSTAR = exports.sep = exports.minimatch = void 0;
4: const brace_expansion_1 = require("brace-expansion");
5: const assert_valid_pattern_js_1 = require("./assert-valid-pattern.js");
6: const ast_js_1 = require("./ast.js");
7: const escape_js_1 = require("./escape.js");
8: const unescape_js_1 = require("./unescape.js");
9: const minimatch = (p, pattern, options = {}) => {
10: (0, assert_valid_pattern_js_1.assertValidPattern)(pattern);
11: // shortcut: comments match nothing.
12: if (!options.nocomment && pattern.charAt(0) === '#') {
13: ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/node_modules/.pnpm/minimatch@10.2.6/node_modules/minimatch/dist/commonjs/index.js</path>
<type>file</type>
<content>
250: return false;
251: }
252: debug(..._) { }
253: make() {
254: const pattern = this.pattern;
255: const options = this.options;
256: // empty patterns and comments match nothing.
257: if (!options.nocomment && pattern.charAt(0) === '#') {
258: this.comment = true;
259: return;
260: }
261: if (!pattern) {
262: this.empty = true;
263: return;
264: }
265: // step 1: figure out negation, etc.
266: this.parseNegate();
267: // step 2: expand braces
268: this.globSet = [...new Set(this.braceExpand())];
269: if (options.debug) {
270: //oxlint-disable-next-line no-console
271: this.debug = (...args) => ...
Found 2 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/node_modules/.pnpm/minimatch@10.2.6/node_modules/minimatch/dist/commonjs/index.js:
Line 266: this.parseNegate();
Line 601: parseNegate() {
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/node_modules/.pnpm/minimatch@10.2.6/node_modules/minimatch/dist/commonjs/index.js</path>
<type>file</type>
<content>
580: bi++;
581: }
582: else if (b[bi] === '*' &&
583: a[ai] &&
584: (this.options.dot || !a[ai].startsWith('.')) &&
585: a[ai] !== '**') {
586: if (which === 'a')
587: return false;
588: which = 'b';
589: result.push(b[bi]);
590: ai++;
591: bi++;
592: }
593: else {
594: return false;
595: }
596: }
597: // if we fall out of the loop, it means they two are identical
598: // as long as their lengths match
599: return a.length === b.length && result;
600: }
601: parseNegate() {
602: if (this.nonegate)
603: ...
<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. ...
<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 the complete suffix as a workspace after checking
9: // out the CI-approved revision.
10: PublishIssueTitle
11: = "publish: " "getsentry/"? repo:Repository path:Path? "@" version:Version !. {
12: return {
13: repo,
14: path: path || "",
15: version,
16: };
17: }
18:
19: Repository
20: = characters:RepositoryCharacter+ { return join(characters); }
21:
22: RepositoryCharacter
23: = [A-Za-z0-9_.-]
24:
25: Path
26: = segments:("/" segment:PathSegment { return `/${segment}`; })+ { return join(segments); }
27:
28: PathSegment
29: = characters:RepositoryCharacter+ { return join(characters); }
30:
31: ...
/home/byk/Code/getsentry/publish-workspace-acceptance/scripts/generate-publish-issue-title-parser.js /home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-issue-title.js /home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-issue-title.peggy
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/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: "generate": "node scripts/generate-publish-issue-title-parser.js",
19: "check:generated": "node scripts/generate-publish-issue-title-parser.js --check",
20: "test": "yarn check:generated && vitest run",
21: "test:watch": "vitest",
22: "lint": "eslint src .github --ignore-pattern '!.github'",
23: "prettier": "prettier --write src"
24: },
25: "devDependencies": {
26: "eslint": ...
<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/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}- uses: docker:\/\/getsentry\/craft:latest/
23: );
24: if (!section?.groups?.script) {
25: throw new Error("Missing Set targets workflow ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/__tests__/config.test.ts</path>
<type>file</type>
<content>
350: test('a different workspace resolves independently', () => {
351: setActiveWorkspace('mcp');
352: const config = loadConfigurationFromString(WS_CONFIG);
353: expect(config.releaseBranchPrefix).toBe('release/mcp');
354: expect(getGitTagPrefix()).toBe('mcp@');
355: expect(getVersioningPolicy()).toBe('calver');
356: expect(config.github).toEqual({ owner: 'getsentry', repo: 'toolkit' });
357: });
358:
359: test('errors when workspaces are defined but none is selected', () => {
360: setActiveWorkspace(undefined);
361: expect(() => loadConfigurationFromString(WS_CONFIG)).toThrow(
362: /defines workspaces; select one/,
363: );
364: });
365:
366: test('lists concrete workspace names without requiring a selection', () => {
367: setActiveWorkspace(undefined);
368: const directory = ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/__tests__/action.test.ts</path>
<type>file</type>
<content>
1: import {
2: chmodSync,
3: mkdtempSync,
4: mkdirSync,
5: readFileSync,
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 { load } from 'js-yaml';
14: import { afterEach, expect, test } from 'vitest';
15:
16: interface ActionStep {
17: env?: Record<string, string>;
18: name?: string;
19: run?: string;
20: }
21:
22: function getActionSteps(): ActionStep[] {
23: const action = load(
24: readFileSync(join(__dirname, '../../action.yml'), 'utf8'),
25: ) as {
26: runs?: { steps?: ActionStep[] };
27: };
28: return action.runs?.steps || [];
29: }
30:
31: function getActionStep(name: string): ActionStep {
32: const step = getActionSteps().find(step => step.name === name);
33: if (!step?.run) {
34: ...
<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: const { isPublishPath } = require("./publish-location");
3: const {
4: isPublishRepository,
5: isReleaseVersion,
6: } = require("./publish-issue-validation");
7:
8: /**
9: * Matches the entire "Targets" section of a github publish issue body.
10: */
11: const TARGETS_SECTION_PARSER_REGEX =
12: /^(?!### Targets$\s)(?: *- \[[ xX]\] \S+\s*$(?:\r?\n)?)+/m;
13:
14: /**
15: * Matches all targets of a github publish issue body in a section that was already matched and extracted with `TARGETS_PARSER_REGEX`.
16: * The "id" of the targets is captured within a capture group. ...
<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 { 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: ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/docs/publish-issue-format.md</path>
<type>file</type>
<content>
1: # Publish Issue Format
2:
3: The Publish workflow treats a publish issue as a release request. The title identifies
4: the release; the body supplies the merge target and selected targets. Do not edit the
5: title by hand unless it remains valid under this format.
6:
7: ## Title
8:
9: Every title starts with `publish: `. This EBNF is canonical:
10:
11: ```text
12: title = "publish: ", [ "getsentry/" ], repository, [ path ],
13: "@", version ;
14: repository = token, { token } ;
15: path = "/", path-segment, { "/", path-segment } ;
16: path-segment = token, { token } ;
17: version = version-character, { version-character } ;
18: token = ? ASCII letter, digit, ".", "_", or "-" ? ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/schemas/project_config.ts</path>
<type>file</type>
<content>
170: *
171: * These are shared between the top-level config (the implicit/default release
172: * unit) and each entry under the top-level `workspaces` map (an explicit,
173: * independently-versioned release unit). A workspace inherits the top-level
174: * values as defaults and overrides the fields it declares.
175: *
176: * NOTE: this "workspace" (a named, independently-versioned release unit) is a
177: * different concept from the `npm` target's `workspaces: true` field, which
178: * discovers npm packages *within* a single target and publishes them all at the
179: * same version. See docs for the disambiguation.
180: */
181: const releaseUnitFields = {
182: github: GitHubGlobalConfigSchema.optional(),
183: targets: z.array(TargetConfigSchema).optional(),
184: preReleaseCommand: z.string().optional(),
185: postReleaseCommand: ...
yarn run v1.22.22 $ node scripts/generate-publish-issue-title-parser.js --check Done in 0.52s.
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-issue-title.js</path>
<type>file</type>
<content>
195: const peg$source = options.grammarSource;
196:
197: const peg$startRuleFunctions = {
198: PublishIssueTitle: peg$parsePublishIssueTitle,
199: };
200: let peg$startRuleFunction = peg$parsePublishIssueTitle;
201:
202: const peg$c0 = "publish: ";
203: const peg$c1 = "getsentry/";
204: const peg$c2 = "@";
205: const peg$c3 = "/";
206:
207: const peg$r0 = /^[A-Za-z0-9_.\-]/;
208: const peg$r1 = /^[A-Za-z0-9_.+\-]/;
209:
210: const peg$e0 = peg$literalExpectation("publish: ", false);
211: const peg$e1 = peg$literalExpectation("getsentry/", false);
212: const peg$e2 = peg$literalExpectation("@", false);
213: const peg$e3 = peg$anyExpectation();
214: const peg$e4 = peg$classExpectation(
215: [["A", "Z"], ["a", "z"], ["0", "9"], "_", ".", "-"],
216: false,
217: false,
218: false
219: );
220: const peg$e5 ...
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/prepare.ts M src/config.ts M src/schemas/project_config.ts M src/utils/__tests__/publishState.test.ts M src/utils/publishState.ts
M .github/workflows/publish.yml M docs/publish-issue-format.md M src/modules/__tests__/ci-poller-input.js M src/modules/__tests__/details-from-context.js M src/modules/__tests__/publish-location.js M src/modules/__tests__/publish-workflow.js M src/modules/ci-poller-input.js M src/modules/details-from-context.js M src/modules/publish-issue-title.js M src/modules/publish-issue-title.peggy M src/modules/publish-location.js M src/publish/resolve-location.js ?? src/modules/publish-issue-validation.js
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/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:
8: const REVISION = "7e5ca7ed5581552de066e2a8bc295b8306be38ac";
9: const issueBody = `Requested by: @byk
10:
11: Merge target: (default)
12:
13: Quick links:
14: - [View changes](https://github.com/getsentry/toolkit/compare/1.2.2...release/1.2.3)
15: - [View check runs](https://github.com/getsentry/toolkit/commit/${REVISION}/checks/)`;
16:
17: test("parses root and full-path publish titles", () => {
18: expect(
19: getCiPollerInput({
20: issueBody,
21: title: "publish: getsentry/toolkit/cli@1.2.3",
22: })
23: ).toEqual({
24: repo: "getsentry/toolkit",
25: revision: REVISION,
26: version: "1.2.3",
27: });
28:
29: ...
Found 2 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/publish.ts:
Line 629: const rev = argv.rev;
Line 680: const revision = await git.revparse('HEAD');
<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 6 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/publish.ts:
Line 634: branchName = await getRevisionBranchName(git, rev);
Line 933: export async function getRevisionBranchName(
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/__tests__/publish.test.ts:
Line 6: getRevisionBranchName,
Line 213: describe('getRevisionBranchName', () => {
Line 219: await expect(getRevisionBranchName(git, 'abc123')).resolves.toBe(
Line 235: await expect(getRevisionBranchName(git, 'abc123')).resolves.toBe('');
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/publish.ts</path>
<type>file</type>
<content>
900: }
901: lines.push(
902: ``,
903: `Error: ${mergeError instanceof Error ? mergeError.message : String(mergeError)}`,
904: );
905: logger.warn(lines.join('\n'));
906: }
907:
908: // XXX(BYK): intentionally DO NOT await unlinking as we do not want
909: // to block (both in terms of waiting for IO and the success of the
910: // operation) finishing the publish flow on the removal of a temporary
911: // file. If unlinking fails, we honestly don't care, at least to fail
912: // the final steps. And it doesn't make sense to wait until this op
913: // finishes then as nothing relies on the removal of this file.
914: safeFs
915: .unlink(publishStateFile)
916: .catch((err: unknown) =>
917: logger.trace("Couldn't remove publish state file: ", err),
918: );
919: ...
Found 3 matches
/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/publish-location.js:
Line 11: ).toStrictEqual({ path: ".", workspace: "packages/CLI" });
/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/publish-workflow.js:
Line 90: "CRAFT_PUBLISH_WORKSPACE: ${{ fromJSON(steps.location.outputs.result).workspace || '' }}"
Line 129: workspace: location.workspace,
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/__tests__/publishState.test.ts</path>
<type>file</type>
<content>
70: test('disambiguates release workspaces at the same repo, cwd, and version', () => {
71: const cli = getPublishStateFilename(
72: '1.2.3',
73: { owner: 'o', repo: 'r' },
74: cwd,
75: 'cli',
76: );
77: const mcp = getPublishStateFilename(
78: '1.2.3',
79: { owner: 'o', repo: 'r' },
80: cwd,
81: 'mcp',
82: );
83: expect(cli).not.toBe(mcp);
84: expect(cli).toMatch(/-workspace-Y2xp-1\.2\.3\.json$/);
85: expect(mcp).toMatch(/-workspace-bWNw-1\.2\.3\.json$/);
86: });
87:
88: test('does not collide when workspace names only differ by case or punctuation', () => {
89: const names = ['CLI', 'cli', 'cli/workspace', 'cli_workspace'];
90: const filenames = names.map(workspace =>
91: getPublishStateFilename(
92: ...
MUST-FIX — High: Malformed brace syntax passes validation. Both implementations return [] for unclosed or non-alternative braces, then accept it through vacuous .every() success: Craft runtime src/config.ts:287-294,327-349; schema src/schemas/project_config.ts:249-285,305-317. Thus keys such as packages/{cli and packages/{cli} are accepted rather than rejected before glob handling. Existing tests cover balanced alternatives but not malformed braces: src/__tests__/config.test.ts:451-515.
PASS: CI input validates repository, Craft-compatible version, and canonical path before getReleaseRevision: publish/src/modules/ci-poller-input.js:12-30. Root . is allowed; unsafe segments are rejected in publish-location.js:19-44. Target-repository API calls occur only after parsed input is consumed: .github/workflows/ci-poller.yml:100-115.
PASS: Craft and Publish use matching version encoding: craft/src/utils/publishState.ts:58-63,95-102; publish/.github/workflows/publish.yml:217-233. Both preserve already-safe lowercase versions and base64url-encode altered values. Regression coverage checks case-distinct encoded names: craft/src/utils/__tests__/publishState.test.ts:101-123; publish/src/modules/__tests__/publish-workflow.js:138-152.
PASS: The action validates path/workspace before later steps, enforces exclusivity, and rejects unsafe segments: craft/action.yml:74-92. It clears inherited CRAFT_WORKSPACE before both Craft invocations: action.yml:180-225; full workspace paths remain intact in titles: action.yml:245-275. Tests cover unsafe inputs, full paths, and ambient-variable clearing: src/__tests__/action.test.ts:204-312.
PASS: Publish resolves the release revision, checks out that SHA before discovery, then performs exact case-sensitive workspace matching: publish.yml:138-182; src/modules/publish-location.js:1-44. It gates on a fresh accepted ci-ready event: publish.yml:102-114, isolates ...
Date: Sep 4, 2026 * 🔴 (20:09) User stated package-management policy for `craft-workspace-action-propagation`: always use `pnpm`; never use `npm` or `yarn`. * 🟡 (20:09) Read-only `AGENTS.md` output for `/home/byk/Code/getsentry/craft-workspace-action-propagation` specifies Node.js v22.12.0 managed by Volta; dependency installation command `pnpm install --frozen-lockfile`; development commands `p…
Date: Sep 4, 2026 * 🔴 (20:09) User stated that new Craft publish requests always include the checkout repository identity. * 🔴 (20:09) User stated that a missing root `.craft.yml` always means checkout-path behavior. * 🔴 (20:09) User stated that the controller never normalizes publish-location names. * 🟡 (20:09) Read-only diff output for `/home/byk/Code/getsentry/publish-workspace-acceptance`…
Date: Sep 4, 2026 * 🔴 (20:09) User stated that Action publish titles always carry the full concrete workspace path; `craft workspace list` outputs concrete workspace paths as a JSON array for automation. * 🟡 (20:09) Read-only tool output enumerated `/home/byk/Code/getsentry/craft-workspace-action-propagation` (33 entries), including `.craft.yml`, `action.yml`, `Dockerfile`, `README.md`, `docs/`…
Date: Sep 4, 2026 * 🔴 (20:08) User directive: never return empty. * 🔴 [requested-read-only-audit] (20:08) User requested a strict final READ-ONLY adversarial audit of the complete CURRENT uncommitted diff and relevant source in both worktrees: `/home/byk/Code/getsentry/craft-workspace-action-propagation` and `/home/byk/Code/getsentry/publish-workspace-acceptance`. * 🔴 (20:08) User required the…