Skip to content

feat(stack-cli): auto-install emulator deps on pull#1384

Merged
BilalG1 merged 4 commits intodevfrom
emulator-dependency-checks
Apr 27, 2026
Merged

feat(stack-cli): auto-install emulator deps on pull#1384
BilalG1 merged 4 commits intodevfrom
emulator-dependency-checks

Conversation

@BilalG1
Copy link
Copy Markdown
Collaborator

@BilalG1 BilalG1 commented Apr 24, 2026

Summary

  • stack emulator pull now preflights VM dependencies (QEMU binaries, socat/curl/nc/lsof/openssl/zstd, and aarch64 UEFI firmware on arm64) before downloading.
  • Missing deps are listed, then installed with user confirmation via brew on macOS (bootstrapping Homebrew itself if absent) or sudo apt-get on Linux.
  • Skipped when --skip-snapshot is passed, since that path never boots the VM.
  • gh / GITHUB_TOKEN are intentionally excluded from the auto-install set.

Test plan

  • node packages/stack-cli/dist/index.js emulator pull on a machine with all deps present → no prompt, proceeds as before.
  • Unlink a dep (e.g. brew unlink zstd) and rerun → missing dep listed, decline prompt → exits with a clear error; accept prompt → brew install runs and pull continues.
  • emulator pull --skip-snapshot still bypasses the dep check.
  • Linux path: missing binaries trigger sudo apt-get update && sudo apt-get install -y ….

Summary by CodeRabbit

  • New Features

    • Emulator pull now detects missing host dependencies and, on macOS/Linux with an interactive terminal and supported package tools, shows a proposed install plan, prompts for confirmation, and can auto-install required packages (including optional ARM64 firmware). Homebrew will be bootstrapped if absent.
    • Use --skip-snapshot to bypass the interactive dependency check and installation.
  • Behavior

    • In non-interactive or unsupported environments, the tool falls back to the prior preflight behavior instead of attempting installation.

`stack emulator pull` now detects missing QEMU/VM dependencies (and
aarch64 UEFI firmware on arm64), prints the list, and — with user
confirmation — installs them via brew (bootstrapping Homebrew first if
absent) or apt. Skipped under --skip-snapshot.
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 24, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
stack-auth-hosted-components Ready Ready Preview, Comment Apr 24, 2026 10:54pm
stack-backend Ready Ready Preview, Comment Apr 24, 2026 10:54pm
stack-dashboard Ready Ready Preview, Comment Apr 24, 2026 10:54pm
stack-demo Ready Ready Preview, Comment Apr 24, 2026 10:54pm
stack-docs Ready Ready Preview, Comment Apr 24, 2026 10:54pm
stack-preview-backend Ready Ready Preview, Comment Apr 24, 2026 10:54pm
stack-preview-dashboard Ready Ready Preview, Comment Apr 24, 2026 10:54pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 24, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6590778b-f51e-4371-ae0e-f43f7d6cda67

📥 Commits

Reviewing files that changed from the base of the PR and between 19d2faa and 9fab6cc.

📒 Files selected for processing (1)
  • packages/stack-cli/src/commands/emulator.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/stack-cli/src/commands/emulator.ts

📝 Walkthrough

Walkthrough

The emulator pull flow adds an interactive ensureDepsForPull(arch) step that detects missing qemu/utility binaries and optional aarch64 firmware, formats install instructions via a new installHint helper, and—on interactive macOS/Homebrew or apt-get Linux—proposes and can auto-install packages (bootstrapping Homebrew if needed); skip with --skip-snapshot. Non-interactive or unsupported platforms fall back to preflightForVmStart.

Changes

