Skip to content

fix(install): fail fast on license gate so partial install can't land#2706

Merged
ericksoa merged 2 commits into
NVIDIA:mainfrom
latenighthackathon:fix/install-license-atomic
May 4, 2026
Merged

fix(install): fail fast on license gate so partial install can't land#2706
ericksoa merged 2 commits into
NVIDIA:mainfrom
latenighthackathon:fix/install-license-atomic

Conversation

@latenighthackathon

@latenighthackathon latenighthackathon commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Summary

curl -fsSL https://www.nvidia.com/nemoclaw.sh | bash (no flags) on a non-TTY host runs phases [1/3] Node.js + [2/3] NemoClaw CLI to completion before failing at the third-party-software notice in phase [3/3]. The user is left with nemoclaw --version working but ~/.nemoclaw/usage-notice.json never created — the license was never accepted, but the binary is on PATH. A retry hits the same wall and they have to rm -rf ~/.nemoclaw ~/.local/bin/nemoclaw to recover. Fixes #2671.

Problem

The license check in show_usage_notice is deterministic: when stdin isn't a TTY, /dev/tty isn't openable, and neither --non-interactive nor --yes-i-accept-third-party-software is set, it errors out. But that check runs as part of phase [3/3], after install_nodejs and install_nemoclaw have already done their work. We knew up front the install couldn't finish, and we still ran two phases of disk changes before saying so.

Fix

