fix(cli): scaffold installs clean — seed/preserve allowBuilds, heal pnpm placeholders - #230
Conversation
pnpm 10+ blocks install-time build scripts unless allowBuilds lists them; with no seed list, pnpm writes literal 'set this to true or false' placeholders into the workspace yaml during the post-init install, which then fail every subsequent install. Worse, the CLI round-tripped the yaml through a packages-only struct, so init service/router/worker and change application clobbered any hand-added fix (allowBuilds, blockExoticSubdeps, overrides, ...). - generate seeds allowBuilds: true for the scaffold's known build-script deps - the workspace struct now preserves unmodeled keys via flatten and heals placeholder values to true on every rewrite - bun installs during release create get a project-local TMPDIR so Bun's temp-dir sandbox cannot block them - fixture test scaffolds fresh, asserts yaml integrity across inits, and requires pnpm install + tsc --noEmit to pass with zero patches Co-Authored-By: Claude Fable 5 <[email protected]>
📝 WalkthroughWalkthroughThe CLI now preserves and sanitizes pnpm workspace settings, renders updated package entries through a dedicated function, uses a local temporary directory for Bun installs, and validates the flow with an end-to-end workspace test. ChangesWorkspace configuration and installation
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant CLI
participant WorkspaceRenderer
participant WorkspaceFile
participant PackageManager
participant Validation
CLI->>WorkspaceRenderer: render workspace with package entries
WorkspaceRenderer->>WorkspaceFile: read, preserve, sanitize, and serialize YAML
CLI->>PackageManager: install dependencies with runtime-specific settings
PackageManager-->>CLI: installation result
CLI->>Validation: run typechecks and workspace build
Validation-->>CLI: validation result
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
cli/tests/init_worker_workspace_and_typecheck.sh (1)
44-53: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy liftCover the runtime workspace renderer.
This fixture checks
init service, but it does not invokechange application, which callsrender_pnpm_workspace_with_packagesincli/src/change/application.rs. Add a runtime-change step after writing the poisoned YAML. Assert that it preservesblockExoticSubdepsand repairs the known placeholders.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@cli/tests/init_worker_workspace_and_typecheck.sh` around lines 44 - 53, Add a runtime change step in cli/tests/init_worker_workspace_and_typecheck.sh after writing the poisoned workspace YAML, invoking change application so render_pnpm_workspace_with_packages in cli/src/change/application.rs executes. Assert the resulting WORKSPACE_YAML still contains blockExoticSubdeps: false and no longer contains the known placeholder allowBuilds values.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@cli/src/core/pnpm_workspace.rs`:
- Around line 63-78: Update sanitize_allow_builds so non-boolean placeholders
are converted to true only for dependencies listed in
ALLOWED_BUILD_DEPENDENCIES; preserve unknown entries as false. Add a fixture
covering an unknown allowBuilds placeholder and verify it remains disallowed.
In `@cli/tests/init_worker_workspace_and_typecheck.sh`:
- Around line 1-8: Update the setup script around the workspace cleanup,
directory change, and cargo run command to fail immediately when any command
fails. Enable shell error propagation before these operations so an unsuccessful
mkdir, cd, or cargo run stops the script and prevents later checks from masking
the original failure.
---
Nitpick comments:
In `@cli/tests/init_worker_workspace_and_typecheck.sh`:
- Around line 44-53: Add a runtime change step in
cli/tests/init_worker_workspace_and_typecheck.sh after writing the poisoned
workspace YAML, invoking change application so
render_pnpm_workspace_with_packages in cli/src/change/application.rs executes.
Assert the resulting WORKSPACE_YAML still contains blockExoticSubdeps: false and
no longer contains the known placeholder allowBuilds values.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 466d9f0d-3b76-4eca-b08f-fcc19f720555
📒 Files selected for processing (4)
cli/src/change/application.rscli/src/core/pnpm_workspace.rscli/src/release/create.rscli/tests/init_worker_workspace_and_typecheck.sh
| fn sanitize_allow_builds(pnpm_workspace: &mut PnpmWorkspace) { | ||
| let mut allow_builds = pnpm_workspace | ||
| .allow_builds | ||
| .take() | ||
| .unwrap_or_else(default_allow_builds); | ||
| for value in allow_builds.values_mut() { | ||
| if !matches!(value, Value::Bool(_)) { | ||
| *value = Value::Bool(true); | ||
| } | ||
| } | ||
| for dep in ALLOWED_BUILD_DEPENDENCIES { | ||
| allow_builds | ||
| .entry((*dep).to_string()) | ||
| .or_insert(Value::Bool(true)); | ||
| } | ||
| pnpm_workspace.allow_builds = Some(allow_builds); |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/sh
set -eu
# Identify the pnpm version contract and all placeholder-healing coverage.
rg -n --glob 'package.json' '"packageManager"[[:space:]]*:[[:space:]]*"pnpm@' .
rg -n -C 3 'set this to true or false|sanitize_allow_builds|allowBuilds' \
cli/src/core/pnpm_workspace.rs cli/tests/init_worker_workspace_and_typecheck.shRepository: forklaunch/forklaunch
Length of output: 7381
🏁 Script executed:
#!/bin/sh
set -eu
printf '%s\n' '--- pnpm_workspace.rs ---'
cat -n cli/src/core/pnpm_workspace.rs | sed -n '1,190p'
printf '%s\n' '--- related fixtures and tests ---'
rg -n -C 8 'set this to true or false|allowBuilds|ALLOWED_BUILD_DEPENDENCIES|render_pnpm_workspace' \
cli tests .github 2>/dev/null || true
printf '%s\n' '--- repository history summary for the target file ---'
git log --oneline -8 -- cli/src/core/pnpm_workspace.rsRepository: forklaunch/forklaunch
Length of output: 20350
🌐 Web query:
pnpm 11 settings allowBuilds "set this to true or false" placeholder behavior
💡 Result:
In pnpm v11, the allowBuilds setting in pnpm-workspace.yaml serves as a unified mechanism to manage which dependencies are permitted to execute build scripts (such as postinstall) [1][2]. Behavior and Placeholder Logic: - Structure: It is a map where keys are package name patterns (or repository URLs for git dependencies) and values are booleans (true to allow, false to disallow) [1][2]. - Default Security: By default, packages not explicitly listed in allowBuilds are disallowed [1][2]. If a dependency attempts to run a build script without being explicitly permitted, pnpm will print an error (assuming the default strictDepBuilds: true) [1][3]. - Automatic Placeholder Behavior: When pnpm detects a dependency with a build script that is not yet listed in allowBuilds, it automatically updates your pnpm-workspace.yaml file by adding the package with a placeholder value [1][4]. This allows you to inspect the dependency and manually update the entry to true or false based on whether you trust the package to execute its scripts [1][3]. - CLI Interaction: You can manage these settings interactively using the pnpm approve-builds command, which also updates the allowBuilds map in your configuration [4]. Additionally, flags like --allow-build used during pnpm add will write the corresponding entries to this file [1][4]. Migration: The allowBuilds setting replaces several older, now-deprecated configurations, including onlyBuiltDependencies, onlyBuiltDependenciesFile, neverBuiltDependencies, ignoredBuiltDependencies, and ignoreDepScripts [1][3][2]. For further details, refer to the official pnpm settings documentation.
Citations:
- 1: https://pnpm.io/settings
- 2: pnpm/pnpm@cb367b9
- 3: https://github.com/pnpm/pnpm.io/blob/048c97bed74ea6696318086620ebd819df435a0b/docs/settings.md
- 4: https://pnpm.io/cli/approve-builds
Preserve unknown allowBuilds entries as false.
pnpm writes placeholders for unapproved build dependencies. This loop converts every placeholder to true, including entries outside ALLOWED_BUILD_DEPENDENCIES, and permits their build scripts. Add a fixture for an unknown placeholder.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@cli/src/core/pnpm_workspace.rs` around lines 63 - 78, Update
sanitize_allow_builds so non-boolean placeholders are converted to true only for
dependencies listed in ALLOWED_BUILD_DEPENDENCIES; preserve unknown entries as
false. Add a fixture covering an unknown allowBuilds placeholder and verify it
remains disallowed.
| if [ -d "output/init-worker-workspace" ]; then | ||
| rm -rf output/init-worker-workspace | ||
| fi | ||
|
|
||
| mkdir -p output/init-worker-workspace | ||
| cd output/init-worker-workspace | ||
|
|
||
| RUST_BACKTRACE=1 cargo run --release init application workspace-test-app -p workspace-test-app -o src/modules -d postgresql -f prettier -l eslint -v zod -F express -r node -t vitest -D "Workspace integrity test application" -A "Forklaunch Team" -L 'MIT' |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Stop on setup command failures.
The script continues after a failed cargo run. Later checks can inspect files from an earlier command and hide the failure. An unsuccessful cd can also run subsequent commands in the wrong directory.
Proposed fix
+set -e
+
if [ -d "output/init-worker-workspace" ]; then
rm -rf output/init-worker-workspace
fi
...
-cd output/init-worker-workspace
+cd output/init-worker-workspace || exit 1
...
-cd workspace-test-app/src/modules
+cd workspace-test-app/src/modules || exit 1Also applies to: 55-55
🧰 Tools
🪛 Shellcheck (0.11.0)
[warning] 6-6: Use 'cd ... || exit' or 'cd ... || return' in case cd fails.
(SC2164)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@cli/tests/init_worker_workspace_and_typecheck.sh` around lines 1 - 8, Update
the setup script around the workspace cleanup, directory change, and cargo run
command to fail immediately when any command fails. Enable shell error
propagation before these operations so an unsuccessful mkdir, cd, or cargo run
stops the script and prevents later checks from masking the original failure.
Source: Linters/SAST tools
Summary
Fixes the scaffold issues that force downstream consumers (ForkLaunch Studio) to patch generated files after
init. Audit of the reported five items against main:queueOptions, freshinit worker -t bullmq+tsc --noEmitpasses with 0 errors (was collateral of the #2-era duplicate-package skew)apps/*apps/*glob anywhereRoot cause for #1
The placeholder text is written by pnpm itself, not the CLI: the post-init
pnpm formattriggers pnpm 11's auto-install, which blocks unapproved build scripts and injectsallowBuilds: { esbuild: set this to true or false, ... }into the workspace yaml. The CLI then made it unfixable: everyinit service/router/workerandchange applicationround-tripped the yaml through apackages-only struct, silently droppingallowBuildsand any other consumer-added keys.Changes
generate_pnpm_workspaceseedsallowBuilds: truefor the scaffold's known build-script deps, so pnpm never injects placeholdersoverrides,blockExoticSubdeps, ...) via serde flatten, and every rewrite heals placeholder string values totruechange applicationrenders through the same preserving helper instead of a from-scratch structrelease createbun installs get a project-localTMPDIRso Bun's temporary-directory sandbox cannot block theminit_worker_workspace_and_typecheck.sh): scaffolds fresh, asserts no placeholders +allowBuildsseeded, asserts survival acrossinit worker/init service, poisons the yaml with placeholders + foreign keys and asserts healing + preservation, then requirespnpm installand per-projecttsc --noEmitto pass with zero patchesTest plan
cargo test: 327 passedtsgo --noEmitclean in core/worker/service, full build green)🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes