Dashboard › craft › Session 1abfTSgx5xe4
1abfTSgx5xe4ySSnvPerform a strict read-only adversarial audit of the exact uncommitted changes in both /home/byk/Code/getsentry/craft-workspace-action-propagation and /home/byk/Code/getsentry/publish-workspace-acceptance. Do not edit any files or run formatters. Inspect the complete diffs plus relevant source, tests, action/workflow behavior, generated parser, and docs. Check: full concrete path identity without normalization; glob expansion correct relative to config/root including --config-from; traversal/absolute/backslash/prototype safety before filesystem or checkout side effects; legacy compatibility required by current code; CI-approved revision ordering; state filename isolation; CI labels contract; title grammar/parser sync; workflow injection risk; and only intended files changed. Run only read-only checks if useful. Return severity-ordered findings with exact file:line citations, explicit PASS/FAIL for each listed check, and end exactly MERGE or DO-NOT-MERGE. If blocked, begin BLOCKED and name the exact issue; never return empty.
<skill_content name="jj-guide"> # Skill: jj-guide # jj Guide for AI Agents Jujutsu (jj) is a Git-compatible VCS with mutable commits, automatic snapshotting, no staging area, and first-class conflicts. This skill teaches you how to use it safely from a non-interactive agent environment. If `.jj/` exists in the repo root, this is a jj repo. **Use `jj` commands, not `git`.** In a colocated repo (`.jj/` *and* `.git/`), git tools can read the state, but mutations should go through `jj` so the operation log stays consistent. ## Critical Rules - **NEVER** use interactive flags (`-i`, `--interactive`). TUI prompts hang in agent environments. This applies to `jj split -i`, `jj squash -i`, `jj commit -i`, `jj resolve`, `jj diffedit`, etc. - **ALWAYS** pass `-m "msg"` when describing/committing. Without `-m`, an editor opens and hangs. - **VERIFY** mutations with `jj st` and `jj log` after `squash`, `abandon`, `rebase`, `restore`, `commit`. ...
<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 README.md scripts/ src/ vitest.config.js yarn.lock (15 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
action.yml | 4 +-
docs/src/content/docs/targets/github.md | 19 ++--
src/__tests__/action.test.ts | 55 ++++++++----
src/__tests__/config.test.ts | 71 ++++++++++++++-
src/commands/prepare.ts | 3 +-
src/config.ts | 149 +++++++++++++++++++++++++++++---
6 files changed, 260 insertions(+), 41 deletions(-)
diff --git a/action.yml b/action.yml
index 94f3158cc43d5167de21fe400b0162b0cb62d44d..84377a84ad432e202c0d739c502ded29bd102202 100644
--- a/action.yml
+++ b/action.yml
@@ -81,8 +81,8 @@ runs:
echo "::error::The path and workspace inputs cannot be used together."
exit 1
fi
- if [[ -n "$WORKSPACE" ]] && ! node -e ...
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/details-from-context.js M src/modules/post-workflow-details.js M src/modules/publish-issue-title.js M src/modules/publish-issue-title.peggy M src/modules/publish-location.js M src/publish/inputs.js M src/publish/resolve-location.js .github/workflows/publish.yml | 7 +- docs/publish-issue-format.md | 40 ++--- src/modules/__tests__/ci-poller-input.js | 4 +- src/modules/__tests__/details-from-context.js | 191 ++-------------------- src/modules/__tests__/publish-location.js | 85 ++++------ src/modules/__tests__/publish-workflow.js | 8 +- src/modules/details-from-context.js | 29 +--- src/modules/post-workflow-details.js | 4 +- ...
Found 14 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/schemas/project_config.ts:
Line 171: * unit) and each entry under the top-level `workspaces` map (an explicit,
Line 172: * independently-versioned release unit). A workspace inherits the top-level
Line 175: * NOTE: this "workspace" (a named, independently-versioned release unit) is a
Line 176: * different concept from the `npm` target's `workspaces: true` field, which
Line 200: * Configuration for a single named workspace (release unit).
Line 202: * A workspace mirrors the release-relevant subset of the top-level config;
Line 204: * `github` block is *partial* (all fields optional) so a workspace can override
Line 207: export const WorkspaceSchema = z.object({
Line 216: export type Workspace = z.infer<typeof WorkspaceSchema>;
Line 221: // preserving an own workspace entry. ...
Found 83 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/helpers.ts:
Line 26: * `CRAFT_WORKSPACE` env var) before yargs parsing.
Line 37: * back to `CRAFT_WORKSPACE`.
Line 40: * @param env The environment to read CRAFT_WORKSPACE from (defaults to process.env)
Line 46: const envWorkspace = env.CRAFT_WORKSPACE || undefined;
Line 49: options: { workspace: { type: 'string' } },
Line 54: let workspace: string | undefined;
Line 60: // an earlier value selected; it falls back to CRAFT_WORKSPACE instead.
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/__tests__/helpers.test.ts:
Line 65: test('CLI flag wins over CRAFT_WORKSPACE env', () => {
Line 68: CRAFT_WORKSPACE: 'mcp',
Line 86: { CRAFT_WORKSPACE: 'mcp' } as NodeJS.ProcessEnv,
Line 91: test('falls back to CRAFT_WORKSPACE when no flag', () => {
Line 94: CRAFT_WORKSPACE: 'mcp',
Line 103: ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/schemas/project_config.ts</path>
<type>file</type>
<content>
155: /**
156: * Changelog configuration
157: */
158: export const ChangelogConfigSchema = z.union([
159: z.string(),
160: z.object({
161: filePath: z.string().optional(),
162: policy: z.enum(['auto', 'simple', 'none']).optional(),
163: scopeGrouping: z.boolean().optional(),
164: }),
165: ]);
166:
167: /**
168: * Fields that describe how a single release unit is built and published.
169: *
170: * These are shared between the top-level config (the implicit/default release
171: * unit) and each entry under the top-level `workspaces` map (an explicit,
172: * independently-versioned release unit). A workspace inherits the top-level
173: * values as defaults and overrides the fields it declares. ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/config.ts</path>
<type>file</type>
<content>
1: import { existsSync, lstatSync, readFileSync } from 'fs';
2: import path from 'path';
3: import { globSync, hasMagic } from 'glob';
4:
5: import { load } from 'js-yaml';
6: import GitUrlParse from 'git-url-parse';
7: import { createGitClient } from './utils/git';
8: import { ZodError } from 'zod';
9:
10: import { logger } from './logger';
11: import {
12: CraftProjectConfig,
13: CraftProjectConfigSchema,
14: GitHubGlobalConfig,
15: ArtifactProviderName,
16: StatusProviderName,
17: TargetConfig,
18: ChangelogPolicy,
19: VersioningPolicy,
20: Workspace,
21: } from './schemas/project_config';
22: import { ConfigurationError } from './utils/errors';
23: import { isCompiledGitHubAction } from './utils/detection';
24: import {
25: getPackageVersion,
26: parseVersion,
27: versionGreaterOrEqualThan,
28: SemVer,
29: } from './utils/version';
30: // ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/prepare.ts</path>
<type>file</type>
<content>
740:
741: const calverFormat =
742: config.versioning?.calver?.format ?? DEFAULT_CALVER_CONFIG.format;
743:
744: return calculateCalVer(git, {
745: offset: calverOffset,
746: format: calverFormat,
747: });
748: }
749:
750: // Handle automatic version detection or version bump types
751: if (version === 'auto' || isBumpType(version)) {
752: if (!requiresMinVersion(AUTO_VERSION_MIN_VERSION)) {
753: const featureName = isBumpType(version)
754: ? 'Version bump types'
755: : 'Auto-versioning';
756: throw new ConfigurationError(
757: `${featureName} requires minVersion >= ${AUTO_VERSION_MIN_VERSION} in .craft.yml. ` +
758: 'Please update your configuration or specify the version explicitly.',
759: );
760: }
761:
762: const latestTag = await getLatestTag(git, ...
<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/craft-workspace-action-propagation/action.yml</path>
<type>file</type>
<content>
260: # GitHub issue bodies are limited to ~65 536 characters. Truncate
261: # the changelog to stay under that limit with room for the rest of
262: # the body template (~2 KB of surrounding markdown).
263: MAX_CHANGELOG_CHARS=60000
264: if [[ ${#CHANGELOG} -gt $MAX_CHANGELOG_CHARS ]]; then
265: CHANGELOG="${CHANGELOG:0:$MAX_CHANGELOG_CHARS}"$'\n\n---\n*Changelog truncated for issue body.*'
266: fi
267:
268: title="publish: ${GITHUB_REPOSITORY}${SUBDIRECTORY}${WORKSPACE:+/$WORKSPACE}@${RESOLVED_VERSION}"
269:
270: # Check if issue already exists by listing all open issues and filtering by exact title match.
271: # We avoid GitHub search API to bypass indexing delays and query syntax edge cases. ...
<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/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: ...
<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. ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/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 (path === ".") {
7: return { path };
8: }
9:
10: if (!workspaceNames.every(isWorkspaceName)) {
11: throw new Error(
12: "Craft workspace discovery returned an invalid workspace list."
13: );
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: (/^\.\/[A-Za-z0-9_.-]+(?:\/[A-Za-z0-9_.-]+)*$/.test(path) &&
29: !path.slice(2).split("/").some((segment) => segment === "." || segment === "..")))
30: );
31: }
32:
33: ...
<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/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/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/craft-workspace-action-propagation/src/__tests__/config.test.ts</path>
<type>file</type>
<content>
280: loadWithTargets([
281: { name: 'github', tagPrefix: 'cli@' },
282: { name: 'github', id: 'second' },
283: ]);
284: // A mixed defined/undefined prefix is still ambiguous.
285: expect(getGitTagPrefix()).toBe('cli@');
286: expect(warnSpy).toHaveBeenCalledTimes(1);
287: });
288: });
289:
290: describe('workspaces', () => {
291: let originalCwd: string;
292: const temporaryDirectories: string[] = [];
293:
294: beforeEach(() => {
295: originalCwd = process.cwd();
296: });
297:
298: afterEach(() => {
299: process.chdir(originalCwd);
300: for (const directory of temporaryDirectories.splice(0)) {
301: rmSync(directory, { recursive: true, force: true });
302: }
303: setActiveWorkspace(undefined);
304: vi.restoreAllMocks();
305: });
306:
307: const WS_CONFIG = [
308: ...
<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: ...
No files found
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/details-from-context.js</path>
<type>file</type>
<content>
1: import { test, expect } from "vitest";
2:
3: const { detailsFromContext } = require("../details-from-context.js");
4:
5: const inputsArgs = {
6: context: {
7: repo: { owner: "getsentry", repo: "publish" },
8: payload: {
9: issue: {
10: number: "223",
11: title: "publish: getsentry/sentry@21.3.1",
12: body: `
13: Requested by: @BYK
14:
15: Merge target: custom-branch
16:
17: Quick links:
18: - [View changes](https://github.com/getsentry/sentry/compare/21.3.0...refs/heads/releases/21.3.1)
19: - [View check runs](https://github.com/getsentry/sentry/commit/7e5ca7ed5581552de066e2a8bc295b8306be38ac/checks/)
20:
21: Assign the **accepted** label to this issue to approve the release. ...
/home/byk/Code/getsentry/publish-workspace-acceptance/src/publish/resolve-ci-poller-input.js /home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/ci-poller-input.js /home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/ci-poller-input.js
<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:
7: function getCiPollerInput({ title, issueBody, revision }) {
8: let parsedTitle;
9: try {
10: parsedTitle = parsePublishIssueTitle(title);
11: } catch {
12: throw new Error(`Invalid publish issue title: '${title}'`);
13: }
14: const { repo, version } = parsedTitle;
15: const currentRevision = getReleaseRevision({ issueBody, repo });
16:
17: return {
18: ...(revision
19: ? { issueBody: updateReleaseRevision({ issueBody, repo, revision }) }
20: : {}),
21: repo: `getsentry/${repo}`,
22: revision: currentRevision,
23: version,
24: };
25: }
26:
27: module.exports = { getCiPollerInput };
(End of file - total 27 lines)
</content>
<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>
670: if (exactMatches.length === 0 && fuzzyMatches.length === 0) {
671: message += `\n\nNo release branches found on the remote.`;
672: }
673:
674: message += `\n\nOriginal error: ${err instanceof Error ? err.message : String(err)}`;
675:
676: throw new ConfigurationError(message);
677: }
678: }
679:
680: const revision = await git.revparse('HEAD');
681: logger.debug('Revision to publish: ', revision);
682:
683: const statusProvider = await getStatusProviderFromConfig();
684: const artifactProvider = await getArtifactProviderFromConfig();
685:
686: // Check status of all CI builds linked to the revision
687: await checkRevisionStatus(statusProvider, revision, argv.noStatusCheck);
688:
689: await printRevisionSummary(artifactProvider, revision);
690:
691: await checkRequiredArtifacts(artifactProvider, ...
Found 100 matches (more matches available)
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/workspaces.ts:
Line 15: /** Information about a workspace package */
Line 25: /** Dependencies that are also workspace packages */
Line 26: workspaceDependencies: string[];
Line 29: /** Result of workspace discovery */
Line 31: /** The type of workspace manager detected */
Line 37: /** Structure of pnpm-workspace.yaml */
Line 45: workspaces?: string[] | { packages?: string[] };
Line 93: * Extract workspaces array from package.json workspaces field
Line 97: workspaces: string[] | { packages?: string[] } | undefined,
Line 99: if (!workspaces) {
Line 102: if (Array.isArray(workspaces)) {
Line 103: return workspaces;
Line 105: return workspaces.packages || [];
Line 115: // First: collect all workspace package names and locations
Line 116: const workspaceLocations: Array<{
Line 120: const workspaceNames = ...
<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. ...
(no output)
Found 1 matches /home/byk/Code/getsentry/craft-workspace-action-propagation/package.json: Line 49: "glob": "^11.0.0",
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/helpers.ts</path>
<type>file</type>
<content>
1: import { appendFileSync, mkdirSync, writeFileSync } from 'fs';
2: import path from 'path';
3: import { parseArgs } from 'node:util';
4:
5: import prompts from 'prompts';
6: import { logger, LogLevel, setLevel } from '../logger';
7:
8: /**
9: * Maximum size (in bytes) for step output values passed through GITHUB_OUTPUT.
10: * Values exceeding this are truncated to avoid E2BIG errors when GitHub Actions
11: * expands them into environment variables for subsequent steps.
12: *
13: * 64 KB is well under the ~2 MB ARG_MAX kernel limit and also under GitHub's
14: * ~65 536-character issue body limit, so truncated changelogs still render. ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-issue-title.js</path>
<type>file</type>
<content>
430: if (input.substr(peg$currPos, 9) === peg$c0) {
431: s1 = peg$c0;
432: peg$currPos += 9;
433: } else {
434: s1 = peg$FAILED;
435: if (peg$silentFails === 0) {
436: peg$fail(peg$e0);
437: }
438: }
439: if (s1 !== peg$FAILED) {
440: if (input.substr(peg$currPos, 10) === peg$c1) {
441: s2 = peg$c1;
442: peg$currPos += 10;
443: } else {
444: s2 = peg$FAILED;
445: if (peg$silentFails === 0) {
446: peg$fail(peg$e1);
447: }
448: }
449: if (s2 === peg$FAILED) {
450: s2 = null;
451: }
452: s3 = peg$parseRepository();
453: if (s3 !== peg$FAILED) {
454: s4 = peg$parsePath();
455: if (s4 === peg$FAILED) {
456: s4 = null;
457: }
458: if (input.charCodeAt(peg$currPos) === ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-issue-title.js</path>
<type>file</type>
<content>
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: };
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 = ...
<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/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 ...
isSafeWorkspacePath() validates the unexpanded pattern, then globSync() evaluates it. Brace alternatives can hide absolute or traversal segments, such as {../outside/*,packages/*} or {/etc/*,packages/*}.
src/config.ts:209-210 classifies brace patterns as globs.src/config.ts:235-250 calls globSync() before filtering matches.src/config.ts:257-270 only checks raw slash-separated text.This violates the required pre-filesystem containment check. Validate every expanded alternative or reject unsafe glob syntax before calling globSync().
Existing JSON-qualified workspace titles now fail parsing, leaving accepted/CI-ready requests unable to publish.
src/modules/publish-issue-title.peggy:10-16 removes LegacyWorkspace.src/modules/publish-issue-title.js:228-234,452-490.src/modules/details-from-context.js:26-33.docs/publish-issue-format.md:11-20.Keep parsing legacy titles while emitting only the new concrete-path form.
Craft permits unsafe literal workspace keys because schema validation only rejects whole-string special names; discovery preserves literal keys unchanged. Publish then accepts nested __proto__, . segments, and leading-dash segments that the action rejects.
src/schemas/project_config.ts:218-227.src/config.ts:208-215.packages/__proto__/cli, packages/./cli, and packages/-cli: src/modules/publish-location.js:33-41.action.yml:84-86. ...