Dashboard › craft › Session 0zt9C8xAGoVL
0zt9C8xAGoVLEEt9tConduct the final strict READ-ONLY, evidence-based adversarial audit. Do not edit files, format, generate, run mutating commands, or alter git state. You MUST return a substantive cited report or start BLOCKED with a specific tool/error; never return empty.
Current worktrees:
First inspect CURRENT git status --short, git diff --name-only, and git diff --check in both. Only report files actually changed. In particular, do not repeat the stale CocoaPods workflow claim unless it appears in current git diff.
Contract: getsentry/craft:latest is intentional and REQUIRED; never treat it as a finding.
Review every changed file plus relevant source/test/workflow code. Required checks:
packages/{cli, packages/{cli}, packages/{cli,{mcp}}, packages/{cli,{{},mcp}}) without dropping bad branches; valid nested packages/{cli,{mcp,api}} resolves. Verify safe brace/class/negated-class/globstar behavior, literal safety, POSIX identities, overlap errors, remote config directory handling, and physical symlink containment.No files found
No files found
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 docs/src/content/docs/targets/github.md src/__tests__/action.test.ts src/__tests__/config.test.ts src/commands/prepare.ts src/config.ts src/schemas/project_config.ts src/utils/__tests__/publishState.test.ts 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 .github/workflows/publish.yml docs/publish-issue-format.md src/modules/__tests__/ci-poller-input.js src/modules/__tests__/details-from-context.js src/modules/__tests__/publish-location.js src/modules/__tests__/publish-workflow.js src/modules/ci-poller-input.js src/modules/details-from-context.js src/modules/publish-issue-title.js src/modules/publish-issue-title.peggy src/modules/publish-location.js src/publish/resolve-location.js
diff --git a/action.yml b/action.yml
index 94f3158..d160509 100644
--- a/action.yml
+++ b/action.yml
@@ -74,6 +74,7 @@ runs:
- name: Validate workspace
shell: bash
env:
+ LC_ALL: C
PATH_INPUT: ${{ inputs.path }}
WORKSPACE: ${{ inputs.workspace }}
run: |
@@ -81,8 +82,12 @@ runs:
echo "::error::The path and workspace inputs cannot be used together."
exit 1
fi
- if [[ -n "$WORKSPACE" ]] && ! node -e 'process.exit(/^(?!\.{1,2}$)(?!__proto__$)(?!-)[A-Za-z0-9_.-]+$/.test(process.env.WORKSPACE) ? 0 : 1)'; then
- echo "::error::Workspace names must use only ASCII letters, digits, periods, underscores, and hyphens."
+ if ! node -e 'const path = process.env.PATH_INPUT; const segment = /^[A-Za-z0-9_.-]+$/; process.exit(path === "." || (typeof path === "string" && path.split("/").every(name => segment.test(name) && ![".", "..", "__proto__"].includes(name) && !name.startsWith("-"))) ? ...
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index f6dc96a..23c546e 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -172,12 +172,7 @@ jobs:
PUBLISH_ARGS: ${{ steps.inputs.outputs.result }}
run: |
workspace_names='[]'
- requires_workspace_discovery="$(node -e '
- const { needsWorkspaceDiscovery } = require("./.__publish__/src/modules/publish-location");
- const input = JSON.parse(process.env.PUBLISH_ARGS || "");
- process.stdout.write(String(needsWorkspaceDiscovery(input)));
- ')"
- if [[ "$requires_workspace_discovery" == "true" && -f __repo__/.craft.yml ]]; then
+ if [[ -f __repo__/.craft.yml ]]; then
workspace_names="$(docker run --rm \
--volume "$GITHUB_WORKSPACE/__repo__:/github/workspace/__repo__" \
--workdir /github/workspace/__repo__ \
@@ -224,13 +219,18 @@ jobs:
...
<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>
275: title="publish: ${GITHUB_REPOSITORY}${SUBDIRECTORY}${WORKSPACE:+/$WORKSPACE}@${RESOLVED_VERSION}"
276:
277: # Check if issue already exists by listing all open issues and filtering by exact title match.
278: # We avoid GitHub search API to bypass indexing delays and query syntax edge cases.
279: # gh issue list returns issues sorted by creation date (most recent first), so we take
280: # the first match to handle the theoretical case of duplicate titles.
281: existing_issue=$(gh -R "$PUBLISH_REPO" issue list --json title,url,number,body | jq -r --arg t "$title" '[.[] | select(.title == $t)] | first // empty')
282: existing_issue_url=""
283: existing_issue_number=""
284: existing_body=""
285: if [[ -n "$existing_issue" ]]; then
286: existing_issue_url=$(echo "$existing_issue" | jq ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/config.ts</path>
<type>file</type>
<content>
1: import { existsSync, lstatSync, readFileSync, realpathSync } 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 ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/config.ts</path>
<type>file</type>
<content>
165: return resolved;
166: }
167:
168: function getWorkspaceConfig(
169: workspaces: NonNullable<CraftProjectConfig['workspaces']>,
170: workspaceName: string,
171: workspaceDirectory: string,
172: ): Workspace | undefined {
173: if (isWorkspacePattern(workspaceName)) {
174: return undefined;
175: }
176: const matchingKeys = Object.keys(workspaces).filter(key =>
177: workspaceKeyMatches(key, workspaceName, workspaceDirectory),
178: );
179: if (matchingKeys.length === 1) {
180: return workspaces[matchingKeys[0]];
181: }
182: if (matchingKeys.length > 1) {
183: throw new ConfigurationError(
184: `Workspace "${workspaceName}" matches multiple workspace patterns: ` +
185: `${matchingKeys.join(', ')}.`,
186: );
187: }
188: return undefined;
189: }
190:
191: function workspaceKeyMatches(
192: key: string,
193: ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/schemas/project_config.ts</path> <type>file</type> <content> 190: statusProvider: BaseStatusProviderSchema.optional(), 191: artifactProvider: BaseArtifactProviderSchema.optional(), 192: versioning: VersioningConfigSchema.optional(), 193: /** 194: * Do not merge the release branch after publishing. 195: * Defaults to true for compiled GitHub Actions (Node.js actions with dist/ folder). 196: */ 197: noMerge: z.boolean().optional(), 198: } as const; 199: 200: /** 201: * Configuration for a single named workspace (release unit). 202: * 203: * A workspace mirrors the release-relevant subset of the top-level config; 204: * every field is optional and inherits the top-level value when omitted. The 205: * `github` block is *partial* (all fields optional) so a workspace can override 206: * `owner` and/or `repo` while inheriting the rest from the top-level `github`. ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/prepare.ts</path>
<type>file</type>
<content>
770: } else {
771: bumpType = version as BumpType;
772: }
773:
774: // Calculate new version from latest tag
775: const currentVersion = (latestTag && getVersion(latestTag)) || '0.0.0';
776:
777: const newVersion = calculateNextVersion(currentVersion, bumpType);
778: logger.info(
779: `Version bump: ${currentVersion} -> ${newVersion} (${bumpType} bump)`,
780: );
781: return newVersion;
782: }
783:
784: // Explicit semver version - return as-is
785: return version;
786: }
787:
788: /**
789: * Body of 'prepare' command
790: *
791: * @param argv Command-line arguments
792: */
793: export async function prepareMain(argv: PrepareOptions): Promise<any> {
794: let git = await getGitClient();
795:
796: // Handle --config-from: load config from remote branch. ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/config.ts</path>
<type>file</type>
<content>
515:
516: /**
517: * Returns project configuration (.craft.yml) file path
518: *
519: * Throws an error if the file cannot be found.
520: */
521: export function getConfigFilePath(): string {
522: const configFilePath = findConfigFile();
523: if (!configFilePath) {
524: throw new ConfigurationError(
525: `Cannot find Craft configuration file. Have you added "${CONFIG_FILE_NAME}" to your project?`,
526: );
527: }
528: return configFilePath;
529: }
530:
531: /**
532: * Returns the path to the directory that contains the configuration file
533: *
534: * Returns "undefined" if no configuration file can be found.
535: */
536: export function getConfigFileDir(): string | undefined {
537: const configFilePath = findConfigFile();
538: if (!configFilePath) {
539: return undefined;
540: }
541: return path.dirname(configFilePath);
542: ...
<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>
1: import { describe, test, expect, vi, afterEach, beforeEach } from 'vitest';
2: import { mkdtempSync, mkdirSync, rmSync, symlinkSync, writeFileSync } from 'fs';
3: import { tmpdir } from 'os';
4: import { join } from 'path';
5: /**
6: * Tests of our ability to read craft config files. (This is NOT general test
7: * configuration).
8: */
9:
10: import {
11: getGitTagPrefix,
12: loadConfigurationFromString,
13: validateConfiguration,
14: setActiveWorkspace,
15: getActiveWorkspace,
16: getConfiguration,
17: getVersioningPolicy,
18: getWorkspaceNames,
19: WORKSPACES_MIN_VERSION,
20: } from '../config';
21: import { CraftProjectConfigSchema } from '../schemas/project_config';
22: import { logger } from '../logger';
23:
24: describe('validateConfiguration', () => {
25: test('parses minimal configuration', () => {
26: const data = { ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/__tests__/config.test.ts</path>
<type>file</type>
<content>
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: `minVersion: ${WORKSPACES_MIN_VERSION}`,
309: 'github:',
310: ' owner: getsentry',
311: ' repo: toolkit',
312: 'changelog: CHANGELOG.md',
313: 'workspaces:',
314: ' cli:',
315: ' releaseBranchPrefix: release/cli',
316: ' targets:',
317: ' - name: github',
318: ' tagPrefix: "cli@"',
319: ' mcp:',
320: ' releaseBranchPrefix: release/mcp',
321: ' versioning:',
322: ' policy: calver',
323: ' targets:',
324: ' - name: github',
325: ' tagPrefix: "mcp@"',
326: ].join('\n');
327:
328: ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/__tests__/config.test.ts</path>
<type>file</type>
<content>
260: { name: 'github', tagPrefix: 'cli@' },
261: { name: 'github', tagPrefix: 'cli@', id: 'second' },
262: ]);
263: expect(getGitTagPrefix()).toBe('cli@');
264: expect(warnSpy).not.toHaveBeenCalled();
265: });
266:
267: test('warns and returns the first prefix when github targets disagree', () => {
268: const warnSpy = vi.spyOn(logger, 'warn').mockImplementation(() => {});
269: loadWithTargets([
270: { name: 'github', tagPrefix: 'cli@' },
271: { name: 'github', tagPrefix: 'mcp@', id: 'second' },
272: ]);
273: expect(getGitTagPrefix()).toBe('cli@');
274: expect(warnSpy).toHaveBeenCalledTimes(1);
275: expect(warnSpy.mock.calls[0][0]).toMatch(/different "tagPrefix"/);
276: });
277:
278: test('warns when one github target has a prefix and another omits it', () => {
279: const warnSpy = ...
Date: Sep 5, 2026 * 🟡 (01:10) `prepareMain(argv: PrepareOptions)` in `src/commands/prepare.ts` supports `argv.configFrom`: warns that `.craft.yml` is being loaded from remote branch `"${argv.configFrom}"` and that its `preReleaseCommand` will execute, so caller must trust the branch; fetches `[argv.remote, argv.configFrom]`, reads `${argv.remote}/${argv.configFrom}:${CONFIG_FILE_NAME}` via `git.…
Date: Sep 5, 2026 * 🟡 (01:09) `action.yml` lines 277-367 implement publish-request issue reuse in `/home/byk/Code/getsentry/craft-workspace-action-propagation/action.yml`: construct title `publish: ${GITHUB_REPOSITORY}${SUBDIRECTORY}${WORKSPACE:+/$WORKSPACE}@${RESOLVED_VERSION}`; use `gh -R "$PUBLISH_REPO" issue list --json title,url,number,body` and `jq` exact-title filtering rather than GitHub…
Date: Sep 5, 2026 * 🔴 (01:09) User requires `pnpm` for all package management and prohibits use of `npm` and `yarn`. * 🔴 (01:09) User stated Node.js is managed through Volta, currently version `v22.12.0`. * 🔴 (01:09) User stated dependencies are installed with `pnpm install --frozen-lockfile`. * 🔴 (01:09) User stated project configuration is stored in root `.craft.yml`; its configuration sche…
Date: Sep 5, 2026 * 🔴 (01:09) User stated new Craft requests always include the checkout repository identity. * 🔴 (01:09) User stated the controller never normalizes names. * 🔴 (01:09) User stated a missing root `.craft.yml` always means checkout-path behavior. * 🟡 (01:09) Publish workflow and documentation change workspace-release title suffixes from one trailing workspace-name segment to th…
Date: Sep 5, 2026 * 🟢 (01:08) Two tool invocations returned `No files found`. * 🟡 (01:08) Craft worktree inspection reported 9 modified tracked files: `action.yml`, `docs/src/content/docs/targets/github.md`, `src/__tests__/action.test.ts`, `src/__tests__/config.test.ts`, `src/commands/prepare.ts`, `src/config.ts`, `src/schemas/project_config.ts`, `src/utils/__tests__/publishState.test.ts`, and …
Date: Sep 5, 2026 * 🔴 (01:07) User requested a final strict READ-ONLY, evidence-based adversarial audit of two worktrees: Craft at `/home/byk/Code/getsentry/craft-workspace-action-propagation` and Publish at `/home/byk/Code/getsentry/publish-workspace-acceptance`. User directed no file edits, formatting, generation, mutating commands, or git-state changes. * 🔴 (01:07) User stated the audit resp…