Cohort / File(s) Summary
Emulator Dependency Management
packages/stack-cli/src/commands/emulator.ts
Refactored dependency preflight to per-OS package fields (linuxPkg, macPkg) and centralized installHint. Added ensureDepsForPull(arch) for emulator pull (skippable with --skip-snapshot) to detect missing binaries and optional aarch64 firmware, present a platform-specific install plan, prompt on interactive macOS/Linux, optionally bootstrap Homebrew and install aggregated packages; falls back to existing preflightForVmStart in non-interactive/unsupported cases.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Pull as "emulator pull"
    participant Deps as "ensureDepsForPull"
    participant Detect as "Dependency Detector"
    participant PkgMgr as "Package Manager (brew/apt-get)"
    participant System as "Host System"

    User->>Pull: Run `emulator pull` (with/without --skip-snapshot)
    alt --skip-snapshot
        Pull->>Pull: Skip ensureDepsForPull
    else
        Pull->>Deps: ensureDepsForPull(arch)
        Deps->>Detect: Check missing qemu/utilities + optional firmware
        Detect->>System: Query installed tools/packages
        System-->>Detect: Installed state
        Detect-->>Deps: Missing/optional list
        Deps->>Deps: Build platform-specific install plan (installHint)
        Deps->>User: Display plan (TTY-only on macOS/Linux)
        alt Interactive TTY on supported OS
            Deps->>User: Prompt for confirmation
            User-->>Deps: Confirm / Decline
            alt Confirm
                Deps->>PkgMgr: Bootstrap if needed (e.g., install brew)
                PkgMgr->>System: Install packages
                System-->>PkgMgr: Install results
                PkgMgr-->>Deps: Success
                Deps-->>Pull: Dependencies satisfied
            else Decline
                Deps-->>Pull: Fail / abort
            end
        else Non-interactive or unsupported
            Deps-->>Pull: Fallback to preflightForVmStart
        end
    end
    Pull->>Pull: Continue pull workflow
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through lines with eager cheer,

Found missing tools and firmware near.
Brew and apt, a prompt in view,
I offered fixes, tidy and true.
Pull on—your VM's almost here!

🚥 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
Title check ✅ Passed The title accurately summarizes the main change: adding automatic dependency installation for emulator pull operations, which is the core feature of this changeset.
Description check ✅ Passed The description provides a clear summary of functionality, test plan with checkboxes, and rationale for design decisions. It aligns well with the PR objectives and change scope.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch emulator-dependency-checks

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 and usage tips.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 24, 2026

Greptile Summary

This PR adds a dependency preflight step to stack emulator pull that detects missing QEMU binaries, common VM tools, and aarch64 firmware, then offers to auto-install them via Homebrew (bootstrapping it if absent) on macOS or apt-get on Linux.

  • P1: After bootstrapping Homebrew on Apple Silicon, brew lands at /opt/homebrew/bin/brew which is not in the current process's PATH; the immediately following execFileSync(\"brew\", [\"install\", ...]) will fail with "command not found", leaving users in a broken half-installed state.
  • P2 (security): The Homebrew install is performed via curl | bash without integrity verification — the user-facing confirmation prompt should disclose this.

Confidence Score: 3/5

Not safe to merge as-is: the Apple Silicon PATH bug will cause brew install to fail immediately after a fresh Homebrew bootstrap, leaving deps uninstalled and the pull aborting with a confusing error.

One P1 defect (brew PATH not updated for the current process on Apple Silicon after bootstrap) that reliably triggers on the machines most likely to need the auto-install path. The security concern around curl-pipe-bash without integrity verification is P2 but worth addressing.

packages/stack-cli/src/commands/emulator.ts — specifically the ensureDepsForPull function around the Homebrew bootstrap block (lines 492-506).

Security Review

  • Remote code execution via curl-pipe-bash (emulator.ts line 494): The Homebrew bootstrap downloads and executes a shell script from raw.githubusercontent.com without checksum/integrity verification. A TLS-inspecting proxy or CDN compromise could substitute malicious content. This runs after user confirmation but the prompt does not disclose that arbitrary remote code will be executed.
  • No other security issues identified; package names passed to brew/apt-get are derived from a hardcoded allowlist, not user input, so there is no argument-injection risk.

Important Files Changed

Filename Overview
packages/stack-cli/src/commands/emulator.ts Adds ensureDepsForPull to auto-install QEMU/tool deps before emulator pull; contains a P1 PATH bug where brew is unreachable on Apple Silicon after a fresh Homebrew bootstrap, and a P2 non-TTY fallback gap.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: packages/stack-cli/src/commands/emulator.ts
Line: 494-501

Comment:
**`brew` not in PATH after bootstrap on Apple Silicon**

After the Homebrew install script runs, the newly created `/opt/homebrew/bin/brew` binary is **not** in the current process's PATH — environment variables are inherited at process start and the install script only updates shell profile files (e.g. `~/.zprofile`). On Apple Silicon Macs the default system `PATH` does not include `/opt/homebrew/bin`, so the immediately following `execFileSync("brew", ["install", ...])` call will throw a "command not found" error, leaving the user with a half-installed state.

