Skip to content

fix: stop forwarding unvalidated ni output to the shell in the CLI - #3422

Open
vlaux wants to merge 4 commits into
devfrom
fix/FAS-1199-package-manager-resolution
Open

fix: stop forwarding unvalidated ni output to the shell in the CLI#3422
vlaux wants to merge 4 commits into
devfrom
fix/FAS-1199-package-manager-resolution

Conversation

@vlaux

@vlaux vlaux commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

What's the purpose of this pull request?

A store build failed with /bin/sh: syntax error: unexpected "(" and exit code 2, with nothing in the log tying the error to its cause.

getPreferredPackageManager() returns the raw stdout of na ? with no validation, and every caller interpolates that value into a shell command. ni runs in non-programmatic mode, where an agent that is not on PATH does not make it fail: it renders a confirm prompt to stdout. The terminalLink non-TTY fallback and the (y/N) option both contain (, so the command becomes unparseable:

sh -c "Would you like to globally install pnpm (https://…)? › (y/N) run build"
  → /bin/sh: syntax error: unexpected "(" → exit 2

The diagnostic that explains it, [ni] Detected pnpm but it doesn't seem to be installed., goes to stderr, which spawnSync discards.

The store had both pnpm-lock.yaml and yarn.lock committed. The build image installs from yarn.lock first, so pnpm was never present, while ni prefers pnpm-lock.yaml. Two opposite precedence rules over the same repo.

How it works?

Resolution moves to the ni library API with programmatic: true, which is the only way to disable the interactive fallback (the na binary does not expose it). The agent is then validated against ni's known agents before it can reach a shell.

When the resolved agent is not on PATH, the CLI reports it and substitutes an available one instead of failing:

warning - Detected "pnpm" but it is not installed in this environment. Using "yarn" instead.
More than one lockfile is committed (pnpm-lock.yaml, yarn.lock). Keep a single one so
the package manager is unambiguous.

Substituting rather than failing keeps regression risk at zero: cmdExists is which.sync, which does not always agree with what a shell would resolve. In the incident, yarn is also the correct choice, since yarn is what installed node_modules.

The return value is split into { agent, command, argv }, so callers stop pattern-matching a command string:

value with Volta present
agent 'yarn' — for comparisons
command 'volta run yarn' — for shell: true
argv ['volta', 'run', 'yarn'] — for spawn without a shell

That fixes two latent bugs a Volta prefix would trigger today: start.ts passed the whole string as argv[0] of a spawn without a shell, and dependencies.ts compared it with === 'npm'.

getPackageRootDir and getDepPackageJSON existed only to locate the na binary and nothing else in packages/ imports them, so they are removed. That leaves resolve-pkg unused here (@faststore/diagnostics declares its own), so it is dropped from the manifest.

How to test it?

  • cd packages/cli && pnpm test — 12 new cases in src/utils/commands.test.ts cover detection, the Volta prefix, substitution with and without multiple lockfiles, the null detection default, and both error paths.
  • To reproduce the original failure: in a store with both pnpm-lock.yaml and yarn.lock, with pnpm not on PATH, run faststore build. Before this change it exits 2 with a shell syntax error; now it warns and builds with yarn.

References

Checklist

PR Title and Commit Messages

Dependencies

  • Committed the pnpm-lock.yaml file when there were changes to the packages

Documentation

  • PR description

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved package manager detection for CLI commands, including Volta-managed environments, with better argument handling when starting.
    • Added automatic fallback to an available package manager when the detected one is missing, with clearer errors when none are usable.
    • Fixed dependency installation to prevent unhandled errors and ensure the progress indicator stops on failures.
  • Tests
    • Added coverage for package manager resolution, fallbacks, lockfile scenarios, dependency installation, and start/build failure behavior.

@vlaux
vlaux requested a review from a team as a code owner July 29, 2026 18:35
@vlaux
vlaux requested review from lariciamota and lemagnetic and removed request for a team July 29, 2026 18:35
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: abc0e33a-2e7c-49d7-b3f9-77fd6fb88cad

📥 Commits

Reviewing files that changed from the base of the PR and between 1a06a13 and aedcdcf.

📒 Files selected for processing (2)
  • packages/cli/src/commands/start.test.ts
  • packages/cli/src/utils/commands.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/cli/src/commands/start.test.ts
  • packages/cli/src/utils/commands.ts

Walkthrough

The CLI replaces manual package-manager probing with @antfu/ni resolution, including fallback and error handling. Startup and dependency installation consume structured runner information. Dependency installation now awaits work and always stops its spinner.

Changes

Package Manager Resolution

Layer / File(s) Summary
Resolution contract and fallback behavior
packages/cli/src/utils/commands.ts, packages/cli/package.json
Adds structured package-manager resolution with agent validation, Volta-aware commands, fallback selection, and explicit errors. Removes resolve-pkg.
CLI command integration
packages/cli/src/commands/start.ts, packages/cli/src/utils/dependencies.ts, packages/cli/src/utils/generate.ts
Startup and dependency installation use resolved commands and agent-specific arguments. Dependency installation is awaited, and spinner cleanup is exception-safe.
Resolution behavior validation
packages/cli/src/utils/commands.test.ts, packages/cli/src/commands/start.test.ts, packages/cli/src/utils/dependencies.test.ts
Tests cover detection, normalization, Volta prefixes, fallbacks, lockfile warnings, startup and build behavior, installation commands, and errors.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Start.run
  participant resolvePackageManager
  participant antfu_ni as "`@antfu/ni`"
  participant FastStore
  participant Next.js
  Start.run->>resolvePackageManager: resolve command and argv
  resolvePackageManager->>antfu_ni: detect agent and command availability
  antfu_ni-->>resolvePackageManager: agent and Volta-prefixed command
  resolvePackageManager-->>Start.run: command and argv
  Start.run->>FastStore: run faststore build with command
  Start.run->>Next.js: spawn runner args plus next start arguments
Loading

Possibly related PRs

  • vtex/faststore#3423: Updates package-manager detection, validation, fallback behavior, and Volta handling in commands.ts.
  • vtex/faststore#3337: Modifies the same commands.ts module for Windows-safe dynamic imports.

Suggested reviewers: renatomaurovtex, lariciamota, lemagnetic

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the primary security change: validating ni output before using it in CLI shell commands.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/FAS-1199-package-manager-resolution

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codesandbox-ci

codesandbox-ci Bot commented Jul 29, 2026

Copy link
Copy Markdown

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

@vlaux
vlaux force-pushed the fix/FAS-1199-package-manager-resolution branch from 31a73e2 to 4f7722c Compare July 29, 2026 18:42
@vlaux vlaux changed the title fix(cli): stop forwarding unvalidated ni output to the shell fix: stop forwarding unvalidated ni output to the shell in the CLI Jul 29, 2026
@pkg-pr-new

pkg-pr-new Bot commented Jul 29, 2026

Copy link
Copy Markdown

Open in StackBlitz

@faststore/api

npm i https://pkg.pr.new/vtex/faststore/@faststore/api@aedcdcf

@faststore/cli

npm i https://pkg.pr.new/vtex/faststore/@faststore/cli@aedcdcf

@faststore/components

npm i https://pkg.pr.new/vtex/faststore/@faststore/components@aedcdcf

@faststore/core

npm i https://pkg.pr.new/vtex/faststore/@faststore/core@aedcdcf

@faststore/diagnostics

npm i https://pkg.pr.new/vtex/faststore/@faststore/diagnostics@aedcdcf

@faststore/lighthouse

npm i https://pkg.pr.new/vtex/faststore/@faststore/lighthouse@aedcdcf

@faststore/sdk

npm i https://pkg.pr.new/vtex/faststore/@faststore/sdk@aedcdcf

@faststore/ui

npm i https://pkg.pr.new/vtex/faststore/@faststore/ui@aedcdcf

commit: aedcdcf

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
packages/cli/src/utils/commands.test.ts (1)

32-33: 📐 Maintainability & Code Quality | 🔵 Trivial

Tests hit the real filesystem instead of mocking it.

fs.mkdtempSync/fs.writeFileSync/fs.rmSync perform real disk I/O for every test case. As per path instructions, packages/cli/**/*.test.ts: "Command tests must mock filesystem access and external processes." Consider mocking node:fs's existsSync (already imported directly in commands.ts) instead of creating/removing real temp directories.

Also applies to: 43-43, 121-123, 135-136

🤖 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 `@packages/cli/src/utils/commands.test.ts` around lines 32 - 33, Update the
command tests around the shared cwd setup and file operations to mock node:fs
rather than calling fs.mkdtempSync, fs.writeFileSync, or fs.rmSync against the
real filesystem. Mock the existsSync dependency used by commands.ts and
configure its return value per test, preserving the existing command assertions
and cleanup behavior without creating temporary directories or files.

Source: Path instructions

🤖 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 `@packages/cli/src/commands/start.ts`:
- Around line 30-37: Check the result returned by spawnSync in the build branch
of the start command and stop or propagate the failure when faststore build does
not succeed. Ensure next start is only spawned after a successful build, while
preserving the existing behavior when .next already exists.

In `@packages/cli/src/utils/commands.test.ts`:
- Around line 155-159: Update the mocked detect result in the test “throws
instead of forwarding an unknown agent to a shell” to use a package-manager
string not recognized by `@antfu/ni`, ensuring resolvePackageManager(cwd) reaches
the UnknownAgentError path rather than substituteAgent.

In `@packages/cli/src/utils/dependencies.ts`:
- Around line 15-16: Pass the target directory to resolvePackageManager at both
call sites: use the existing cwd parameter in
packages/cli/src/utils/dependencies.ts (lines 15-16), and use getRoot() in
packages/cli/src/commands/start.ts (lines 30-31), so package-manager detection
evaluates the directory each command operates on.

---

Nitpick comments:
In `@packages/cli/src/utils/commands.test.ts`:
- Around line 32-33: Update the command tests around the shared cwd setup and
file operations to mock node:fs rather than calling fs.mkdtempSync,
fs.writeFileSync, or fs.rmSync against the real filesystem. Mock the existsSync
dependency used by commands.ts and configure its return value per test,
preserving the existing command assertions and cleanup behavior without creating
temporary directories or files.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: b4d6dddf-08b3-406d-ac96-d20797ff66cd

📥 Commits

Reviewing files that changed from the base of the PR and between adf842c and 31a73e2.

⛔ Files ignored due to path filters (2)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml and included by none
  • specs/package-manager-resolution.md is excluded by none and included by none
📒 Files selected for processing (5)
  • packages/cli/package.json
  • packages/cli/src/commands/start.ts
  • packages/cli/src/utils/commands.test.ts
  • packages/cli/src/utils/commands.ts
  • packages/cli/src/utils/dependencies.ts

Comment thread packages/cli/src/commands/start.ts Outdated
Comment thread packages/cli/src/utils/commands.test.ts
Comment thread packages/cli/src/utils/dependencies.ts Outdated
getPreferredPackageManager() returned the raw stdout of `na ?` with no
validation, and every caller interpolates that value into a shell command.

`ni` ran in non-programmatic mode, where an agent that is not on PATH makes
it render a confirm prompt to stdout instead of failing. Both the
terminalLink non-TTY fallback and the "(y/N)" option contain "(", so the
command became unparseable:

  sh -c "Would you like to globally install pnpm (https://…)? > (y/N) run build"
    -> /bin/sh: syntax error: unexpected "(" -> exit 2

The diagnostic that would have explained it goes to stderr, which spawnSync
discards.

Resolution now uses the ni library API with programmatic: true, so the
prompt path is unreachable, and the agent is validated against ni's known
agents before it can reach a shell. When the agent is not installed, the CLI
logs what it detected and substitutes an available one instead of failing,
so every build that works today keeps working.

The return value is split into { agent, command, argv }, which also fixes
two latent bugs: a `volta run` prefix broke start.ts, where the value was
passed as argv[0] of a spawn without a shell, and dependencies.ts, which
compared it with === 'npm'. Both now have regression tests.

getPackageRootDir and getDepPackageJSON existed only to locate the `na`
binary and nothing else imports them, so they are removed along with the now
unused resolve-pkg dependency.

Ref: FAS-1199
@vlaux
vlaux force-pushed the fix/FAS-1199-package-manager-resolution branch from 4f7722c to 84eb880 Compare July 29, 2026 18:53
@vlaux

vlaux commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

Added regression tests for the two latent bugs this PR fixes, which had no coverage before:

  • src/utils/dependencies.test.ts — asserts npm is still recognised behind a volta run prefix, so the install subcommand stays install and not add. Against the previous code, 'volta run npm' === 'npm' was false.
  • src/commands/start.test.ts — asserts the Volta prefix is split into the spawn arguments (spawn('volta', ['run', 'yarn', 'next', …])). Against the previous code the whole string was passed as argv[0] of a spawn without a shell.

Coverage of the changed files is now 100% (commands.ts, dependencies.ts, start.ts), which should clear the Sonar coverage gate.

The other Sonar condition, 0.00% Security Hotspots Reviewed on New Code, needs a human in the Sonar UI. The hotspots are the child_process calls in start.ts, which were already there and only count as new code because the lines moved.

@vtex vtex deleted a comment from sonar-workflows Bot Jul 29, 2026
Review feedback on #3422.

resolvePackageManager() takes a cwd, but both call sites were calling it
with no argument, so detection ran against process.cwd() instead of the
directory the command operates on. That is wrong whenever the CLI is invoked
against another path, which `faststore start <path>` supports. Pass the
store root in start.ts and the install directory in dependencies.ts.

`faststore start` also ignored the exit status of the build it triggers, so
a failed build was followed by `next start` against a missing .next. It now
stops with the reason instead.

The unknown-agent test used "deno", which is not in ni 0.21.12's agent list
but would become one if the dependency were bumped, silently turning the
assertion into a different code path. It now uses a string that cannot ever
be a real agent.

Ref: FAS-1199

@renatomaurovtex renatomaurovtex left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the full diff (cli source, tests, spec doc; skimmed the lockfile churn). Impressive root-cause work — the diagnosis of ni's interactive fallback leaking a confirm prompt into a shell command is exactly right, and moving to the library API with programmatic: true is the only correct fix (the na binary indeed doesn't expose it). I verified the claims that matter:

  • @antfu/ni@0.21.12 (catalog) exports everything used here: detect({ programmatic, cwd }), agents, LOCKS, cmdExists, getCommand, getVoltaPrefix, Agent.
  • getPackageRootDir/getDepPackageJSON have no other consumers, and resolve-pkg is only otherwise used by @faststore/diagnostics, which declares its own copy — so the manifest removal is clean (and it's a dep reduction on a published package, no Dependency Discipline concern).
  • The remaining getPreferredPackageManager callers (build.ts, dev.ts, test.ts, generate-graphql.ts) all interpolate into shell: true commands, so keeping the wrapper returning the Volta-prefixed command string preserves their semantics exactly.
  • The agent/command/argv split does fix the two latent Volta bugs described (start.ts shell-less spawn with the whole string as argv[0]; dependencies.ts === 'npm' comparison).
  • specs/ already has precedent on dev (contract-switcher.md), so the spec doc placement is fine.

Findings, none blocking:

🟡 [edge case] Substituting on the install path can create the exact dual-lockfile state this PR diagnoses. installDependencies is a mutating operation: if pnpm is detected-but-missing in a store with only pnpm-lock.yaml, the substitution runs yarn add …, which writes a yarn.lock next to pnpm-lock.yaml — manufacturing the ambiguity that caused FAS-1199 (and yarn may choke on workspace: protocol deps in a pnpm project). Substitute-don't-fail is the right call for read-only commands (build, start), but consider failing hard (or refusing to substitute when a foreign lockfile is present) specifically in installDependencies. Non-blocking since the incident path is the build one, but worth deciding deliberately.

🟡 [ci] SonarQube red — confirm it's the known cli coverage-feed gate, not a regression. Same pattern as #3419: cli tests don't feed coverage to Sonar, so the new-code gate fails despite this PR adding 15+ test cases. If that's what the Sonar report shows, fine to merge past it; if not, worth a look.

💬 [question] FALLBACK_AGENTS = ['yarn', 'npm'] ignores an installed pnpm/bun. If the detected agent is missing and only pnpm is on PATH, this throws NoAvailablePackageManagerError even though a usable PM exists. Deliberate (yarn/npm being the only images you support), or worth appending the remaining known agents as last resorts?

💬 [follow-up] Detection cwd for the remaining callers. start.ts now correctly resolves against basePath, but build/dev/test/generate-graphql still detect at process.cwd() through the wrapper. Pre-existing behavior, but now that resolvePackageManager(cwd) exists, threading basePath through them would make faststore build <path> consistent. Same for start.ts's build fallback: spawnSync(${command} faststore build) doesn't forward args.path or set cwd, so serving a non-cwd store still builds the wrong directory — also pre-existing, fits the same follow-up.

The new build-failure guard in start.ts (throw instead of serving a stale/absent build) is a good behavior fix, and the test suites are genuinely strong — the not-a-package-manager as never future-proofing on the UnknownAgentError path is a nice touch.

Verdict: Approved with comments

Blocking (🔴/🟠):

  • None.

Non-blocking (🟡/💬):

  • Substitution on the mutating installDependencies path can create the dual-lockfile ambiguity — consider fail-hard there.
  • Confirm SonarQube red is the known cli coverage-feed gate (#3419 pattern).
  • FALLBACK_AGENTS omits pnpm/bun; detection cwd follow-up for the remaining callers.

Checks to confirm before merge: pnpm lint · build · pnpm turbo run test --filter=@faststore/cli · SonarQube triage as above.

Review feedback on #3422.

installDependencies is the one call site that mutates the project:
substituting e.g. yarn into a pnpm store would run `yarn add`, writing a
yarn.lock next to pnpm-lock.yaml — manufacturing exactly the dual-lockfile
ambiguity behind FAS-1199 (and yarn may not understand workspace: ranges in
a pnpm project). resolvePackageManager() now takes { substitute }, and the
install path resolves with substitute: false, throwing an actionable error
instead of writing a conflicting lockfile.

The missing-dependencies loop in generate.ts was forEach(async ...), so
that throw would surface as an unhandled rejection instead of going through
oclif's error handling. It is now a for...of, with the spinner stopped in a
finally.

FALLBACK_AGENTS gains pnpm and bun as last resorts: the branch where yarn
and npm are both missing previously threw even when a usable package
manager was installed. Substitution order is unchanged for every case that
resolved before.

Ref: FAS-1199

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vlaux
vlaux requested a review from renatomaurovtex July 30, 2026 21:38

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
packages/cli/src/utils/commands.ts (1)

77-80: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the avoidable tuple assertion.

Construct argv as a non-empty tuple instead of asserting that split() produced one.

Proposed refactor
-  const voltaPrefix = getVoltaPrefix()
-  const command = voltaPrefix ? `${voltaPrefix} ${binOf(agent)}` : binOf(agent)
+  const argv: [string, ...string[]] = [binOf(agent)]
+  const voltaPrefix = getVoltaPrefix()
 
-  return { agent, command, argv: command.split(' ') as [string, ...string[]] }
+  if (voltaPrefix) {
+    argv.unshift(...voltaPrefix.split(' '))
+  }
+
+  return { agent, command: argv.join(' '), argv }

As per coding guidelines, TypeScript files must “avoid type assertions when possible.”

🤖 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 `@packages/cli/src/utils/commands.ts` around lines 77 - 80, Update the command
construction near binOf(agent) so argv is created as a guaranteed non-empty
tuple without relying on a TypeScript type assertion on command.split(' ').
Preserve the existing command value and argument ordering while using tuple
construction or another assertion-free approach.

Source: Coding guidelines

packages/cli/src/commands/start.test.ts (1)

49-64: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Assert the build step is skipped when .next already exists.

This test only checks the spawn call; adding expect(spawnSyncMock).not.toHaveBeenCalled() would close the loop and make the "no build" branch explicit.

✅ Suggested addition
     expect(spawnMock).toHaveBeenCalledWith(
       'yarn',
       ['next', 'start', path.join(storeDir, '.faststore'), '-p', '3000'],
       { stdio: 'inherit' }
     )
+    expect(spawnSyncMock).not.toHaveBeenCalled()
🤖 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 `@packages/cli/src/commands/start.test.ts` around lines 49 - 64, Update the
test case “spawns the package manager binary without a shell” to also assert
that spawnSyncMock is not called, explicitly verifying the build step is skipped
when the existing .next directory is present. Keep the current spawn and
package-manager assertions unchanged.
🤖 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 `@packages/cli/src/commands/start.test.ts`:
- Around line 38-47: Update the beforeEach and afterEach hooks in the start
command tests to mock filesystem operations instead of calling fs.mkdtempSync,
fs.mkdirSync, and fs.rmSync against the real filesystem. Reuse the shared
filesystem-mocking approach established for commands.test.ts while preserving
the temporary storeDir and .next setup behavior.

In `@packages/cli/src/utils/commands.test.ts`:
- Around line 31-44: Update the test setup and teardown around beforeEach and
afterEach to mock node:fs filesystem operations instead of calling mkdtempSync
and rmSync on the real disk. Ensure the mocked temporary-directory behavior
still provides each test with an isolated cwd and preserves the existing
command-test behavior, including any writeFileSync usage in this suite.

---

Nitpick comments:
In `@packages/cli/src/commands/start.test.ts`:
- Around line 49-64: Update the test case “spawns the package manager binary
without a shell” to also assert that spawnSyncMock is not called, explicitly
verifying the build step is skipped when the existing .next directory is
present. Keep the current spawn and package-manager assertions unchanged.

In `@packages/cli/src/utils/commands.ts`:
- Around line 77-80: Update the command construction near binOf(agent) so argv
is created as a guaranteed non-empty tuple without relying on a TypeScript type
assertion on command.split(' '). Preserve the existing command value and
argument ordering while using tuple construction or another assertion-free
approach.
🪄 Autofix (Beta)

❌ Autofix failed (check again to retry)

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a9d68b87-1027-4938-bb87-2e8975c6560b

📥 Commits

Reviewing files that changed from the base of the PR and between 31a73e2 and 1a06a13.

⛔ Files ignored due to path filters (2)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml and included by none
  • specs/package-manager-resolution.md is excluded by none and included by none
📒 Files selected for processing (8)
  • packages/cli/package.json
  • packages/cli/src/commands/start.test.ts
  • packages/cli/src/commands/start.ts
  • packages/cli/src/utils/commands.test.ts
  • packages/cli/src/utils/commands.ts
  • packages/cli/src/utils/dependencies.test.ts
  • packages/cli/src/utils/dependencies.ts
  • packages/cli/src/utils/generate.ts

Comment thread packages/cli/src/commands/start.test.ts
Comment thread packages/cli/src/utils/commands.test.ts
@vtex vtex deleted a comment from coderabbitai Bot Jul 31, 2026
Review feedback on #3422 (CodeRabbit).

Constructing argv as a non-empty tuple and deriving command from it
removes the type assertion on command.split(' '). Same values, one
source of truth for both forms.

Also asserts in start.test.ts that the build step is skipped when
.next already exists, making the no-build branch explicit.

Ref: FAS-1199

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@sonar-workflows

Copy link
Copy Markdown

Failed Quality Gate failed

  • 26.60% Coverage on New Code (is less than 75.00%)
  • 0.00% Security Hotspots Reviewed on New Code (is less than 100.00%)

Project ID: vtex_faststore_f0a862d5-9557-49f9-8d09-de40caa76622

View in SonarQube

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants