mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-19 14:00:51 +00:00
CLI: skip exec SecretRef dry-run resolution unless explicitly allowed (#49322)
* CLI: gate exec SecretRef dry-run resolution behind opt-in * Docs: clarify config dry-run exec opt-in behavior * CLI: preserve static exec dry-run validation
This commit is contained in:
@@ -23,6 +23,39 @@ function createTestRuntime() {
|
||||
};
|
||||
}
|
||||
|
||||
function createExecDryRunBatch(params: { markerPath: string }) {
|
||||
const response = JSON.stringify({
|
||||
protocolVersion: 1,
|
||||
values: {
|
||||
dryrun_id: "ok",
|
||||
},
|
||||
});
|
||||
const script = [
|
||||
'const fs = require("node:fs");',
|
||||
`fs.writeFileSync(${JSON.stringify(params.markerPath)}, "dryrun\\n", "utf8");`,
|
||||
`process.stdout.write(${JSON.stringify(response)});`,
|
||||
].join("");
|
||||
return [
|
||||
{
|
||||
path: "secrets.providers.runner",
|
||||
provider: {
|
||||
source: "exec",
|
||||
command: process.execPath,
|
||||
args: ["-e", script],
|
||||
allowInsecurePath: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "channels.discord.token",
|
||||
ref: {
|
||||
source: "exec",
|
||||
provider: "runner",
|
||||
id: "dryrun_id",
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
describe("config cli integration", () => {
|
||||
it("supports batch-file dry-run and then writes real config changes", async () => {
|
||||
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-config-cli-int-"));
|
||||
@@ -183,4 +216,115 @@ describe("config cli integration", () => {
|
||||
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("skips exec provider execution during dry-run by default", async () => {
|
||||
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-config-cli-int-exec-skip-"));
|
||||
const configPath = path.join(tempDir, "openclaw.json");
|
||||
const batchPath = path.join(tempDir, "batch.json");
|
||||
const markerPath = path.join(tempDir, "marker.txt");
|
||||
const envSnapshot = captureEnv(["OPENCLAW_CONFIG_PATH", "OPENCLAW_TEST_FAST"]);
|
||||
try {
|
||||
fs.writeFileSync(
|
||||
configPath,
|
||||
`${JSON.stringify(
|
||||
{
|
||||
gateway: { port: 18789 },
|
||||
},
|
||||
null,
|
||||
2,
|
||||
)}\n`,
|
||||
"utf8",
|
||||
);
|
||||
fs.writeFileSync(
|
||||
batchPath,
|
||||
`${JSON.stringify(createExecDryRunBatch({ markerPath }), null, 2)}\n`,
|
||||
"utf8",
|
||||
);
|
||||
|
||||
process.env.OPENCLAW_TEST_FAST = "1";
|
||||
process.env.OPENCLAW_CONFIG_PATH = configPath;
|
||||
clearConfigCache();
|
||||
clearRuntimeConfigSnapshot();
|
||||
|
||||
const runtime = createTestRuntime();
|
||||
const before = fs.readFileSync(configPath, "utf8");
|
||||
await runConfigSet({
|
||||
cliOptions: {
|
||||
batchFile: batchPath,
|
||||
dryRun: true,
|
||||
},
|
||||
runtime: runtime.runtime,
|
||||
});
|
||||
const after = fs.readFileSync(configPath, "utf8");
|
||||
|
||||
expect(after).toBe(before);
|
||||
expect(fs.existsSync(markerPath)).toBe(false);
|
||||
expect(
|
||||
runtime.logs.some((line) =>
|
||||
line.includes("Dry run note: skipped 1 exec SecretRef resolvability check(s)."),
|
||||
),
|
||||
).toBe(true);
|
||||
} finally {
|
||||
envSnapshot.restore();
|
||||
clearConfigCache();
|
||||
clearRuntimeConfigSnapshot();
|
||||
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("executes exec providers during dry-run when --allow-exec is set", async () => {
|
||||
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-config-cli-int-exec-allow-"));
|
||||
const configPath = path.join(tempDir, "openclaw.json");
|
||||
const batchPath = path.join(tempDir, "batch.json");
|
||||
const markerPath = path.join(tempDir, "marker.txt");
|
||||
const envSnapshot = captureEnv(["OPENCLAW_CONFIG_PATH", "OPENCLAW_TEST_FAST"]);
|
||||
try {
|
||||
fs.writeFileSync(
|
||||
configPath,
|
||||
`${JSON.stringify(
|
||||
{
|
||||
gateway: { port: 18789 },
|
||||
},
|
||||
null,
|
||||
2,
|
||||
)}\n`,
|
||||
"utf8",
|
||||
);
|
||||
fs.writeFileSync(
|
||||
batchPath,
|
||||
`${JSON.stringify(createExecDryRunBatch({ markerPath }), null, 2)}\n`,
|
||||
"utf8",
|
||||
);
|
||||
|
||||
process.env.OPENCLAW_TEST_FAST = "1";
|
||||
process.env.OPENCLAW_CONFIG_PATH = configPath;
|
||||
clearConfigCache();
|
||||
clearRuntimeConfigSnapshot();
|
||||
|
||||
const runtime = createTestRuntime();
|
||||
const before = fs.readFileSync(configPath, "utf8");
|
||||
await runConfigSet({
|
||||
cliOptions: {
|
||||
batchFile: batchPath,
|
||||
dryRun: true,
|
||||
allowExec: true,
|
||||
},
|
||||
runtime: runtime.runtime,
|
||||
});
|
||||
const after = fs.readFileSync(configPath, "utf8");
|
||||
|
||||
expect(after).toBe(before);
|
||||
expect(fs.existsSync(markerPath)).toBe(true);
|
||||
expect(
|
||||
runtime.logs.some((line) =>
|
||||
line.includes("Dry run note: skipped 1 exec SecretRef resolvability check(s)."),
|
||||
),
|
||||
).toBe(false);
|
||||
} finally {
|
||||
envSnapshot.restore();
|
||||
clearConfigCache();
|
||||
clearRuntimeConfigSnapshot();
|
||||
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -386,6 +386,7 @@ describe("config cli", () => {
|
||||
expect(helpText).toContain("--provider-source");
|
||||
expect(helpText).toContain("--batch-json");
|
||||
expect(helpText).toContain("--dry-run");
|
||||
expect(helpText).toContain("--allow-exec");
|
||||
expect(helpText).toContain("openclaw config set gateway.port 19001 --strict-json");
|
||||
expect(helpText).toContain(
|
||||
"openclaw config set channels.discord.token --ref-provider default --ref-source",
|
||||
@@ -556,6 +557,169 @@ describe("config cli", () => {
|
||||
expect(mockResolveSecretRefValue).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("skips exec SecretRef resolvability checks in dry-run by default", async () => {
|
||||
const resolved: OpenClawConfig = {
|
||||
gateway: { port: 18789 },
|
||||
secrets: {
|
||||
providers: {
|
||||
runner: {
|
||||
source: "exec",
|
||||
command: "/usr/bin/env",
|
||||
allowInsecurePath: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
setSnapshot(resolved, resolved);
|
||||
|
||||
await runConfigCommand([
|
||||
"config",
|
||||
"set",
|
||||
"channels.discord.token",
|
||||
"--ref-provider",
|
||||
"runner",
|
||||
"--ref-source",
|
||||
"exec",
|
||||
"--ref-id",
|
||||
"openai",
|
||||
"--dry-run",
|
||||
]);
|
||||
|
||||
expect(mockWriteConfigFile).not.toHaveBeenCalled();
|
||||
expect(mockResolveSecretRefValue).not.toHaveBeenCalled();
|
||||
expect(mockLog).toHaveBeenCalledWith(
|
||||
expect.stringContaining(
|
||||
"Dry run note: skipped 1 exec SecretRef resolvability check(s). Re-run with --allow-exec",
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it("allows exec SecretRef resolvability checks in dry-run when --allow-exec is set", async () => {
|
||||
const resolved: OpenClawConfig = {
|
||||
gateway: { port: 18789 },
|
||||
secrets: {
|
||||
providers: {
|
||||
runner: {
|
||||
source: "exec",
|
||||
command: "/usr/bin/env",
|
||||
allowInsecurePath: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
setSnapshot(resolved, resolved);
|
||||
|
||||
await runConfigCommand([
|
||||
"config",
|
||||
"set",
|
||||
"channels.discord.token",
|
||||
"--ref-provider",
|
||||
"runner",
|
||||
"--ref-source",
|
||||
"exec",
|
||||
"--ref-id",
|
||||
"openai",
|
||||
"--dry-run",
|
||||
"--allow-exec",
|
||||
]);
|
||||
|
||||
expect(mockWriteConfigFile).not.toHaveBeenCalled();
|
||||
expect(mockResolveSecretRefValue).toHaveBeenCalledTimes(1);
|
||||
expect(mockResolveSecretRefValue).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
source: "exec",
|
||||
provider: "runner",
|
||||
id: "openai",
|
||||
}),
|
||||
expect.any(Object),
|
||||
);
|
||||
expect(mockLog).not.toHaveBeenCalledWith(
|
||||
expect.stringContaining("Dry run note: skipped 1 exec SecretRef resolvability check(s)."),
|
||||
);
|
||||
});
|
||||
|
||||
it("rejects --allow-exec without --dry-run", async () => {
|
||||
const nonexistentBatchPath = path.join(
|
||||
os.tmpdir(),
|
||||
`openclaw-config-batch-nonexistent-${Date.now()}-${Math.random().toString(16).slice(2)}.json`,
|
||||
);
|
||||
await expect(
|
||||
runConfigCommand(["config", "set", "--batch-file", nonexistentBatchPath, "--allow-exec"]),
|
||||
).rejects.toThrow("__exit__:1");
|
||||
|
||||
expect(mockWriteConfigFile).not.toHaveBeenCalled();
|
||||
expect(mockResolveSecretRefValue).not.toHaveBeenCalled();
|
||||
expect(mockError).toHaveBeenCalledWith(
|
||||
expect.stringContaining("config set mode error: --allow-exec requires --dry-run."),
|
||||
);
|
||||
});
|
||||
|
||||
it("fails dry-run when skipped exec refs use an unconfigured provider", async () => {
|
||||
const resolved: OpenClawConfig = {
|
||||
gateway: { port: 18789 },
|
||||
secrets: {
|
||||
providers: {},
|
||||
},
|
||||
};
|
||||
setSnapshot(resolved, resolved);
|
||||
|
||||
await expect(
|
||||
runConfigCommand([
|
||||
"config",
|
||||
"set",
|
||||
"channels.discord.token",
|
||||
"--ref-provider",
|
||||
"runner",
|
||||
"--ref-source",
|
||||
"exec",
|
||||
"--ref-id",
|
||||
"openai",
|
||||
"--dry-run",
|
||||
]),
|
||||
).rejects.toThrow("__exit__:1");
|
||||
|
||||
expect(mockResolveSecretRefValue).not.toHaveBeenCalled();
|
||||
expect(mockError).toHaveBeenCalledWith(
|
||||
expect.stringContaining('Secret provider "runner" is not configured'),
|
||||
);
|
||||
});
|
||||
|
||||
it("fails dry-run when skipped exec refs use a provider with mismatched source", async () => {
|
||||
const resolved: OpenClawConfig = {
|
||||
gateway: { port: 18789 },
|
||||
secrets: {
|
||||
providers: {
|
||||
runner: {
|
||||
source: "env",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
setSnapshot(resolved, resolved);
|
||||
|
||||
await expect(
|
||||
runConfigCommand([
|
||||
"config",
|
||||
"set",
|
||||
"channels.discord.token",
|
||||
"--ref-provider",
|
||||
"runner",
|
||||
"--ref-source",
|
||||
"exec",
|
||||
"--ref-id",
|
||||
"openai",
|
||||
"--dry-run",
|
||||
]),
|
||||
).rejects.toThrow("__exit__:1");
|
||||
|
||||
expect(mockResolveSecretRefValue).not.toHaveBeenCalled();
|
||||
expect(mockError).toHaveBeenCalledWith(
|
||||
expect.stringContaining(
|
||||
'Secret provider "runner" has source "env" but ref requests "exec".',
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it("writes sibling SecretRef paths when target uses sibling-ref shape", async () => {
|
||||
const resolved: OpenClawConfig = {
|
||||
gateway: { port: 18789 },
|
||||
@@ -749,19 +913,66 @@ describe("config cli", () => {
|
||||
expect(typeof raw).toBe("string");
|
||||
const payload = JSON.parse(String(raw)) as {
|
||||
ok: boolean;
|
||||
checks: { schema: boolean; resolvability: boolean };
|
||||
checks: { schema: boolean; resolvability: boolean; resolvabilityComplete: boolean };
|
||||
refsChecked: number;
|
||||
skippedExecRefs: number;
|
||||
operations: number;
|
||||
};
|
||||
expect(payload.ok).toBe(true);
|
||||
expect(payload.operations).toBe(1);
|
||||
expect(payload.refsChecked).toBe(1);
|
||||
expect(payload.skippedExecRefs).toBe(0);
|
||||
expect(payload.checks).toEqual({
|
||||
schema: false,
|
||||
resolvability: true,
|
||||
resolvabilityComplete: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("emits skipped exec metadata for --dry-run --json success", async () => {
|
||||
const resolved: OpenClawConfig = {
|
||||
gateway: { port: 18789 },
|
||||
secrets: {
|
||||
providers: {
|
||||
runner: {
|
||||
source: "exec",
|
||||
command: "/usr/bin/env",
|
||||
allowInsecurePath: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
setSnapshot(resolved, resolved);
|
||||
|
||||
await runConfigCommand([
|
||||
"config",
|
||||
"set",
|
||||
"channels.discord.token",
|
||||
"--ref-provider",
|
||||
"runner",
|
||||
"--ref-source",
|
||||
"exec",
|
||||
"--ref-id",
|
||||
"openai",
|
||||
"--dry-run",
|
||||
"--json",
|
||||
]);
|
||||
|
||||
const raw = mockLog.mock.calls.at(-1)?.[0];
|
||||
expect(typeof raw).toBe("string");
|
||||
const payload = JSON.parse(String(raw)) as {
|
||||
ok: boolean;
|
||||
checks: { resolvability: boolean; resolvabilityComplete: boolean };
|
||||
refsChecked: number;
|
||||
skippedExecRefs: number;
|
||||
};
|
||||
expect(payload.ok).toBe(true);
|
||||
expect(payload.checks.resolvability).toBe(true);
|
||||
expect(payload.checks.resolvabilityComplete).toBe(false);
|
||||
expect(payload.refsChecked).toBe(0);
|
||||
expect(payload.skippedExecRefs).toBe(1);
|
||||
});
|
||||
|
||||
it("emits structured JSON for --dry-run --json failure", async () => {
|
||||
const resolved: OpenClawConfig = {
|
||||
gateway: { port: 18789 },
|
||||
|
||||
@@ -22,6 +22,7 @@ import type { RuntimeEnv } from "../runtime.js";
|
||||
import { defaultRuntime } from "../runtime.js";
|
||||
import {
|
||||
formatExecSecretRefIdValidationMessage,
|
||||
isValidExecSecretRefId,
|
||||
isValidFileSecretRefId,
|
||||
isValidSecretProviderAlias,
|
||||
secretRefKey,
|
||||
@@ -815,6 +816,66 @@ async function collectDryRunResolvabilityErrors(params: {
|
||||
return failures;
|
||||
}
|
||||
|
||||
function collectDryRunStaticErrorsForSkippedExecRefs(params: {
|
||||
refs: SecretRef[];
|
||||
config: OpenClawConfig;
|
||||
}): ConfigSetDryRunError[] {
|
||||
const failures: ConfigSetDryRunError[] = [];
|
||||
for (const ref of params.refs) {
|
||||
const id = ref.id.trim();
|
||||
const refLabel = `${ref.source}:${ref.provider}:${id}`;
|
||||
if (!id) {
|
||||
failures.push({
|
||||
kind: "resolvability",
|
||||
message: "Error: Secret reference id is empty.",
|
||||
ref: refLabel,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
if (!isValidExecSecretRefId(id)) {
|
||||
failures.push({
|
||||
kind: "resolvability",
|
||||
message: `Error: ${formatExecSecretRefIdValidationMessage()} (ref: ${refLabel}).`,
|
||||
ref: refLabel,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
const providerConfig = params.config.secrets?.providers?.[ref.provider];
|
||||
if (!providerConfig) {
|
||||
failures.push({
|
||||
kind: "resolvability",
|
||||
message: `Error: Secret provider "${ref.provider}" is not configured (ref: ${refLabel}).`,
|
||||
ref: refLabel,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
if (providerConfig.source !== ref.source) {
|
||||
failures.push({
|
||||
kind: "resolvability",
|
||||
message: `Error: Secret provider "${ref.provider}" has source "${providerConfig.source}" but ref requests "${ref.source}".`,
|
||||
ref: refLabel,
|
||||
});
|
||||
}
|
||||
}
|
||||
return failures;
|
||||
}
|
||||
|
||||
function selectDryRunRefsForResolution(params: { refs: SecretRef[]; allowExecInDryRun: boolean }): {
|
||||
refsToResolve: SecretRef[];
|
||||
skippedExecRefs: SecretRef[];
|
||||
} {
|
||||
const refsToResolve: SecretRef[] = [];
|
||||
const skippedExecRefs: SecretRef[] = [];
|
||||
for (const ref of params.refs) {
|
||||
if (ref.source === "exec" && !params.allowExecInDryRun) {
|
||||
skippedExecRefs.push(ref);
|
||||
continue;
|
||||
}
|
||||
refsToResolve.push(ref);
|
||||
}
|
||||
return { refsToResolve, skippedExecRefs };
|
||||
}
|
||||
|
||||
function collectDryRunSchemaErrors(config: OpenClawConfig): ConfigSetDryRunError[] {
|
||||
const validated = validateConfigObjectRaw(config);
|
||||
if (validated.ok) {
|
||||
@@ -826,7 +887,11 @@ function collectDryRunSchemaErrors(config: OpenClawConfig): ConfigSetDryRunError
|
||||
}));
|
||||
}
|
||||
|
||||
function formatDryRunFailureMessage(errors: ConfigSetDryRunError[]): string {
|
||||
function formatDryRunFailureMessage(params: {
|
||||
errors: ConfigSetDryRunError[];
|
||||
skippedExecRefs: number;
|
||||
}): string {
|
||||
const { errors, skippedExecRefs } = params;
|
||||
const schemaErrors = errors.filter((error) => error.kind === "schema");
|
||||
const resolveErrors = errors.filter((error) => error.kind === "resolvability");
|
||||
const lines: string[] = [];
|
||||
@@ -847,6 +912,11 @@ function formatDryRunFailureMessage(errors: ConfigSetDryRunError[]): string {
|
||||
lines.push(`- ... ${resolveErrors.length - 5} more`);
|
||||
}
|
||||
}
|
||||
if (skippedExecRefs > 0) {
|
||||
lines.push(
|
||||
`Dry run note: skipped ${skippedExecRefs} exec SecretRef resolvability check(s). Re-run with --allow-exec to execute exec providers during dry-run.`,
|
||||
);
|
||||
}
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
@@ -868,6 +938,9 @@ export async function runConfigSet(opts: {
|
||||
if (!modeResolution.ok) {
|
||||
throw modeError(modeResolution.error);
|
||||
}
|
||||
if (opts.cliOptions.allowExec && !opts.cliOptions.dryRun) {
|
||||
throw modeError("--allow-exec requires --dry-run.");
|
||||
}
|
||||
|
||||
const batchEntries = parseBatchSource(opts.cliOptions);
|
||||
if (batchEntries) {
|
||||
@@ -903,14 +976,24 @@ export async function runConfigSet(opts: {
|
||||
operations,
|
||||
})
|
||||
: [];
|
||||
const selectedDryRunRefs = selectDryRunRefsForResolution({
|
||||
refs,
|
||||
allowExecInDryRun: Boolean(opts.cliOptions.allowExec),
|
||||
});
|
||||
const errors: ConfigSetDryRunError[] = [];
|
||||
if (hasJsonMode) {
|
||||
errors.push(...collectDryRunSchemaErrors(nextConfig));
|
||||
}
|
||||
if (hasJsonMode || hasBuilderMode) {
|
||||
errors.push(
|
||||
...collectDryRunStaticErrorsForSkippedExecRefs({
|
||||
refs: selectedDryRunRefs.skippedExecRefs,
|
||||
config: nextConfig,
|
||||
}),
|
||||
);
|
||||
errors.push(
|
||||
...(await collectDryRunResolvabilityErrors({
|
||||
refs,
|
||||
refs: selectedDryRunRefs.refsToResolve,
|
||||
config: nextConfig,
|
||||
})),
|
||||
);
|
||||
@@ -923,15 +1006,23 @@ export async function runConfigSet(opts: {
|
||||
checks: {
|
||||
schema: hasJsonMode,
|
||||
resolvability: hasJsonMode || hasBuilderMode,
|
||||
resolvabilityComplete:
|
||||
(hasJsonMode || hasBuilderMode) && selectedDryRunRefs.skippedExecRefs.length === 0,
|
||||
},
|
||||
refsChecked: refs.length,
|
||||
refsChecked: selectedDryRunRefs.refsToResolve.length,
|
||||
skippedExecRefs: selectedDryRunRefs.skippedExecRefs.length,
|
||||
...(errors.length > 0 ? { errors } : {}),
|
||||
};
|
||||
if (errors.length > 0) {
|
||||
if (opts.cliOptions.json) {
|
||||
throw new ConfigSetDryRunValidationError(dryRunResult);
|
||||
}
|
||||
throw new Error(formatDryRunFailureMessage(errors));
|
||||
throw new Error(
|
||||
formatDryRunFailureMessage({
|
||||
errors,
|
||||
skippedExecRefs: selectedDryRunRefs.skippedExecRefs.length,
|
||||
}),
|
||||
);
|
||||
}
|
||||
if (opts.cliOptions.json) {
|
||||
runtime.log(JSON.stringify(dryRunResult, null, 2));
|
||||
@@ -943,6 +1034,13 @@ export async function runConfigSet(opts: {
|
||||
),
|
||||
);
|
||||
}
|
||||
if (dryRunResult.skippedExecRefs > 0) {
|
||||
runtime.log(
|
||||
info(
|
||||
`Dry run note: skipped ${dryRunResult.skippedExecRefs} exec SecretRef resolvability check(s). Re-run with --allow-exec to execute exec providers during dry-run.`,
|
||||
),
|
||||
);
|
||||
}
|
||||
runtime.log(
|
||||
info(
|
||||
`Dry run successful: ${operations.length} update(s) validated against ${shortenHomePath(snapshot.path)}.`,
|
||||
@@ -1133,7 +1231,12 @@ export function registerConfigCli(program: Command) {
|
||||
.option("--json", "Legacy alias for --strict-json", false)
|
||||
.option(
|
||||
"--dry-run",
|
||||
"Validate changes without writing openclaw.json (checks run in builder/json/batch modes)",
|
||||
"Validate changes without writing openclaw.json (checks run in builder/json/batch modes; exec SecretRefs are skipped unless --allow-exec is set)",
|
||||
false,
|
||||
)
|
||||
.option(
|
||||
"--allow-exec",
|
||||
"Dry-run only: allow exec SecretRef resolvability checks (may execute provider commands)",
|
||||
false,
|
||||
)
|
||||
.option("--ref-provider <alias>", "SecretRef builder: provider alias")
|
||||
|
||||
@@ -14,7 +14,9 @@ export type ConfigSetDryRunResult = {
|
||||
checks: {
|
||||
schema: boolean;
|
||||
resolvability: boolean;
|
||||
resolvabilityComplete: boolean;
|
||||
};
|
||||
refsChecked: number;
|
||||
skippedExecRefs: number;
|
||||
errors?: ConfigSetDryRunError[];
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@ export type ConfigSetOptions = {
|
||||
strictJson?: boolean;
|
||||
json?: boolean;
|
||||
dryRun?: boolean;
|
||||
allowExec?: boolean;
|
||||
refProvider?: string;
|
||||
refSource?: string;
|
||||
refId?: string;
|
||||
|
||||
Reference in New Issue
Block a user