The fix is to resolve the `brew` binary by its known absolute path after a fresh install:
```typescript
// After Homebrew bootstrap, resolve brew's absolute path since the
// current process PATH has not been updated.
const brewBin = existsSync("/opt/homebrew/bin/brew")
  ? "/opt/homebrew/bin/brew"
  : "/usr/local/bin/brew";   // Intel fallback

console.log("\nInstalling packages...");
execFileSync(brewBin, ["install", ...pkgList], { stdio: "inherit" });
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: packages/stack-cli/src/commands/emulator.ts
Line: 492-496

Comment:
**Homebrew bootstrap executes remote code without integrity verification**

The Homebrew install script is fetched over HTTPS and piped directly to `bash` without any checksum verification. If `raw.githubusercontent.com` is intercepted (e.g. a corporate TLS-inspecting proxy) or GitHub's CDN is compromised, this would silently execute malicious code on the user's machine.

Consider verifying the script's SHA-256 against a known-good hash before executing, or at minimum document this risk in the user-facing prompt so users can make an informed decision.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: packages/stack-cli/src/commands/emulator.ts
Line: 460-465

Comment:
**Non-TTY path on darwin/linux produces unhelpful error**

`ensureDepsForPull` is only reached when `missingBins.length > 0 || firmwareMissing`. In non-TTY environments (CI, piped input) on macOS or Linux, `confirmPrompt` immediately throws "stdin is not a TTY" with no install hints. Consider falling back to `requireBinaries` (which produces actionable install hints) when stdin is not a TTY, rather than only throwing the TTY error.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "feat(stack-cli): auto-install emulator d..." | Re-trigger Greptile

Comment thread packages/stack-cli/src/commands/emulator.ts Outdated
Comment thread packages/stack-cli/src/commands/emulator.ts
Comment thread packages/stack-cli/src/commands/emulator.ts Outdated
- Resolve `brew` by absolute path after a fresh Homebrew bootstrap, since
  the installer only updates shell profiles and not the current process's
  PATH (breaks on clean Apple Silicon installs).
- Fall back to `preflightForVmStart` when stdin is not a TTY so CI/piped
  runs get the standard per-binary install hints instead of a generic
  TTY error.
- Disclose in the confirmation prompt that Homebrew bootstrap executes
  remote code from raw.githubusercontent.com.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/stack-cli/src/commands/emulator.ts (1)

513-522: ⚠️ Potential issue | 🟠 Major

nc Linux package mapping installs the wrong binary.

The entry bin("nc", "ncat", "netcat") at line 518 has its linuxPkg value passed directly to sudo apt-get install via the pkgList at line 502. On Debian/Ubuntu, the ncat package provides /usr/bin/ncat (from the nmap suite), not /usr/bin/nc. The nc binary is provided by netcat-openbsd (or netcat-traditional). After auto-install completes, commandExists("nc") will still return false, and the subsequent operation will fail with the same "missing binaries" error — the exact failure this new flow attempts to prevent.

Note that netcat is correct for the macOS Homebrew formula name, but netcat-openbsd is required on Debian.

Proposed fix
-    bin("nc", "ncat", "netcat"),
+    bin("nc", "netcat-openbsd", "netcat"),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/stack-cli/src/commands/emulator.ts` around lines 513 - 522, The
Linux package mapping for the "nc" binary in commonVmBins() is incorrect: update
the BinarySpec entry created via bin("nc", ...) so its linuxPkg uses the Debian
package that provides /usr/bin/nc (e.g., "netcat-openbsd") instead of "ncat" or
"netcat"; leave the macPkg (Homebrew) as "netcat" and keep the binary name "nc"
unchanged so commandExists("nc") succeeds after auto-install. Locate the bin
call for "nc" in commonVmBins() and change only the linux package string to
"netcat-openbsd".
🧹 Nitpick comments (1)
packages/stack-cli/src/commands/emulator.ts (1)

453-507: Consider re-verifying after install and de-duplicating the zstd spec.

