Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions scripts/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3088,13 +3088,19 @@ fail_force_station_terminal_required() {
}

validate_force_station_install_override() {
local platform="$1"
local platform="$1" release_state
if [ "${FORCE_STATION_INSTALL:-}" != "1" ]; then
return 0
fi
if [ "$platform" != "DGX Station" ]; then
error "--force-station-install requires DGX Station GB300 hardware (detected: ${platform:-unsupported platform})."
fi
release_state="$(classify_dgx_station_release)"
case "$release_state" in
generic-ubuntu | supported-dgx-os | supported-colossus-baseos | supported-ai-developer-tools)
error "--force-station-install is only for unrecognized DGX Station release metadata. This host is already supported (${release_state}); omit --force-station-install."
;;
esac
if [ "${NEMOCLAW_NO_EXPRESS:-}" = "1" ]; then
error "--force-station-install cannot be combined with NEMOCLAW_NO_EXPRESS=1. Remove one override."
fi
Expand Down Expand Up @@ -3689,7 +3695,9 @@ maybe_offer_express_install() {
describe_express_install "$platform"
printf " Run express install with these settings? [Y/n]: "
if ! IFS= read -r reply; then
if [ "${STATION_DEEPSEEK:-}" = "1" ]; then
if [ "${FORCE_STATION_INSTALL:-}" = "1" ]; then
fail_force_station_terminal_required
elif [ "${STATION_DEEPSEEK:-}" = "1" ]; then
fail_station_deepseek_terminal_required
fi
info "Skipping express install (unable to read from TTY)."
Expand All @@ -3701,7 +3709,9 @@ maybe_offer_express_install() {
printf " Run express install with these settings? [Y/n]: "
if ! IFS= read -r reply <&3; then
exec 3<&-
if [ "${STATION_DEEPSEEK:-}" = "1" ]; then
if [ "${FORCE_STATION_INSTALL:-}" = "1" ]; then
fail_force_station_terminal_required
elif [ "${STATION_DEEPSEEK:-}" = "1" ]; then
fail_station_deepseek_terminal_required
fi
info "Skipping express install (unable to read from TTY)."
Expand Down
9 changes: 9 additions & 0 deletions scripts/prepare-dgx-station-host.sh
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,13 @@ check_platform() {
is_station_gb300_product "$product" || fatal "Expected DGX Station GB300 DMI, found ${product}"
release_path="$(dgx_station_release_path)"
release_state="$(dgx_station_release_state "$release_path")"
if ((FORCE_STATION_INSTALL == 1)); then
case "$release_state" in
generic-ubuntu | supported-dgx-os | supported-colossus-baseos | supported-ai-developer-tools)
fatal "--force-station-install is only for unrecognized DGX Station release metadata. This host is already supported (${release_state}); omit --force-station-install."
;;
esac
fi
case "$release_state" in
generic-ubuntu)
station_has_exact_gb300_pci_gpu "$(station_pci_devices_path)" \
Expand All @@ -642,6 +649,8 @@ check_platform() {
supported-ai-developer-tools) STATION_HOST_PROFILE="ai-developer-tools" ;;
*)
if ((FORCE_STATION_INSTALL == 1)); then
station_has_exact_gb300_pci_gpu "$(station_pci_devices_path)" \
|| fatal "Expected an NVIDIA GB300 PCI GPU (${GB300_PCI_VENDOR#0x}:${GB300_PCI_DEVICE#0x}) before forced factory-runtime validation"
STATION_HOST_PROFILE="forced-factory-runtime"
warn "DGX release metadata allowlist bypassed by explicit --force-station-install intent; all hardware and factory-runtime health checks remain required"
else
Expand Down
32 changes: 31 additions & 1 deletion test/install-express-prompt.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ else:
script = r'''
source "$INSTALLER_UNDER_TEST" >/dev/null
detect_express_platform() { printf "$EXPRESS_PLATFORM"; }
classify_dgx_station_release() { printf "%s" "\${EXPRESS_RELEASE_STATE:-generic-ubuntu}"; }
NON_INTERACTIVE="\${NON_INTERACTIVE:-}"
NEMOCLAW_PROVIDER="\${NEMOCLAW_PROVIDER:-}"
NEMOCLAW_NO_EXPRESS="\${NEMOCLAW_NO_EXPRESS:-}"
Expand Down Expand Up @@ -519,10 +520,24 @@ ensure_station_express_host`,
name: "a forced Station install in non-interactive mode",
args: ["--force-station-install", "--non-interactive"],
platform: "DGX Station",
env: {},
env: { EXPRESS_RELEASE_STATE: "unsupported-dgx-os" },
message:
/--force-station-install selects the DGX Station express prompt and cannot be combined with non-interactive mode \(triggered by: the --non-interactive flag\)/,
},
...[
"generic-ubuntu",
"supported-dgx-os",
"supported-colossus-baseos",
"supported-ai-developer-tools",
].map((releaseState) => ({
name: `an unnecessary forced Station install on ${releaseState}`,
args: ["--force-station-install"],
platform: "DGX Station",
env: { EXPRESS_RELEASE_STATE: releaseState },
message: new RegExp(
`This host is already supported \\(${releaseState}\\); omit --force-station-install`,
),
})),
{
name: "a Station-only flag on DGX Spark",
args: ["--station-deepseek"],
Expand Down Expand Up @@ -570,6 +585,7 @@ ensure_station_express_host`,
`
source "$INSTALLER_UNDER_TEST" >/dev/null
detect_express_platform() { printf "%s" "$EXPRESS_PLATFORM"; }
classify_dgx_station_release() { printf "%s" "\${EXPRESS_RELEASE_STATE:-generic-ubuntu}"; }
ensure_docker() { printf "ensure_docker\\n" >>"$MUTATION_LOG"; }
ensure_openshell_build_deps() { printf "ensure_openshell_build_deps\\n" >>"$MUTATION_LOG"; }
main "$@"
Expand Down Expand Up @@ -762,6 +778,20 @@ sys.exit(result.returncode)
expect(output).not.toMatch(/RESULT NON_INTERACTIVE=/);
});

it("fails closed if the forced Station prompt becomes unreadable after preflight (#7138)", () => {
const result = runExpressPromptWithTty("", "tty", "DGX Station", {
EXPRESS_RELEASE_STATE: "unsupported-dgx-os",
FORCE_EXPRESS_PROMPT_READ_FAILURE: "1",
FORCE_STATION_INSTALL: "1",
});
const output = `${result.stdout}${result.stderr}`;
expect(result.error, output).toBeUndefined();
expect(result.status, output).not.toBe(0);
expect(output).toMatch(/--force-station-install.*needs an interactive terminal/);
expect(output).not.toMatch(/Skipping express install/);
expect(output).not.toMatch(/RESULT NON_INTERACTIVE=/);
});

it.each([
["Unsupported DGX Station OS", { NEMOCLAW_NO_EXPRESS: "1" }],
["Unsupported DGX Station generation", { NEMOCLAW_PROVIDER: "openai" }],
Expand Down
52 changes: 52 additions & 0 deletions test/install-station-dgx-os.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@ station_os_release_path() { printf '%s' "$HOME/os-release"; }
station_product_name_path() { printf '%s' "$HOME/product-name"; }
dgx_station_release_path() { printf '%s' "$HOME/dgx-release"; }
dgx_station_release_state() { printf 'unsupported-dgx-os'; }
station_has_exact_gb300_pci_gpu() { return 0; }
FORCE_STATION_INSTALL=1
check_platform
printf 'PROFILE=%s\n' "$STATION_HOST_PROFILE"
Expand Down Expand Up @@ -431,6 +432,57 @@ check_platform
expect(unforced.output).toContain("outside the validated boundary");
});

it.each([
"generic-ubuntu",
"supported-dgx-os",
"supported-colossus-baseos",
"supported-ai-developer-tools",
])("rejects explicit metadata intent for recognized %s before preparation (#7138)", (releaseState) => {
const { result, output } = runSourced(
STATION_PREPARE,
`
printf 'ID=ubuntu\nVERSION_ID="24.04"\nPRETTY_NAME="Ubuntu 24.04"\n' >"$HOME/os-release"
printf 'NVIDIA DGX Station GB300\n' >"$HOME/product-name"
uname() { printf 'aarch64\n'; }
station_os_release_path() { printf '%s' "$HOME/os-release"; }
station_product_name_path() { printf '%s' "$HOME/product-name"; }
dgx_station_release_path() { printf '%s' "$HOME/dgx-release"; }
dgx_station_release_state() { printf '%s' "$RELEASE_STATE"; }
FORCE_STATION_INSTALL=1
check_platform
printf 'PREPARATION_REACHED\n'
`,
{ RELEASE_STATE: releaseState },
);

expect(result.status, output).not.toBe(0);
expect(output).toContain(
`This host is already supported (${releaseState}); omit --force-station-install`,
);
expect(output).not.toContain("PREPARATION_REACHED");
});

it("keeps generic Ubuntu preparation unchanged without explicit metadata intent (#7138)", () => {
const { result, output } = runSourced(
STATION_PREPARE,
`
printf 'ID=ubuntu\nVERSION_ID="24.04"\nPRETTY_NAME="Ubuntu 24.04"\n' >"$HOME/os-release"
printf 'NVIDIA DGX Station GB300\n' >"$HOME/product-name"
uname() { printf 'aarch64\n'; }
station_os_release_path() { printf '%s' "$HOME/os-release"; }
station_product_name_path() { printf '%s' "$HOME/product-name"; }
dgx_station_release_path() { printf '%s' "$HOME/dgx-release"; }
dgx_station_release_state() { printf 'generic-ubuntu'; }
station_has_exact_gb300_pci_gpu() { return 0; }
check_platform
printf 'PROFILE=%s\n' "$STATION_HOST_PROFILE"
`,
);

expect(result.status, output).toBe(0);
expect(output).toContain("PROFILE=generic-ubuntu");
});

it("parses the metadata override only alongside a preparation mode", () => {
const accepted = runSourced(
STATION_PREPARE,
Expand Down
46 changes: 46 additions & 0 deletions test/install-station-platform-identity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,50 @@ run_apply
expect(output).toContain("Expected an NVIDIA GB300 PCI GPU (10de:31c2)");
expect(output).not.toContain("UNEXPECTED_MUTATION");
});

it("rejects forced metadata intent without the exact GB300 PCI identity before mutation (#7138)", () => {
const fixtureRoot = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-station-platform-"));
const osReleasePath = path.join(fixtureRoot, "os-release");
const productNamePath = path.join(fixtureRoot, "product_name");
const dgxReleasePath = path.join(fixtureRoot, "dgx-release");
const pciRoot = writePciIdentityFixture("0x1234");
fs.writeFileSync(
osReleasePath,
'ID=ubuntu\nVERSION_ID="24.04"\nPRETTY_NAME="Ubuntu 24.04.4 LTS"\n',
);
fs.writeFileSync(productNamePath, "DGX Station GB300\n");
fs.writeFileSync(dgxReleasePath, 'DGX_PRETTY_NAME="Unrecognized Station"\n');

const { result, output } = runStationPrepare(
`
station_os_release_path() { printf '%s' "$OS_RELEASE_PATH"; }
station_product_name_path() { printf '%s' "$PRODUCT_NAME_PATH"; }
station_pci_devices_path() { printf '%s' "$PCI_ROOT"; }
dgx_station_release_path() { printf '%s' "$DGX_RELEASE_PATH"; }
dgx_station_release_state() { printf 'unsupported-dgx-os'; }
uname() {
case "$*" in
-m) printf 'aarch64' ;;
-r) printf 'test-kernel' ;;
*) return 1 ;;
esac
}
require_command() { :; }
acquire_sudo() { :; }
install_packages() { printf 'UNEXPECTED_MUTATION\n'; }
FORCE_STATION_INSTALL=1
run_apply
`,
{
OS_RELEASE_PATH: osReleasePath,
PRODUCT_NAME_PATH: productNamePath,
PCI_ROOT: pciRoot,
DGX_RELEASE_PATH: dgxReleasePath,
},
);

expect(result.status, output).not.toBe(0);
expect(output).toContain("Expected an NVIDIA GB300 PCI GPU (10de:31c2)");
expect(output).not.toContain("UNEXPECTED_MUTATION");
});
});
Loading