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
38 changes: 36 additions & 2 deletions src/lib/debug.test.ts
Original file line number Diff line number Diff line change
@@ -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", () => {
Expand Down Expand Up @@ -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);
});
});
29 changes: 23 additions & 6 deletions src/lib/debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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" : "";

Expand All @@ -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`);
}
Expand Down Expand Up @@ -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;
}

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -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 });
}
Expand Down
Loading