Dashboard › craft › Distillation
21bf93d4-6a34-4c16-8115-d7dba8293779["lore_tm_v1_R72QeHWbohYQ4AmcXFXsAGDrYXkwxBpXQ5chaiiKNNE","lore_tm_v1_K6ndjzUSzsQY2z3Yb6vQjqzYAau094Ly0UUuJDK7TPQ","lore_tm_v1_Irk1eXgfQpHp0-Mh-UvZvpCJxkvmluWoi8BwG8k6xyc"]
Date: Aug 25, 2026
--workspace --dry-run is never mistaken for a workspace name./home/byk/Code/getsentry/craft-workspace-action-propagation identified workspace-related implementation in src/utils/workspaces.ts, publish-resume state isolation in src/utils/publishState.ts, raw CLI workspace extraction in src/utils/helpers.ts, and workspace tests in src/utils/__tests__/workspaces.test.ts.src/utils/workspaces.ts supports npm/yarn workspaces from package.jsonβs workspaces field and pnpm workspaces from pnpm-workspace.yaml; it discovers workspace packages, resolves workspace dependencies by filtering dependency names against known workspace package names, supports include/exclude filtering, and topologically sorts packages while detecting circular dependencies.src/utils/__tests__/workspaces.test.ts tests npm workspace discovery, pnpm workspace discovery, no-workspace behavior, workspace dependency resolution (including @test/pkg-b depending on @test/pkg-a), dependency ordering, and circular dependency cases.src/utils/publishState.ts exists to prevent a repository being published from pre-populating Craftβs completed-target publish state via a committed .craft-publish-<version>.json or an earlier CI step, which could otherwise cause targets to be silently skipped.src/utils/publishState.ts stores publish state outside the target checkout under $XDG_STATE_HOME/craft/, falling back to $HOME/.local/state/craft/; getsentry/publish runs in Docker with HOME=/root, providing a clean workflow-writable location inaccessible to committed repository content.getCraftStateDir() in src/utils/publishState.ts returns join(process.env.XDG_STATE_HOME, 'craft') when XDG_STATE_HOME is nonempty, otherwise join(homedir(), '.local', 'state', 'craft'); it is exported for tests and scripts/print-publish-state-path.ts if present.sanitiseForFilename() lowercases strings, replaces runs of characters outside [a-z0-9._-] with _, and trims leading/trailing underscores. shortCwdHash() uses sha1(cwd) and the first 12 hex characters to distinguish monorepo subpaths.getPublishStateFilename(version, githubConfig, cwd = process.cwd(), workspace?) creates publish-state-<owner>-<repo>-<sha1(cwd)[:12]>-<workspace-prefix><sanitised-version>.json with GitHub config, or publish-state-<sha256(cwd)[:16]>-<workspace-prefix><sanitised-version>.json without it. The optional workspace prefix is workspace-${Buffer.from(workspace).toString('base64url')}-, isolating same-repository, same-version releases by workspace.getPublishStatePath(version, githubConfig, cwd = process.cwd(), workspace?) returns join(getCraftStateDir(), getPublishStateFilename(version, githubConfig, cwd, workspace)).pnpm; never use npm or yarn.pnpm install --frozen-lockfile.pnpm build (builds dist/craft), pnpm test (runs tests), pnpm lint (runs ESLint), pnpm fix (auto-fixes lint), and manual testing via pnpm build && ./dist/craft..prettierrc.yml; ESLint 9.x flat configuration in eslint.config.mjs using typescript-eslint; unused variables prefixed with _ are permitted.src/__tests__/ with *.test.ts names and use vi.fn(), vi.mock(), and vi.spyOn() for mocks.master; Craft releases itself using its own tooling..craft.yml at repository root, with schemas in src/schemas/.getGitClient() from src/utils/git.ts or createGitClient(directory); GitHub API uses getGitHubClient() from src/utils/githubApi.ts; file writes use safeFs from src/utils/dryRun.ts; other actions use safeExec() or safeExecSync() from src/utils/dryRun.ts.simple-git imports via no-restricted-imports and direct new Octokit() via no-restricted-syntax; wrapper modules needing raw access use // eslint-disable-next-line no-restricted-imports -- This is the wrapper module before importing simple-git.isDryRun() checks include commands with their own dry-run flag such as dart pub publish --dry-run, operations needing mock data during dry runs, and UX optimizations such as skipping sleep timers.extractWorkspaceSelection(argv, env = process.env) in src/utils/helpers.ts resolves workspace selection before yargs builders because builders may read configuration before middleware; this is required for publish, whose --target choices derive from config.targets.extractWorkspaceSelection() uses Node parseArgs() with options: { workspace: { type: 'string' } }, allowPositionals: true, strict: false, and tokens: true; it supports --workspace foo and --workspace=foo.extractWorkspaceSelection() reads CRAFT_WORKSPACE, scans parseArgs() tokens for workspace options, and applies last-occurrence semantics: each workspace token first clears a previously selected value; a nonempty string is accepted only when token.inlineValue is true or the value does not begin with -; otherwise it falls back to CRAFT_WORKSPACE.src/utils/helpers.ts defines MAX_STEP_OUTPUT_BYTES = 64 * 1024, chosen below the approximately 2 MB ARG_MAX limit and GitHubβs approximately 65,536-character issue-body limit to avoid E2BIG errors when GITHUB_OUTPUT values become subsequent-step environment variables.envToBool(envVar) lowercases String(envVar) and returns false for '', 'undefined', 'null', '0', 'false', or 'no'; all other values return true.GLOBAL_FLAGS defaults are 'dry-run': false, 'no-input': false, and 'log-level': 'Info'; setGlobals() updates supplied values, logs flags and argv, and calls setLevel(LogLevel[GLOBAL_FLAGS['log-level']]); isDryRun() returns GLOBAL_FLAGS['dry-run'].