CLI: resolve parent/subcommand option collisions (#18725)
Merged via /review-pr -> /prepare-pr -> /merge-pr. Prepared head SHA: b7e51cf90950cdd3049ac3c7a3a949717b8ba261 Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com> Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com> Reviewed-by: @gumadeiras
This commit is contained in:
committed by
GitHub
parent
fa4f66255c
commit
985ec71c55
72
src/cli/daemon-cli/register-service-commands.test.ts
Normal file
72
src/cli/daemon-cli/register-service-commands.test.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { Command } from "commander";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { addGatewayServiceCommands } from "./register-service-commands.js";
|
||||
|
||||
const runDaemonInstall = vi.fn(async () => {});
|
||||
const runDaemonRestart = vi.fn(async () => {});
|
||||
const runDaemonStart = vi.fn(async () => {});
|
||||
const runDaemonStatus = vi.fn(async () => {});
|
||||
const runDaemonStop = vi.fn(async () => {});
|
||||
const runDaemonUninstall = vi.fn(async () => {});
|
||||
|
||||
vi.mock("./runners.js", () => ({
|
||||
runDaemonInstall: (opts: unknown) => runDaemonInstall(opts),
|
||||
runDaemonRestart: (opts: unknown) => runDaemonRestart(opts),
|
||||
runDaemonStart: (opts: unknown) => runDaemonStart(opts),
|
||||
runDaemonStatus: (opts: unknown) => runDaemonStatus(opts),
|
||||
runDaemonStop: (opts: unknown) => runDaemonStop(opts),
|
||||
runDaemonUninstall: (opts: unknown) => runDaemonUninstall(opts),
|
||||
}));
|
||||
|
||||
function createGatewayParentLikeCommand() {
|
||||
const gateway = new Command().name("gateway");
|
||||
// Mirror overlapping root gateway options that conflict with service subcommand options.
|
||||
gateway.option("--port <port>", "Port for the gateway WebSocket");
|
||||
gateway.option("--token <token>", "Gateway token");
|
||||
gateway.option("--password <password>", "Gateway password");
|
||||
gateway.option("--force", "Gateway run --force", false);
|
||||
addGatewayServiceCommands(gateway);
|
||||
return gateway;
|
||||
}
|
||||
|
||||
describe("addGatewayServiceCommands", () => {
|
||||
beforeEach(() => {
|
||||
runDaemonInstall.mockClear();
|
||||
runDaemonRestart.mockClear();
|
||||
runDaemonStart.mockClear();
|
||||
runDaemonStatus.mockClear();
|
||||
runDaemonStop.mockClear();
|
||||
runDaemonUninstall.mockClear();
|
||||
});
|
||||
|
||||
it("forwards install option collisions from parent gateway command", async () => {
|
||||
const gateway = createGatewayParentLikeCommand();
|
||||
await gateway.parseAsync(["install", "--force", "--port", "19000", "--token", "tok_test"], {
|
||||
from: "user",
|
||||
});
|
||||
|
||||
expect(runDaemonInstall).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
force: true,
|
||||
port: "19000",
|
||||
token: "tok_test",
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("forwards status auth collisions from parent gateway command", async () => {
|
||||
const gateway = createGatewayParentLikeCommand();
|
||||
await gateway.parseAsync(["status", "--token", "tok_status", "--password", "pw_status"], {
|
||||
from: "user",
|
||||
});
|
||||
|
||||
expect(runDaemonStatus).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
rpc: expect.objectContaining({
|
||||
token: "tok_status",
|
||||
password: "pw_status",
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,6 @@
|
||||
import type { Command } from "commander";
|
||||
import type { DaemonInstallOptions, GatewayRpcOpts } from "./types.js";
|
||||
import { inheritOptionFromParent } from "../command-options.js";
|
||||
import {
|
||||
runDaemonInstall,
|
||||
runDaemonRestart,
|
||||
@@ -8,6 +10,31 @@ import {
|
||||
runDaemonUninstall,
|
||||
} from "./runners.js";
|
||||
|
||||
function resolveInstallOptions(
|
||||
cmdOpts: DaemonInstallOptions,
|
||||
command?: Command,
|
||||
): DaemonInstallOptions {
|
||||
const parentForce = inheritOptionFromParent<boolean>(command, "force");
|
||||
const parentPort = inheritOptionFromParent<string>(command, "port");
|
||||
const parentToken = inheritOptionFromParent<string>(command, "token");
|
||||
return {
|
||||
...cmdOpts,
|
||||
force: Boolean(cmdOpts.force || parentForce),
|
||||
port: cmdOpts.port ?? parentPort,
|
||||
token: cmdOpts.token ?? parentToken,
|
||||
};
|
||||
}
|
||||
|
||||
function resolveRpcOptions(cmdOpts: GatewayRpcOpts, command?: Command): GatewayRpcOpts {
|
||||
const parentToken = inheritOptionFromParent<string>(command, "token");
|
||||
const parentPassword = inheritOptionFromParent<string>(command, "password");
|
||||
return {
|
||||
...cmdOpts,
|
||||
token: cmdOpts.token ?? parentToken,
|
||||
password: cmdOpts.password ?? parentPassword,
|
||||
};
|
||||
}
|
||||
|
||||
export function addGatewayServiceCommands(parent: Command, opts?: { statusDescription?: string }) {
|
||||
parent
|
||||
.command("status")
|
||||
@@ -19,9 +46,9 @@ export function addGatewayServiceCommands(parent: Command, opts?: { statusDescri
|
||||
.option("--no-probe", "Skip RPC probe")
|
||||
.option("--deep", "Scan system-level services", false)
|
||||
.option("--json", "Output JSON", false)
|
||||
.action(async (cmdOpts) => {
|
||||
.action(async (cmdOpts, command) => {
|
||||
await runDaemonStatus({
|
||||
rpc: cmdOpts,
|
||||
rpc: resolveRpcOptions(cmdOpts, command),
|
||||
probe: Boolean(cmdOpts.probe),
|
||||
deep: Boolean(cmdOpts.deep),
|
||||
json: Boolean(cmdOpts.json),
|
||||
@@ -36,8 +63,8 @@ export function addGatewayServiceCommands(parent: Command, opts?: { statusDescri
|
||||
.option("--token <token>", "Gateway token (token auth)")
|
||||
.option("--force", "Reinstall/overwrite if already installed", false)
|
||||
.option("--json", "Output JSON", false)
|
||||
.action(async (cmdOpts) => {
|
||||
await runDaemonInstall(cmdOpts);
|
||||
.action(async (cmdOpts, command) => {
|
||||
await runDaemonInstall(resolveInstallOptions(cmdOpts, command));
|
||||
});
|
||||
|
||||
parent
|
||||
|
||||
Reference in New Issue
Block a user