Run the same precondition check at the top of main(), right after flag parsing. Same error message — just emitted before phase 1 instead of after phase 2. Skipped when NON_INTERACTIVE=1, ACCEPT_THIRD_PARTY_SOFTWARE=1 (the #2692 gate), stdin is a TTY, or /dev/tty is openable.

Stacks cleanly with #2692 — that PR makes --yes-i-accept-third-party-software work on its own; this PR makes the no-flag case fail without leaving a half-install behind.

Test plan

  • npx vitest run --project installer-integration -t "installer atomicity" — 3 new sourced tests:
    • #2671: curl|bash with no flags exits 1 BEFORE phase 1 — stub node / npm / docker write to a phase log on every invocation; assert the log is empty after the run, and that no [1/3] or [2/3] step output appeared.
    • --yes-i-accept-third-party-software alone is sufficient to clear the fail-fast gate — guards against the new check over-firing and regressing fix(install): honor --yes-i-accept-third-party-software in non-TTY mode #2692.
    • --non-interactive alone is sufficient to clear the fail-fast gate — same.
  • One existing test (attempts nvm upgrade when system Node.js is below minimum version, line 116) needed NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE: "1" added to its env so it can reach the Node-version path. The test isn't about licensing, but with the new gate it can't get past main()'s entry without something to satisfy it. Inline comment explains why.
  • Full installer-integration suite: 71/71 pass (66.7s).
  • npm run typecheck clean.

Signed-off-by: latenighthackathon latenighthackathon@users.noreply.github.com

Summary by CodeRabbit

  • New Features

    • Installer performs an early preflight check and aborts quickly when interactive license acceptance requires a TTY, preventing partial runs.
  • Bug Fixes

    • Explicit third‑party acceptance now forces non‑interactive behavior so gating is consistent across install phases.
  • Tests

    • Added tests verifying installer behavior for TTY vs non‑TTY and for acceptance vs non‑acceptance paths.

@copy-pr-bot

copy-pr-bot Bot commented Apr 29, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 2e5395af-8651-48b5-9cef-f22b738ea0a8

📥 Commits

Reviewing files that changed from the base of the PR and between af55822 and 0464769.

📒 Files selected for processing (2)
  • scripts/install.sh
  • test/install-preflight.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/install-preflight.test.ts

📝 Walkthrough

Walkthrough

The installer now aborts early if interactive third‑party license acceptance cannot proceed: when not non‑interactive, stdin isn't a TTY, and /dev/tty can't be opened it exits before Phase 1/2. If the accept flag is explicitly set, the script forces non‑interactive mode so phases may run.

Changes

Early Termination Guard

Layer / File(s) Summary
Preflight Check / Env normalization
scripts/install.sh
If ACCEPT_THIRD_PARTY_SOFTWARE=1 is present, NON_INTERACTIVE=1 is forced for the remainder of the run. Adds an early fail-fast check that aborts with an error when not non-interactive, stdin is not a TTY, and /dev/tty cannot be opened.
Control Flow Effect
scripts/install.sh
Prevents Phase 1/Phase 2 from executing when interactive acceptance cannot proceed, ensuring the installer does not leave a partial install state.

Installer Atomicity Tests

Layer / File(s) Summary
Test adjustments
test/install-preflight.test.ts
Existing nvm-upgrade test environment now sets NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE=1 to bypass the license gate.
Test helpers / stubs
test/install-preflight.test.ts
Adds runInstaller(env, options) helper that creates a temp fake node/npm/docker which log invocations to phases.log, and spawns scripts/install.sh with empty stdin to simulate non-TTY.
Behavioral tests
test/install-preflight.test.ts
New installer atomicity (#2671) suite: (1) verifies non‑TTY + no acceptance flag exits non‑zero and leaves phases.log empty; (2) verifies NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE=1 allows phases to run; (3) verifies NEMOCLAW_NON_INTERACTIVE=1 allows phases to run.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant Installer
  participant TTY as "/dev/tty"
  participant Phase1 as "Phase 1: Node.js"
  participant Phase2 as "Phase 2: CLI"

  User->>Installer: invoke scripts/install.sh
  Installer->>Installer: check NON_INTERACTIVE env / accept flag
  Installer->>TTY: attempt to open /dev/tty
  alt /dev/tty unavailable AND not non-interactive AND no accept flag
    Installer->>User: print "Interactive third-party software acceptance requires a TTY"
    Installer-->>User: exit 1
  else accept flag present or non-interactive
    Installer->>Phase1: run Node.js install
    Phase1-->>Installer: success
    Installer->>Phase2: run CLI install
    Phase2-->>Installer: success
    Installer->>User: continue to onboarding
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰
I nudged the install to check the door,
If no TTY's there, we stop before.
No half‑done CLI left in the hay,
Accept or non‑interactive clears the way.
A tidy run — hooray!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: implementing a fail-fast check on the license gate to prevent partial installations.
Linked Issues check ✅ Passed The PR directly addresses issue #2671 by implementing the fail-fast check that prevents partial installations when license acceptance is not verified.
Out of Scope Changes check ✅ Passed All changes in scripts/install.sh and test/install-preflight.test.ts are directly scoped to implementing the fail-fast license gate and related tests.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Review rate limit: 9/10 reviews remaining, refill in 6 minutes.

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

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/install.sh`:
- Around line 1373-1381: The current TTY preflight lets
ACCEPT_THIRD_PARTY_SOFTWARE=1 bypass the TTY check but phase 3 still needs
NON_INTERACTIVE=1, causing later failures in show_usage_notice(); fix by
ensuring acceptance implies non-interactive: when ACCEPT_THIRD_PARTY_SOFTWARE is
set, set NON_INTERACTIVE=1 early (or change the TTY preflight to not skip the
TTY check for ACCEPT_THIRD_PARTY_SOFTWARE), and then keep the existing TTY
check/early error around the conditional that references NON_INTERACTIVE and
ACCEPT_THIRD_PARTY_SOFTWARE so phase 3 (show_usage_notice()) will run in the
non-interactive path without later failing.

In `@test/install-preflight.test.ts`:
- Around line 2482-2533: The bypass tests currently only assert the TTY message
is absent which can false-pass; update the two bypass tests that call
runInstaller(...) (the ones around the existing assertions at lines ~2482-2533
and ~2550-2563) to also assert that the returned phases string is non-empty
(i.e., phases !== "" or expect(phases).not.toBe("")). This ensures the installer
actually progressed past preflight; reference the runInstaller helper and its
returned phases property when adding the additional assertion.
🪄 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: Enterprise

Run ID: a5fed121-4213-43e4-97a1-e68e408e0a64

📥 Commits

Reviewing files that changed from the base of the PR and between 502d725 and d07b6a9.

📒 Files selected for processing (2)
  • scripts/install.sh
  • test/install-preflight.test.ts

Comment thread scripts/install.sh Outdated
Comment thread test/install-preflight.test.ts
@wscurran wscurran added bug Something fails against expected or documented behavior NemoClaw CLI labels Apr 30, 2026
@wscurran

Copy link
Copy Markdown
Contributor

✨ Thanks for submitting this PR that fixes a bug where the installation process fails to check for license acceptance before proceeding with installation.


Related open PRs:


Related open issues:

A plain `curl -fsSL https://www.nvidia.com/nemoclaw.sh | bash` (no flags)
in non-TTY mode runs phases [1/3] Node.js + [2/3] NemoClaw CLI to
completion before failing at the third-party-software acceptance step
in phase [3/3]. The user is left in a half-installed state — the
nemoclaw binary is on PATH and `--version` works, but
~/.nemoclaw/usage-notice.json was never created (license never
accepted), and onboard cannot proceed. A retry hits the same failure
because the license helper still has nothing to read from.

The license gate already knew it could not succeed: in
show_usage_notice() the failure is deterministic when stdin is not a
TTY, /dev/tty is unopenable, NON_INTERACTIVE != 1, and
ACCEPT_THIRD_PARTY_SOFTWARE != 1. Hoist that same check to the top
of main(), right after flag parsing, so the install errors out
BEFORE phases 1/2 leave artifacts on disk. Same friendly error
message as before — just emitted before any state changes.

Effect on the user-visible flow:
- `curl|bash` (no flags) on a server with no /dev/tty: fail-fast,
  exit 1 with the existing TTY hint, no Node.js install attempted,
  no nemoclaw on PATH. Retry-after-fixing-the-flag is now clean.
- `curl|bash --yes-i-accept-third-party-software` (post NVIDIA#2670): clear,
  install proceeds.
- `curl|bash --non-interactive`: clear, install proceeds.
- Interactive run on a desktop terminal: clear, prompt as before.
- curl|bash piped from a desktop where /dev/tty IS openable: clear,
  show_usage_notice falls back to /dev/tty input as before.

- `npx vitest run --project installer-integration -t "installer atomicity"`
  — 3 new sourced tests:
    - NVIDIA#2671: curl|bash with no flags exits 1 BEFORE phase 1 — stub
      node/npm/docker record every invocation; assertion that the log
      stays empty is the atomicity proof.
    - --yes-i-accept-third-party-software alone clears the gate
      (regression-guard against an over-aggressive check).
    - --non-interactive alone clears the gate (same).
- `attempts nvm upgrade when system Node.js is below minimum version`
  needed `NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE=1` added to its env —
  the test exercises the Node-version path which is unrelated to
  license acceptance, and now (correctly) cannot reach Node detection
  without bypassing the license gate first.
- Full installer-integration suite: 71/71 pass (prev. 70 + 3 new − 1
  test re-bypassed = 71).

Fixes NVIDIA#2671.

Signed-off-by: latenighthackathon <latenighthackathon@users.noreply.github.com>
CodeRabbit flagged that ACCEPT_THIRD_PARTY_SOFTWARE=1 alone bypassed
the fail-fast preflight, but show_usage_notice is only one of two
phase-3 steps that need a TTY or --non-interactive. run_onboard has
the same gate, so a curl|bash run with --yes-i-accept-third-party-software
but no --non-interactive could still partial-fail at run_onboard,
leaving phases 1/2 on disk anyway.

Treat the acceptance flag as non-interactive intent: when
ACCEPT_THIRD_PARTY_SOFTWARE=1, set NON_INTERACTIVE=1 early in main()
before the preflight check runs. The user already said "yes I accept"
non-interactively; assuming they also want the rest of the install
non-interactive matches the curl|bash use case the flag was named for.

Tighten the preflight to no longer skip on the acceptance flag alone
(it's now redundant since acceptance implies NON_INTERACTIVE=1).

Also harden the bypass tests: assert phases !== "" in addition to
absence of the TTY error message, so the tests can't false-pass if
the install bailed for some other reason.

- Re-ran installer-integration: 71/71 pass (4.5s atomicity tests).

Signed-off-by: latenighthackathon <latenighthackathon@users.noreply.github.com>
@latenighthackathon latenighthackathon force-pushed the fix/install-license-atomic branch from af55822 to 0464769 Compare May 2, 2026 05:51

@ericksoa ericksoa 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 against issue #2671 and duplicate PR #2694. This is the right fix shape: the license/TTY preflight stays shell-native and runs before Phase 1/2, explicit acceptance is normalized into non-interactive intent, and the added install-preflight coverage proves the no-flag curl-pipe path exits before Node/CLI while accepted and non-interactive paths still clear the preflight. CodeRabbit's earlier findings are addressed on the current head.

@ericksoa

ericksoa commented May 4, 2026

Copy link
Copy Markdown
Contributor

/ok to test 0464769

@ericksoa ericksoa enabled auto-merge (squash) May 4, 2026 18:24
@ericksoa ericksoa merged commit 3cdaf53 into NVIDIA:main May 4, 2026
10 checks passed
miyoungc added a commit that referenced this pull request May 5, 2026
## Summary
Catch up the docs for user-facing changes that landed over the weekend
and today, so the published guidance matches current installer,
onboarding, status, logs, local inference, rebuild backup behavior, the
next docs version selector, and refreshed generated user skills.

## Related Issue
None.

## Changes
- Document WSL Windows-host Ollama onboarding actions, including use,
start, restart, install, and `host.docker.internal` model pulls from
#2800; clarify that onboard owns Ollama install and model pulls from
#2952.
- Document installer fail-fast behavior for non-TTY third-party software
acceptance from #2706.
- Update sandbox-name guidance and Brev deploy validation behavior from
#2948.
- Add `nemoclaw <name> logs --tail/--since` coverage from #2825.
- Add global `nemoclaw status --json` coverage from #2822.
- Document verified-gateway status behavior and non-zero degraded exits
from #2884.
- Clarify that rebuild stops before deleting the original sandbox when
backup fails, including unreadable or root-owned state paths.
- Bump docs switcher metadata from 0.0.33 to 0.0.34 without changing
package versions or creating release tags.
- Regenerate `.agents/skills/nemoclaw-user-*` from docs, including the
new `nemoclaw-user-manage-sandboxes` generated skill and removal of the
stale `nemoclaw-user-workspace` output.

## Type of Change
- [ ] Code change (feature, bug fix, or refactor)
- [ ] Code change with doc updates
- [ ] Doc only (prose changes, no code sample modifications)
- [x] Doc only (includes code sample changes)

## Verification
- [ ] `npx prek run --all-files` passes
- [ ] `npm test` passes
- [ ] Tests added or updated for new or changed behavior
- [x] No secrets, API keys, or credentials committed
- [x] Docs updated for user-facing behavior changes
- [x] `make docs` builds without warnings (doc changes only)
- [x] Doc pages follow the [style
guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md)
(doc changes only)
- [ ] New doc pages include SPDX header and frontmatter (new pages only)

---
<!-- DCO sign-off required by CI. Run: git config user.name && git
config user.email -->
Signed-off-by: Miyoung Choi <miyoungc@nvidia.com>

Made with [Cursor](https://cursor.com)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Documentation**
* Expanded Ollama/local inference guidance with detailed WSL and
Windows-host workflows, proxy/token behavior, and onboarding options
* Standardized sandbox name validation and updated troubleshooting, CLI,
and deploy docs to surface the rules and validation timing
* Added/rewrote Manage Sandboxes, policy management, backup/restore,
messaging channels, workspace persistence, and CLI selection guides
* Refreshed quickstart/Hermes guidance, skill mappings, and bumped docs
version to 0.0.34
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
@latenighthackathon latenighthackathon deleted the fix/install-license-atomic branch May 18, 2026 05:10
@wscurran wscurran added area: cli Command line interface, flags, terminal UX, or output bug-fix PR fixes a bug or regression and removed NemoClaw CLI bug Something fails against expected or documented behavior labels Jun 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: cli Command line interface, flags, terminal UX, or output bug-fix PR fixes a bug or regression

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[DGX Spark][Install] curl|bash install without license flag leaves partial install state — nemoclaw binary on PATH but no usage-notice.json

3 participants