diff --git a/src/lib/debug.test.ts b/src/lib/debug.test.ts index 7aa76d19596..f2d16d91407 100644 --- a/src/lib/debug.test.ts +++ b/src/lib/debug.test.ts @@ -1,9 +1,12 @@ // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { describe, it, expect } from "vitest"; +import { describe, it, expect, afterEach, beforeEach } from "vitest"; // Import from compiled dist/ so coverage is attributed correctly. -import { redact } from "../../dist/lib/debug"; +import { redact, createTarball } from "../../dist/lib/debug"; +import { mkdtempSync, rmSync, writeFileSync, existsSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; describe("redact", () => { it("redacts NVIDIA_API_KEY=value patterns", () => { @@ -50,3 +53,34 @@ describe("redact", () => { expect(redact(clean)).toBe(clean); }); }); + +describe("createTarball", () => { + let tempDir: string; + + beforeEach(() => { + process.exitCode = undefined; + }); + + afterEach(() => { + if (tempDir) rmSync(tempDir, { recursive: true, force: true }); + process.exitCode = undefined; + }); + + it("sets process.exitCode = 1 and returns false when tar fails on invalid output path", () => { + tempDir = mkdtempSync(join(tmpdir(), "debug-test-")); + writeFileSync(join(tempDir, "dummy.txt"), "test data"); + const ok = createTarball(tempDir, "/nonexistent/path/debug.tar.gz"); + expect(ok).toBe(false); + expect(process.exitCode).toBe(1); + }); + + it("creates tarball successfully and returns true for valid output path", () => { + tempDir = mkdtempSync(join(tmpdir(), "debug-test-")); + writeFileSync(join(tempDir, "dummy.txt"), "test data"); + const output = join(tempDir, "output.tar.gz"); + const ok = createTarball(tempDir, output); + expect(ok).toBe(true); + expect(process.exitCode).toBeUndefined(); + expect(existsSync(output)).toBe(true); + }); +}); diff --git a/src/lib/debug.ts b/src/lib/debug.ts index aa898cf443f..b8ae3b0bacb 100644 --- a/src/lib/debug.ts +++ b/src/lib/debug.ts @@ -29,6 +29,7 @@ export interface DebugOptions { const useColor = !process.env.NO_COLOR && process.stdout.isTTY; const GREEN = useColor ? "\x1b[0;32m" : ""; const YELLOW = useColor ? "\x1b[1;33m" : ""; +const RED = useColor ? "\x1b[0;31m" : ""; const CYAN = useColor ? "\x1b[0;36m" : ""; const NC = useColor ? "\x1b[0m" : ""; @@ -40,6 +41,10 @@ function warn(msg: string): void { console.log(`${YELLOW}[debug]${NC} ${msg}`); } +function error(msg: string): void { + console.error(`${RED}[debug]${NC} ${msg}`); +} + function section(title: string): void { console.log(`\n${CYAN}═══ ${title} ═══${NC}\n`); } @@ -446,16 +451,25 @@ function collectKernelMessages(collectDir: string): void { // Tarball // --------------------------------------------------------------------------- -function createTarball(collectDir: string, output: string): void { - spawnSync("tar", ["czf", output, "-C", dirname(collectDir), basename(collectDir)], { +export function createTarball(collectDir: string, output: string): boolean { + const result = spawnSync("tar", ["czf", output, "-C", dirname(collectDir), basename(collectDir)], { stdio: "inherit", timeout: 60_000, }); + if (result.status !== 0 || result.signal) { + const reason = result.signal + ? `killed by signal ${result.signal}` + : `exited with code ${result.status ?? "unknown"}`; + error(`Failed to create tarball at ${output} (tar ${reason})`); + process.exitCode = 1; + return false; + } info(`Tarball written to ${output}`); warn( "Known secrets are auto-redacted, but please review for any remaining sensitive data before sharing.", ); info("Attach this file to your GitHub issue."); + return true; } // --------------------------------------------------------------------------- @@ -499,13 +513,16 @@ export function runDebug(opts: DebugOptions = {}): void { collectKernelMessages(collectDir); + let tarballOk = true; if (output) { - createTarball(collectDir, output); + tarballOk = createTarball(collectDir, output); } - console.log(""); - info("Done. If filing a bug, run with --output and attach the tarball to your issue:"); - info(" nemoclaw debug --output /tmp/nemoclaw-debug.tar.gz"); + if (tarballOk) { + console.log(""); + info("Done. If filing a bug, run with --output and attach the tarball to your issue:"); + info(" nemoclaw debug --output /tmp/nemoclaw-debug.tar.gz"); + } } finally { rmSync(collectDir, { recursive: true, force: true }); }