From 1a1d7aa45e2939f86672f574515f95c0699d1581 Mon Sep 17 00:00:00 2001 From: moguangyu5-design Date: Sun, 12 Jul 2026 07:44:25 +0800 Subject: [PATCH] fix(scripts): make clawlog default behavior match documented usage (#104059) * fix(scripts): make clawlog default behavior match documented usage Running scripts/clawlog.sh with no arguments was printing the usage help instead of the documented default behavior (last 50 lines from the past 5 minutes). Remove the early no-args help branch so the defaults are used. Also validate that options requiring a value (-n, -l, -c, -s, -o) actually receive one, preventing an unbound-variable abort under set -u. Fixes #104058 * fix(scripts): preserve dash-prefixed operands in clawlog and add regression tests ClawSweeper review feedback on #104059 noted that the missing-value guards introduced in the previous commit also rejected valid operands beginning with a dash (e.g., search text '-failed' or an output file named '-debug.log'). Narrow the guards so they only reject genuinely missing operands, not dash-prefixed values. Add focused regression coverage in test/scripts/clawlog.test.ts for: - no-argument default behavior - missing values for -n/-l/-c/-s/-o - acceptance of dash-prefixed operands - --help still printing usage Related: #104058 * test(scripts): isolate clawlog command execution * test(clawlog): use managed temp directories --------- Co-authored-by: moguangyu5-design Co-authored-by: Vincent Koc --- scripts/clawlog.sh | 26 +++++++++--- test/scripts/clawlog.test.ts | 76 ++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 6 deletions(-) create mode 100644 test/scripts/clawlog.test.ts diff --git a/scripts/clawlog.sh b/scripts/clawlog.sh index b856d0eec6ec..d3395fde6aae 100755 --- a/scripts/clawlog.sh +++ b/scripts/clawlog.sh @@ -146,12 +146,6 @@ escape_predicate_literal() { printf '%s' "$value" } -# Show help if no arguments provided -if [[ $# -eq 0 ]]; then - show_usage - exit 0 -fi - # Parse command line arguments while [[ $# -gt 0 ]]; do case $1 in @@ -165,14 +159,26 @@ while [[ $# -gt 0 ]]; do shift ;; -n|--lines) + if [[ $# -lt 2 ]]; then + echo -e "${RED}Error: $1 requires a value${NC}" >&2 + exit 1 + fi TAIL_LINES="$2" shift 2 ;; -l|--last) + if [[ $# -lt 2 ]]; then + echo -e "${RED}Error: $1 requires a value${NC}" >&2 + exit 1 + fi TIME_RANGE="$2" shift 2 ;; -c|--category) + if [[ $# -lt 2 ]]; then + echo -e "${RED}Error: $1 requires a value${NC}" >&2 + exit 1 + fi CATEGORY="$2" shift 2 ;; @@ -185,10 +191,18 @@ while [[ $# -gt 0 ]]; do shift ;; -s|--search) + if [[ $# -lt 2 ]]; then + echo -e "${RED}Error: $1 requires a value${NC}" >&2 + exit 1 + fi SEARCH_TEXT="$2" shift 2 ;; -o|--output) + if [[ $# -lt 2 ]]; then + echo -e "${RED}Error: $1 requires a value${NC}" >&2 + exit 1 + fi OUTPUT_FILE="$2" shift 2 ;; diff --git a/test/scripts/clawlog.test.ts b/test/scripts/clawlog.test.ts new file mode 100644 index 000000000000..7bde5ae389af --- /dev/null +++ b/test/scripts/clawlog.test.ts @@ -0,0 +1,76 @@ +// Clawlog tests cover argument parsing contracts in the macOS logging helper. +// These tests do not require a real macOS log(1) binary; they verify that the +// script reaches the expected code paths before any platform-specific command. +import { spawnSync } from "node:child_process"; +import { chmodSync, mkdirSync, writeFileSync } from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { afterEach, describe, expect, it } from "vitest"; +import { useAutoCleanupTempDirTracker } from "../helpers/temp-dir.js"; + +const SCRIPT_PATH = fileURLToPath(new URL("../../scripts/clawlog.sh", import.meta.url)); +const tempDirs = useAutoCleanupTempDirTracker(afterEach); + +function runClawlog(args: string[] = []) { + const cwd = tempDirs.make("openclaw-clawlog-test-"); + const binDir = path.join(cwd, "bin"); + mkdirSync(binDir); + const sudoPath = path.join(binDir, "sudo"); + writeFileSync(sudoPath, "#!/bin/sh\nexit 0\n"); + chmodSync(sudoPath, 0o755); + + return spawnSync("bash", [SCRIPT_PATH, ...args], { + cwd, + encoding: "utf8", + env: { + ...process.env, + PATH: `${binDir}${path.delimiter}${process.env.PATH ?? ""}`, + }, + }); +} + +describe("clawlog.sh argument parsing", () => { + it("uses the documented default view when run without arguments", () => { + const result = runClawlog(); + const output = result.stdout + result.stderr; + + // Should reach the default log-view path, not print usage. + expect(output).toContain("Showing last 50 log lines from the past 5m"); + expect(output).not.toContain("USAGE:"); + }); + + it("still prints usage for --help", () => { + const result = runClawlog(["--help"]); + + expect(result.status).toBe(0); + expect(result.stdout + result.stderr).toContain("USAGE:"); + }); + + const valueOptions = ["-n", "-l", "-c", "-s", "-o"]; + for (const option of valueOptions) { + it(`reports a clear error when ${option} is missing a value`, () => { + const result = runClawlog([option]); + + expect(result.status).toBe(1); + expect(result.stderr).toContain(`Error: ${option} requires a value`); + }); + } + + it("accepts dash-prefixed search text", () => { + const result = runClawlog(["-s", "-failed"]); + + expect(result.stderr).not.toContain("requires a value"); + }); + + it("accepts dash-prefixed category", () => { + const result = runClawlog(["-c", "-ServerManager"]); + + expect(result.stderr).not.toContain("requires a value"); + }); + + it("accepts dash-prefixed output path", () => { + const result = runClawlog(["-o", "-debug.log"]); + + expect(result.stderr).not.toContain("requires a value"); + }); +});