A couple of quality-of-life follow-ups for ensureDepsForPull:

  1. After brew install / apt-get install succeeds, no re-check is performed. If the install ran cleanly but didn't actually provide a required binary (see the nc/ncat issue), the user sees a "still missing" error several seconds later from captureLocalSnapshot. Calling preflightForVmStart("pull", arch) (or re-running the same commandExists filter) at the end would surface that immediately with context.
  2. bin("zstd", "zstd", "zstd") is repeated on line 454 and line 533. Consider hoisting it to a zstdBin() helper to keep the spec authoritative in one place.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/stack-cli/src/commands/emulator.ts` around lines 453 - 507,
ensureDepsForPull currently doesn't re-verify that installed packages provided
the required binaries and repeats the zstd bin spec; after the install steps
(after brew/apt-get execFileSync) re-run the same check (e.g., call
preflightForVmStart("pull", arch) or re-evaluate missingBins via commandExists
and firmware check) and throw or surface the same contextual error if things are
still missing so users see immediate feedback; also deduplicate the zstd spec by
centralizing it (create a zstdBin() helper or a shared constant used where
bin("zstd", "zstd", "zstd") appears) and use that symbol in the allBins array
and any other place to keep the spec authoritative.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/stack-cli/src/commands/emulator.ts`:
- Around line 459-465: The Linux auto-install currently assumes apt-get for all
distros; update the platform check in emulator.ts so that after confirming
process.platform === "linux" you also verify commandExists("apt-get") before
performing the apt-get install path—if commandExists("apt-get") is false, call
preflightForVmStart("pull", arch) and return (mirroring the existing non-Linux
fallback). Apply the same guard where the other apt-get auto-install block
occurs (the second occurrence around the related install logic) to avoid running
apt-get on non-Debian systems.
- Around line 472-505: The install plan can end up calling `brew install` with
an empty `pkgList` when `firmwareMissing` is true on macOS but `missingBins`
yields no packages; update the flow around `pkgs`/`pkgList` to avoid invoking
`brew install` with no args: detect the case where `pkgList.length === 0 &&
firmwareMissing && platform === "darwin"` and either (A) add `"qemu"` to `pkgs`
so `pkgList` includes a package to reinstall firmware, or (B) short-circuit and
call `preflightForVmStart()` / throw a clear `CliError` to surface the firmware
issue instead of attempting an empty install; ensure the change touches the
variables `pkgs`, `firmwareMissing`, `platform`, and the code paths that call
`execFileSync("brew", ...)` so you never call `brew install` with an empty
argument list.

---

Outside diff comments:
In `@packages/stack-cli/src/commands/emulator.ts`:
- Around line 513-522: The Linux package mapping for the "nc" binary in
commonVmBins() is incorrect: update the BinarySpec entry created via bin("nc",
...) so its linuxPkg uses the Debian package that provides /usr/bin/nc (e.g.,
"netcat-openbsd") instead of "ncat" or "netcat"; leave the macPkg (Homebrew) as
"netcat" and keep the binary name "nc" unchanged so commandExists("nc") succeeds
after auto-install. Locate the bin call for "nc" in commonVmBins() and change
only the linux package string to "netcat-openbsd".

---

Nitpick comments:
In `@packages/stack-cli/src/commands/emulator.ts`:
- Around line 453-507: ensureDepsForPull currently doesn't re-verify that
installed packages provided the required binaries and repeats the zstd bin spec;
after the install steps (after brew/apt-get execFileSync) re-run the same check
(e.g., call preflightForVmStart("pull", arch) or re-evaluate missingBins via
commandExists and firmware check) and throw or surface the same contextual error
if things are still missing so users see immediate feedback; also deduplicate
the zstd spec by centralizing it (create a zstdBin() helper or a shared constant
used where bin("zstd", "zstd", "zstd") appears) and use that symbol in the
allBins array and any other place to keep the spec authoritative.
🪄 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: 2d806fdb-de58-4b06-8c79-73a1c3516b2c

📥 Commits

Reviewing files that changed from the base of the PR and between 2f71990 and 3208bf8.

📒 Files selected for processing (1)
  • packages/stack-cli/src/commands/emulator.ts

Comment thread packages/stack-cli/src/commands/emulator.ts
Comment thread packages/stack-cli/src/commands/emulator.ts
Comment thread packages/stack-cli/src/commands/emulator.ts
…nly miss on macOS

- Only take the apt-get path when `apt-get` is actually available;
  otherwise fall through to the standard per-binary install hints so
  Fedora/Arch/Alpine/etc. users get something actionable.
- On macOS arm64 when all binaries are present but aarch64 firmware is
  missing, add `qemu` to the brew install set so a reinstall recreates
  the bundled firmware; guard against an empty package list by falling
  back to preflight.
@BilalG1 BilalG1 requested a review from nams1570 April 24, 2026 23:36
@BilalG1 BilalG1 merged commit 5423d77 into dev Apr 27, 2026
33 checks passed
@BilalG1 BilalG1 deleted the emulator-dependency-checks branch April 27, 2026 20:01
@promptless
Copy link
Copy Markdown
Contributor

promptless Bot commented Apr 27, 2026

Promptless prepared a documentation update related to this change.

Triggered by PR #1384

Adds documentation for the stack emulator CLI, covering VM prerequisites, auto-install of dependencies on macOS and Linux, and all emulator commands (pull, start, stop, reset, status, run, list-releases).

Review: Document Local Emulator CLI commands

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.

1 participant