fix(cli): report masked configuration overrides (#2547)

A whole-file rig config reset now names the keys whose effective value still comes from an environment variable, and keeps the existing message when there are none. If reading the settings back after the reset fails, the reset is still reported as successful (exit 0) and the read-back problem is noted on stderr. Single-key reset is unchanged. Contributed by dajiaohuang; reported in #2528.
This commit is contained in:
Wu Shuwen authored and GitHub committed 2026-11-07 02:11:39 -08:00
1 parent 73638fcddd
commit 00cb932c0f
2 files changed
+81 -9

No files matched your search

+24 -4
View File
@@ -200,10 +200,17 @@ Precedence: CLI flag > environment variable > config file > default`)
.action(async (key: string, value: string) => {
try {
store.set(key, value);
console.log(`${key} = ${store.get(key)}`);
const resolved = store.resolveWithSource(key);
console.log(resolved.source === "env"
? `${key} = ${resolved.value} (source: env; file override saved but masked)`
: resolved.source === "default"
? `${key} file override saved; effective value ${resolved.value} (source: default)`
: `${key} = ${resolved.value}`);
// SWEEP-c: boot-only key + running daemon = stale-until-restart; say so loudly.
if (isBootOnlyKey(key) && (await probeDaemonRunning())) {
console.error(`note: '${key}' is read at daemon BOOT — the running daemon keeps its current value; this takes effect on the next daemon restart (rig daemon stop && rig daemon start).`);
console.error(resolved.source === "env"
? `note: '${key}' is read at daemon BOOT — the running daemon keeps its current value; the saved value remains masked after restart while the environment override is set.`
: `note: '${key}' is read at daemon BOOT — the running daemon keeps its current value; this takes effect on the next daemon restart (rig daemon stop && rig daemon start).`);
if (key === "terminal.tmux_server") console.error("note: surviving sessions keep their previous server until deliberately stopped; rig terminal status shows the effective selection and any pending migration.");
}
} catch (err) {
@@ -219,9 +226,22 @@ Precedence: CLI flag > environment variable > config file > default`)
try {
store.reset(key);
if (key) {
console.log(`${key} reset to default (${store.get(key)}).`);
const resolved = store.resolveWithSource(key);
console.log(resolved.source === "env"
? `${key} file override cleared; effective value ${resolved.value} (source: env; default: ${resolved.defaultValue}).`
: `${key} reset to default (${resolved.value}).`);
} else {
console.log("Config reset to defaults.");
let envKeys: string[] = [];
try {
envKeys = Object.entries(store.resolveAllWithSource())
.filter(([, resolved]) => resolved.source === "env")
.map(([name]) => name);
} catch (err) {
console.error(`note: could not read effective configuration after reset: ${(err as Error).message}`);
}
console.log(envKeys.length
? `Config reset to defaults, except where the environment overrides them: ${envKeys.join(", ")}.`
: "Config reset to defaults.");
}
} catch (err) {
console.error((err as Error).message);
@@ -5,10 +5,10 @@ import * as fs from "node:fs";
import * as os from "node:os";
import * as path from "node:path";
import { configCommand } from "../src/commands/config.js";
import { ENV_MAP } from "../src/config-store.js";
let dir: string;
let configPath: string;
let savedPort: string | undefined;
async function run(args: string[]): Promise<string> {
const logSpy = vi.spyOn(console, "log").mockImplementation(() => {});
@@ -25,13 +25,14 @@ describe("rig config get --with-source", () => {
beforeEach(() => {
dir = fs.mkdtempSync(path.join(os.tmpdir(), "config-get-with-source-"));
configPath = path.join(dir, "config.json");
savedPort = process.env["OPENRIG_PORT"];
delete process.env["OPENRIG_PORT"];
for (const { primary, legacy } of Object.values(ENV_MAP)) {
vi.stubEnv(primary, undefined);
if (legacy) vi.stubEnv(legacy, undefined);
}
});
afterEach(() => {
if (savedPort === undefined) delete process.env["OPENRIG_PORT"];
else process.env["OPENRIG_PORT"] = savedPort;
vi.unstubAllEnvs();
fs.rmSync(dir, { recursive: true, force: true });
});
@@ -72,4 +73,55 @@ describe("rig config get --with-source", () => {
const json = JSON.parse(await run(["get", "daemon.port", "--json"]));
expect(json).toMatchObject({ value: 7555, source: "file" });
});
it("reports the active environment override when setting or resetting a file value", async () => {
process.env["OPENRIG_PORT"] = "7666";
expect(await run(["set", "daemon.port", "7555"])).toContain("7666 (source: env; file override saved but masked)");
expect(JSON.parse(fs.readFileSync(configPath, "utf8")).daemon.port).toBe(7555);
expect(await run(["reset", "daemon.port"])).toContain("effective value 7666 (source: env; default: 7433)");
expect(await run(["reset"])).toContain("environment overrides them: daemon.port.");
});
it("retains the default reset message when no environment override remains", async () => {
expect(await run(["reset"])).toBe("Config reset to defaults.");
});
it("reports a successful reset when reading effective configuration fails", async () => {
fs.writeFileSync(configPath, JSON.stringify({ daemon: { port: 7555 } }));
vi.stubEnv("OPENRIG_CONTEXT_PACKS_ROOT", "/old-context");
const previousExitCode = process.exitCode;
process.exitCode = 0;
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
try {
expect(await run(["reset"])).toBe("Config reset to defaults.");
expect(fs.existsSync(configPath)).toBe(false);
expect(errorSpy.mock.calls.flat().join(" ")).toContain("OPENRIG_CONTEXT_PACKS_ROOT was removed");
expect(process.exitCode).toBe(0);
} finally {
process.exitCode = previousExitCode;
errorSpy.mockRestore();
}
});
it("does not promise a masked boot setting will take effect on restart", async () => {
process.env["OPENRIG_PORT"] = "7666";
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
try {
await configCommand(configPath, { probeDaemonRunning: async () => true })
.parseAsync(["node", "rig", "set", "daemon.port", "7555"]);
expect(errorSpy.mock.calls.flat().join(" ")).toContain("remains masked after restart");
} finally { errorSpy.mockRestore(); }
});
it("distinguishes the saved threshold from the default used after an invalid environment pair", async () => {
vi.stubEnv("OPENRIG_HEALTH_CONTEXT_PRESSURE_WARNING_PERCENT", "98");
vi.stubEnv("OPENRIG_HEALTH_CONTEXT_PRESSURE_CRITICAL_PERCENT", undefined);
const stderr = vi.spyOn(process.stderr, "write").mockImplementation(() => true);
try {
expect(await run(["set", "health.context_pressure.critical_percent", "97"]))
.toContain("file override saved; effective value 99 (source: default)");
expect(JSON.parse(fs.readFileSync(configPath, "utf8")).health.contextPressure.criticalPercent).toBe(97);
expect(stderr).toHaveBeenCalled();
} finally { stderr.mockRestore(); }
});
});