Dashboard › craft › Session 0DURWGP3WfIN
0DURWGP3WfINmnFMaStrict read-only adversarial review in /home/byk/Code/getsentry/craft-workspace-action-propagation of the current uncommitted fix for Craft PR #872 against current HEAD f174ceafa57355e71617f32ba1b0a030953be0a2. Do not edit or mutate files. Review src/config.ts and src/tests/config.test.ts plus relevant callers. Verify the realpath error handling skips only broken/racy glob candidates, cannot hide unsafe entries or bypass lexical/physical containment, maintains deterministic behavior, and regression genuinely fails before the fix. Check diff hygiene. Return severity-ranked file:line findings, explicit PASS/FAIL, test gaps, and end exactly MERGE or DO-NOT-MERGE.
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/AGENTS.md</path> <type>file</type> <content> 1: # AGENTS.md 2: 3: This file provides guidance for AI coding assistants working with the Craft codebase. 4: 5: ## Package Management 6: 7: - **Always use `pnpm`** for package management. Never use `npm` or `yarn`. 8: - Node.js version is managed by [Volta](https://volta.sh/) (currently v22.12.0). 9: - Install dependencies with `pnpm install --frozen-lockfile`. 10: 11: ## Development Commands 12: 13: | Command | Description | 14: | ------------ | ------------------------------------------- | 15: | `pnpm build` | Build the project (outputs to `dist/craft`) | 16: | `pnpm test` | Run tests | 17: | `pnpm lint` | Run ESLint | 18: | `pnpm fix` | Auto-fix lint issues | 19: 20: To manually test changes: 21: 22: ```bash 23: pnpm build && ...
M src/__tests__/config.test.ts
M src/config.ts
f174ceafa57355e71617f32ba1b0a030953be0a2
M src/__tests__/config.test.ts
M src/config.ts
diff --git a/src/__tests__/config.test.ts b/src/__tests__/config.test.ts
index 619c940..80968d1 100644
--- a/src/__tests__/config.test.ts
+++ b/src/__tests__/config.test.ts
@@ -501,6 +501,27 @@ describe('workspaces', () => {
expect(getWorkspaceNames()).toEqual(['packages/internal/release']);
});
+ test('ignores broken symlinks matched by workspace globs', () => {
+ const directory = mkdtempSync(join(tmpdir(), 'craft-workspaces-'));
+ temporaryDirectories.push(directory);
+ mkdirSync(join(directory, 'packages', 'cli'), { recursive: true });
+ symlinkSync(
+ join(directory, 'missing-workspace'),
+ join(directory, 'packages', 'broken'),
+ );
+ writeFileSync(
+ join(directory, '.craft.yml'),
+ [
+ `minVersion: ${WORKSPACES_MIN_VERSION}`,
+ 'workspaces:',
+ ' packages/*: {}',
+ ...
<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/__tests__/config.test.ts</path>
<type>file</type>
<content>
380: expect(() => getWorkspaceNames()).toThrow(
381: `requires minVersion >= ${WORKSPACES_MIN_VERSION}`,
382: );
383: });
384:
385: test('expands a workspace glob into concrete directory paths', () => {
386: const directory = mkdtempSync(join(tmpdir(), 'craft-workspaces-'));
387: temporaryDirectories.push(directory);
388: mkdirSync(join(directory, 'packages', 'cli'), { recursive: true });
389: mkdirSync(join(directory, 'packages', 'mcp'), { recursive: true });
390: writeFileSync(join(directory, 'packages', 'README.md'), 'not a workspace');
391: writeFileSync(
392: join(directory, '.craft.yml'),
393: [
394: `minVersion: ${WORKSPACES_MIN_VERSION}`,
395: 'workspaces:',
396: ' packages/*:',
397: ' releaseBranchPrefix: release/package',
398: ].join('\n'),
399: ...
<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 = { ...
Found 68 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/index.ts:
Line 20: import { setActiveWorkspace } from './config';
Line 99: setActiveWorkspace(extractWorkspaceSelection(argv));
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/config.ts:
Line 72: * (single implicit release unit). Set once via `setActiveWorkspace` from the
Line 84: export function setActiveWorkspace(name: string | undefined): void {
Line 124: const available = getWorkspaceNamesFromConfig(base, workspaceDirectory);
Line 197: ? getWorkspaceGlobMatches(key, workspaceDirectory).includes(workspaceName)
Line 201: function getWorkspaceNamesFromConfig(
Line 210: ? ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/config.ts</path>
<type>file</type>
<content>
420: }
421:
422: function checkWorkspacesMinVersion(config: CraftProjectConfig): void {
423: if (!isVersionGteMinVersion(config.minVersion, WORKSPACES_MIN_VERSION)) {
424: throw new ConfigurationError(
425: `Using "workspaces" requires minVersion >= ${WORKSPACES_MIN_VERSION} ` +
426: 'in the configuration file.',
427: );
428: }
429: }
430:
431: /**
432: * SemVer build metadata does not affect precedence, but the comparison helper
433: * intentionally rejects versions carrying it. Strip it before compatibility
434: * checks so config values such as `2.29.0+linux` remain valid.
435: */
436: function withoutBuildMetadata(version: SemVer): SemVer {
437: return version.build ? { ...version, build: undefined } : version;
438: }
439:
440: /**
441: * Applies workspace selection + validation to a freshly-parsed config. ...
246 dot: true,
247 ignore: ['**/node_modules/**'],
248 posix: true,
249 })
250 .filter(match => {
251 const resolvedMatch = path.resolve(root, match);
252 const realMatch = realpathSync(resolvedMatch);
253 return (
254 isSafeWorkspacePath(match) &&
255 (resolvedMatch === root ||
256 resolvedMatch.startsWith(`${root}${path.sep}`)) &&
257 (realMatch === realRoot ||
258 realMatch.startsWith(`${realRoot}${path.sep}`)) &&
RUN v4.1.8 /home/byk/Code/getsentry/craft-workspace-action-propagation
Test Files 1 passed (1)
Tests 68 passed (68)
Start at 17:16:07
Duration 1.48s (transform 383ms, setup 0ms, import 1.16s, tests 121ms, environment 0ms)
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/index.ts</path>
<type>file</type>
<content>
70: result.push(arg);
71: if (arg.slice(2) in GLOBAL_BOOLEAN_FLAGS) {
72: result.push('1');
73: }
74: }
75: return result;
76: }
77:
78: /**
79: * Main entrypoint
80: */
81: async function main(): Promise<void> {
82: // Strip dynamic-linker env vars (LD_PRELOAD, DYLD_*, ...) before anything
83: // else so they cannot leak into subprocesses Craft spawns.
84: sanitizeDynamicLinkerEnv();
85:
86: printVersion();
87:
88: warnIfCraftEnvFileExists();
89:
90: const argv = fixGlobalBooleanFlags(process.argv.slice(2));
91:
92: // Resolve the active workspace BEFORE parsing. yargs runs command `builder`s
93: // (which may read the configuration, e.g. `publish` derives its --target
94: // choices from config.targets) *before* middleware, so setting the workspace
95: // via middleware would be too late — the builder would ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/prepare.ts</path>
<type>file</type>
<content>
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. The caller
797: // is responsible for ensuring the branch is trusted — the remote
798: // config's preReleaseCommand will be executed by Craft.
799: if (argv.configFrom) ...
No files found
No files found
src/config.ts:253-258: The unconditional catch suppresses every realpathSync failure, not only broken or raced-away candidates. It silently drops permission failures (EACCES/EPERM), symlink loops (ELOOP), and other unexpected I/O errors. Because lexical and physical containment checks occur only at :260-265, these entries never receive validation or diagnostics. This can hide unsafe or ambiguous filesystem state, though it cannot admit a candidate past containment checks. Rethrow non-disappearance errors.src/__tests__/config.test.ts:504-523: The regression does not prove the broken symlink reaches globSync. It passes both when globSync emits the link and the new filter skips it, and when globSync omits it before the filter. Therefore it does not demonstrate that the base implementation at f174ce…:src/config.ts:252 fails. Assert the fixture is returned by globSync, or add a deterministic mock/spying seam that makes realpathSync throw ENOENT for a glob candidate.isSafeWorkspacePath() (src/config.ts:235-239, :260).src/config.ts:263-265)..sort() (src/config.ts:268).:197), listing (:210, :628-635), and remote config loading (src/commands/prepare.ts:805-815).git diff --check passed.src/__tests__/config.test.ts.realpathSync errors must fail rather than disappear.realpathSync should still be skipped.DO-NOT-MERGE
Date: Sep 8, 2026 * 🔴 (17:14) User requested a strict read-only adversarial review in `/home/byk/Code/getsentry/craft-workspace-action-propagation` of the current uncommitted fix for Craft PR #872 against current HEAD `f174ceafa57355e71617f32ba1b0a030953be0a2`; instructed not to edit or mutate files. Requested review of `src/config.ts`, `src/__tests__/config.test.ts`, and relevant callers; verif…