366 lines
20 KiB
TypeScript
366 lines
20 KiB
TypeScript
// SPEC: PJ2026-01060307 控制面模块化 draft-2026-06-25-p0. options module for scripts/src/hwlab-g14.ts.
|
|
|
|
// Moved mechanically from scripts/src/hwlab-g14.ts:289-638 for #903.
|
|
|
|
import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
import { dirname, join } from "node:path";
|
|
import { createHash, randomBytes } from "node:crypto";
|
|
import { repoRoot, rootPath, type Config } from "../config";
|
|
import { runCommand } from "../command";
|
|
import { cancelJob, listJobs, readJob, startJob } from "../jobs";
|
|
import { hwlabRequiredNoProxyEntries, hwlabRuntimeLaneConfigPath, hwlabRuntimeLaneIds, hwlabRuntimeLaneSpec, isHwlabRuntimeLane, type HwlabRuntimeLane, type HwlabRuntimeLaneSpec } from "../hwlab-node-lanes";
|
|
|
|
import type { G14ControlPlaneOptions, G14GitMirrorOptions, G14LegacyRetirementOptions, G14MonitorLane, G14MonitorOptions, G14ObservabilityOptions, G14RecordRolloutOptions, G14SecretOptions, G14ToolsImageOptions, G14UpstreamImageOptions } from "./types";
|
|
import { positiveIntegerOption, runtimeLaneMasterAdminApiKeySecretName, runtimeLaneOpenFgaSecretName } from "./remote";
|
|
import { DEFAULT_INTERVAL_SECONDS, DEFAULT_MAX_CYCLES, DEFAULT_TIMEOUT_SECONDS, G14_CI_TOOLS_BASE_TAG, V02_MASTER_ADMIN_API_KEY_SECRET, V02_MASTER_ADMIN_API_KEY_SECRET_KEY, V02_OPENFGA_AUTHN_SECRET_KEY, V02_OPENFGA_DATASTORE_URI_SECRET_KEY, V02_OPENFGA_POSTGRES_PASSWORD_SECRET_KEY, V02_OPENFGA_SECRET } from "./types";
|
|
|
|
export function hwlabG14MonitorStateFileName(options: Pick<G14MonitorOptions, "once" | "dryRun"> & { lane?: G14MonitorLane }): string {
|
|
const prefix = options.lane !== undefined && options.lane !== "g14" ? `latest-${options.lane}-` : "latest-";
|
|
if (options.once && options.dryRun) return `${prefix}once-dry-run-job.json`;
|
|
if (options.once) return `${prefix}once-job.json`;
|
|
if (options.dryRun) return `${prefix}dry-run-job.json`;
|
|
return `${prefix}monitor-job.json`;
|
|
}
|
|
|
|
export function hwlabG14MonitorStateRole(options: Pick<G14MonitorOptions, "once" | "dryRun"> & { lane?: G14MonitorLane }): string {
|
|
const lanePrefix = options.lane !== undefined && options.lane !== "g14" ? `${options.lane}-` : "";
|
|
if (options.once && options.dryRun) return `${lanePrefix}once-dry-run`;
|
|
if (options.once) return `${lanePrefix}once`;
|
|
if (options.dryRun) return `${lanePrefix}dry-run-monitor`;
|
|
return `${lanePrefix}monitor`;
|
|
}
|
|
|
|
export function parseMonitorLane(args: string[]): G14MonitorLane {
|
|
const lane = optionValue(args, "--lane") ?? "g14";
|
|
if (lane !== "g14" && !isHwlabRuntimeLane(lane)) throw new Error(`monitor-prs --lane must be g14 or ${hwlabRuntimeLaneIds().join("|")}`);
|
|
return lane;
|
|
}
|
|
|
|
export function parseOptions(args: string[]): G14MonitorOptions {
|
|
return {
|
|
lane: parseMonitorLane(args),
|
|
intervalSeconds: positiveIntegerOption(args, "--interval-seconds", DEFAULT_INTERVAL_SECONDS, 86400),
|
|
maxCycles: positiveIntegerOption(args, "--max-cycles", DEFAULT_MAX_CYCLES, 100000),
|
|
once: args.includes("--once"),
|
|
dryRun: args.includes("--dry-run"),
|
|
worker: args.includes("--worker"),
|
|
timeoutSeconds: positiveIntegerOption(args, "--timeout-seconds", DEFAULT_TIMEOUT_SECONDS, 86400),
|
|
};
|
|
}
|
|
|
|
export function optionValue(args: string[], name: string): string | undefined {
|
|
const index = args.indexOf(name);
|
|
if (index === -1) return undefined;
|
|
const value = args[index + 1];
|
|
if (value === undefined || value.startsWith("--")) throw new Error(`${name} requires a value`);
|
|
return value;
|
|
}
|
|
|
|
export function validateFullShaOption(value: string, name: string): string {
|
|
if (!/^[0-9a-f]{40}$/iu.test(value)) throw new Error(`${name} must be a full 40-character git SHA`);
|
|
return value.toLowerCase();
|
|
}
|
|
|
|
export function validateV02PipelineRunOption(value: string): string {
|
|
if (!/^hwlab-v02-ci-poll-[0-9a-f]{7,40}$/iu.test(value)) throw new Error("--pipeline-run must be a hwlab-v02-ci-poll-<sha> PipelineRun name");
|
|
return value.toLowerCase();
|
|
}
|
|
|
|
export function validateRuntimeLanePipelineRunOption(lane: HwlabRuntimeLane, value: string): string {
|
|
const spec = hwlabRuntimeLaneSpec(lane);
|
|
const escapedPrefix = spec.pipelineRunPrefix.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
if (!new RegExp(`^${escapedPrefix}-[0-9a-f]{7,40}(?:-[a-z0-9][a-z0-9-]{0,24})?$`, "iu").test(value)) {
|
|
throw new Error(`--pipeline-run must be a ${spec.pipelineRunPrefix}-<sha>[-rerun] PipelineRun name for --lane ${lane}`);
|
|
}
|
|
return value.toLowerCase();
|
|
}
|
|
|
|
export function parseRecordRolloutOptions(args: string[]): G14RecordRolloutOptions {
|
|
const prRaw = optionValue(args, "--pr") ?? optionValue(args, "--number");
|
|
const prNumber = Number(prRaw);
|
|
if (!Number.isInteger(prNumber) || prNumber <= 0) throw new Error("record-rollout requires --pr <number>");
|
|
return {
|
|
prNumber,
|
|
sourceCommit: optionValue(args, "--source-commit"),
|
|
pipelineRun: optionValue(args, "--pipeline-run"),
|
|
gitopsRevision: optionValue(args, "--gitops-revision"),
|
|
mergedAt: optionValue(args, "--merged-at"),
|
|
pipelineSucceededAt: optionValue(args, "--pipeline-succeeded-at"),
|
|
finishedAt: optionValue(args, "--finished-at"),
|
|
dryRun: args.includes("--dry-run"),
|
|
};
|
|
}
|
|
|
|
export function parseControlPlaneOptions(args: string[]): G14ControlPlaneOptions {
|
|
const [rawAction] = args;
|
|
const actionRaw = rawAction === "rerun-current" ? "trigger-current" : rawAction;
|
|
if (
|
|
actionRaw !== "status" &&
|
|
actionRaw !== "closeout" &&
|
|
actionRaw !== "apply" &&
|
|
actionRaw !== "trigger-current" &&
|
|
actionRaw !== "refresh" &&
|
|
actionRaw !== "cleanup-runs" &&
|
|
actionRaw !== "cleanup-released-pvs" &&
|
|
actionRaw !== "runtime-migration"
|
|
) {
|
|
throw new Error("control-plane usage: status|apply|trigger-current|refresh --lane v02|v03 | closeout|runtime-migration --lane v02 | cleanup-runs --lane v02|v03|g14|all | cleanup-released-pvs --lane all [--dry-run|--confirm]");
|
|
}
|
|
const laneRaw = optionValue(args, "--lane") ?? (actionRaw === "cleanup-released-pvs" ? "all" : "v02");
|
|
let lane: G14ControlPlaneOptions["lane"];
|
|
if (actionRaw === "cleanup-runs") {
|
|
if (laneRaw !== "g14" && laneRaw !== "all" && !isHwlabRuntimeLane(laneRaw)) throw new Error("control-plane cleanup-runs requires --lane v02|v03|g14|all");
|
|
lane = laneRaw;
|
|
} else if (actionRaw === "cleanup-released-pvs") {
|
|
if (laneRaw !== "all") throw new Error("control-plane cleanup-released-pvs requires --lane all because released PVs no longer preserve the v02/g14 PipelineRun lane");
|
|
lane = laneRaw;
|
|
} else if (actionRaw === "closeout" || actionRaw === "runtime-migration") {
|
|
if (laneRaw !== "v02") throw new Error("control-plane closeout/runtime-migration currently requires --lane v02");
|
|
lane = laneRaw;
|
|
} else if (!isHwlabRuntimeLane(laneRaw)) {
|
|
throw new Error(`control-plane ${actionRaw} requires --lane ${hwlabRuntimeLaneIds().join("|")}`);
|
|
} else {
|
|
lane = laneRaw;
|
|
}
|
|
const confirm = args.includes("--confirm");
|
|
const explicitDryRun = args.includes("--dry-run");
|
|
if (confirm && explicitDryRun) throw new Error("control-plane accepts only one of --confirm or --dry-run");
|
|
const allowLiveDbRead = args.includes("--allow-live-db-read");
|
|
if (allowLiveDbRead && confirm) throw new Error("control-plane runtime-migration accepts --allow-live-db-read only with dry-run/source-check mode, not --confirm");
|
|
if (allowLiveDbRead && actionRaw !== "runtime-migration") throw new Error("--allow-live-db-read is only valid for control-plane runtime-migration");
|
|
const sourceCommitRaw = optionValue(args, "--source-commit");
|
|
const pipelineRunRaw = optionValue(args, "--pipeline-run");
|
|
if ((sourceCommitRaw !== undefined || pipelineRunRaw !== undefined) && actionRaw !== "status" && actionRaw !== "closeout" && actionRaw !== "cleanup-runs") {
|
|
throw new Error("--source-commit and --pipeline-run are only valid for control-plane status/closeout/cleanup-runs");
|
|
}
|
|
if (actionRaw === "closeout" && sourceCommitRaw === undefined && pipelineRunRaw === undefined) {
|
|
throw new Error("control-plane closeout requires --source-commit <full-sha> or --pipeline-run hwlab-v02-ci-poll-<sha>");
|
|
}
|
|
if (sourceCommitRaw !== undefined && pipelineRunRaw !== undefined) throw new Error("control-plane status/closeout/cleanup-runs accepts only one of --source-commit or --pipeline-run");
|
|
return {
|
|
action: actionRaw,
|
|
lane,
|
|
confirm,
|
|
wait: args.includes("--wait"),
|
|
rerun: args.includes("--rerun") || rawAction === "rerun-current",
|
|
allowLiveDbRead,
|
|
dryRun: actionRaw === "status" || actionRaw === "closeout" ? true : explicitDryRun || !confirm,
|
|
timeoutSeconds: positiveIntegerOption(args, "--timeout-seconds", 120, 600),
|
|
minAgeMinutes: positiveIntegerOption(args, "--min-age-minutes", 60, 10080),
|
|
limit: positiveIntegerOption(args, "--limit", 20, 200),
|
|
sourceCommit: sourceCommitRaw === undefined ? undefined : validateFullShaOption(sourceCommitRaw, "--source-commit"),
|
|
pipelineRun: pipelineRunRaw === undefined
|
|
? undefined
|
|
: isHwlabRuntimeLane(lane)
|
|
? validateRuntimeLanePipelineRunOption(lane, pipelineRunRaw)
|
|
: validateV02PipelineRunOption(pipelineRunRaw),
|
|
history: args.includes("--history"),
|
|
};
|
|
}
|
|
|
|
export function parseLegacyRetirementOptions(args: string[]): G14LegacyRetirementOptions {
|
|
const [actionRaw = "status"] = args;
|
|
if (actionRaw !== "status" && actionRaw !== "plan" && actionRaw !== "execute") {
|
|
throw new Error("retirement usage: status|plan|execute [--dry-run|--confirm] [--wait] [--reason text] [--timeout-seconds N]");
|
|
}
|
|
const confirm = args.includes("--confirm");
|
|
const explicitDryRun = args.includes("--dry-run");
|
|
if (confirm && explicitDryRun) throw new Error("retirement accepts only one of --confirm or --dry-run");
|
|
if (confirm && actionRaw !== "execute") throw new Error("--confirm is only valid for retirement execute");
|
|
if (args.includes("--wait") && actionRaw !== "execute") throw new Error("--wait is only valid for retirement execute");
|
|
const reason = optionValue(args, "--reason") ?? "user requested immediate retirement of legacy G14 DEV/PROD";
|
|
if (reason.length > 240) throw new Error("--reason is limited to 240 characters");
|
|
return {
|
|
action: actionRaw,
|
|
confirm,
|
|
wait: args.includes("--wait"),
|
|
dryRun: actionRaw !== "execute" || explicitDryRun || !confirm,
|
|
timeoutSeconds: positiveIntegerOption(args, "--timeout-seconds", actionRaw === "execute" ? 60 : 30, 300),
|
|
reason,
|
|
};
|
|
}
|
|
|
|
export function parseToolsImageOptions(args: string[]): G14ToolsImageOptions {
|
|
const [actionRaw] = args;
|
|
if (actionRaw !== "status" && actionRaw !== "build") {
|
|
throw new Error("tools-image usage: status|build --name ci-node-tools --tag <tag> [--dockerfile path] [--dry-run|--confirm]");
|
|
}
|
|
const name = optionValue(args, "--name") ?? "ci-node-tools";
|
|
if (name !== "ci-node-tools") throw new Error("tools-image currently supports --name ci-node-tools");
|
|
const tag = optionValue(args, "--tag") ?? G14_CI_TOOLS_BASE_TAG;
|
|
if (!/^[A-Za-z0-9_.-]+$/u.test(tag)) throw new Error("--tag may only contain letters, numbers, dot, underscore, and dash");
|
|
const dockerfile = optionValue(args, "--dockerfile") ?? "deploy/ci/hwlab-ci-node-tools.Dockerfile";
|
|
if (dockerfile.startsWith("/") || dockerfile.includes("..")) throw new Error("--dockerfile must be a repo-relative path without '..'");
|
|
const confirm = args.includes("--confirm");
|
|
const explicitDryRun = args.includes("--dry-run");
|
|
if (confirm && explicitDryRun) throw new Error("tools-image accepts only one of --confirm or --dry-run");
|
|
return {
|
|
action: actionRaw,
|
|
name,
|
|
tag,
|
|
dockerfile,
|
|
confirm,
|
|
dryRun: actionRaw === "status" ? true : explicitDryRun || !confirm,
|
|
timeoutSeconds: positiveIntegerOption(args, "--timeout-seconds", 600, 1800),
|
|
};
|
|
}
|
|
|
|
export function parseUpstreamImageOptions(args: string[]): G14UpstreamImageOptions {
|
|
const [actionRaw] = args;
|
|
if (actionRaw !== "status" && actionRaw !== "ensure") {
|
|
throw new Error("upstream-image usage: status|ensure --name openfga --tag v1.17.0 [--dry-run|--confirm]");
|
|
}
|
|
const name = optionValue(args, "--name") ?? "openfga";
|
|
if (name !== "openfga") throw new Error("upstream-image currently supports --name openfga");
|
|
const tag = optionValue(args, "--tag") ?? "v1.17.0";
|
|
if (tag !== "v1.17.0") throw new Error("upstream-image currently supports OpenFGA tag v1.17.0");
|
|
const confirm = args.includes("--confirm");
|
|
const explicitDryRun = args.includes("--dry-run");
|
|
if (confirm && explicitDryRun) throw new Error("upstream-image accepts only one of --confirm or --dry-run");
|
|
return {
|
|
action: actionRaw,
|
|
name,
|
|
tag,
|
|
confirm,
|
|
dryRun: actionRaw === "status" ? true : explicitDryRun || !confirm,
|
|
timeoutSeconds: positiveIntegerOption(args, "--timeout-seconds", 600, 1800),
|
|
};
|
|
}
|
|
|
|
export function parseGitMirrorOptions(args: string[]): G14GitMirrorOptions {
|
|
const [actionRaw] = args;
|
|
if (actionRaw !== "status" && actionRaw !== "apply" && actionRaw !== "sync" && actionRaw !== "flush") {
|
|
throw new Error("git-mirror usage: status|apply|sync|flush [--lane v02|v03] [--dry-run|--confirm]");
|
|
}
|
|
const laneRaw = optionValue(args, "--lane") ?? "v02";
|
|
if (!isHwlabRuntimeLane(laneRaw)) throw new Error(`git-mirror --lane must be ${hwlabRuntimeLaneIds().join("|")}`);
|
|
const confirm = args.includes("--confirm");
|
|
const explicitDryRun = args.includes("--dry-run");
|
|
if (confirm && explicitDryRun) throw new Error("git-mirror accepts only one of --confirm or --dry-run");
|
|
return {
|
|
action: actionRaw,
|
|
lane: laneRaw,
|
|
confirm,
|
|
wait: args.includes("--wait"),
|
|
dryRun: actionRaw === "status" ? true : explicitDryRun || !confirm,
|
|
timeoutSeconds: positiveIntegerOption(args, "--timeout-seconds", actionRaw === "sync" || actionRaw === "flush" ? 300 : 120, 900),
|
|
};
|
|
}
|
|
|
|
export function parseObservabilityOptions(args: string[]): G14ObservabilityOptions {
|
|
const [actionRaw] = args;
|
|
if (
|
|
actionRaw !== "status" &&
|
|
actionRaw !== "apply" &&
|
|
actionRaw !== "query" &&
|
|
actionRaw !== "targets" &&
|
|
actionRaw !== "boundary" &&
|
|
actionRaw !== "closeout"
|
|
) {
|
|
throw new Error("observability usage: status|apply|query|targets|boundary|closeout [--lane v02] [--promql <expr>] [--expect-count N] [--expect-value V] [--dry-run|--confirm]");
|
|
}
|
|
const valueOptions = new Set(["--lane", "--promql", "--query", "--expect-count", "--expect-value", "--timeout-seconds"]);
|
|
const booleanOptions = new Set(["--confirm", "--dry-run"]);
|
|
for (let i = 1; i < args.length; i += 1) {
|
|
const arg = args[i];
|
|
if (valueOptions.has(arg)) {
|
|
i += 1;
|
|
if (i >= args.length || args[i].startsWith("--")) throw new Error(`${arg} requires a value`);
|
|
continue;
|
|
}
|
|
if (booleanOptions.has(arg)) continue;
|
|
if (arg === "--wait") throw new Error("observability does not support --wait; commands are bounded short CLI calls");
|
|
if (arg.startsWith("--")) throw new Error(`unsupported observability option: ${arg}`);
|
|
throw new Error(`unexpected observability argument: ${arg}`);
|
|
}
|
|
const lane = optionValue(args, "--lane") ?? "v02";
|
|
if (lane !== "v02") throw new Error("observability currently supports --lane v02");
|
|
const confirm = args.includes("--confirm");
|
|
const explicitDryRun = args.includes("--dry-run");
|
|
if (confirm && explicitDryRun) throw new Error("observability accepts only one of --confirm or --dry-run");
|
|
if ((confirm || explicitDryRun) && actionRaw !== "apply") throw new Error("--confirm and --dry-run are only supported by observability apply");
|
|
const query = optionValue(args, "--promql") ?? optionValue(args, "--query") ?? 'up{namespace="hwlab-v02"}';
|
|
if (query.length > 500) throw new Error("--promql is limited to 500 characters");
|
|
if (query.includes("\n") || query.includes("\r")) throw new Error("--promql must be a single-line expression");
|
|
const expectCountRaw = optionValue(args, "--expect-count");
|
|
const expectCount = expectCountRaw === undefined ? undefined : Number(expectCountRaw);
|
|
if (expectCountRaw !== undefined && (!Number.isInteger(expectCount) || expectCount < 0)) throw new Error("--expect-count must be a non-negative integer");
|
|
const expectValue = optionValue(args, "--expect-value");
|
|
if ((expectCount !== undefined || expectValue !== undefined) && actionRaw !== "query") throw new Error("--expect-count and --expect-value are only supported by observability query");
|
|
if (expectValue !== undefined && expectValue.trim().length === 0) throw new Error("--expect-value must not be blank");
|
|
return {
|
|
action: actionRaw,
|
|
lane,
|
|
confirm,
|
|
wait: args.includes("--wait"),
|
|
dryRun: actionRaw === "apply" ? explicitDryRun || !confirm : true,
|
|
timeoutSeconds: positiveIntegerOption(args, "--timeout-seconds", actionRaw === "apply" ? 240 : 120, 900),
|
|
query,
|
|
expectCount,
|
|
expectValue,
|
|
};
|
|
}
|
|
|
|
export function parseSecretOptions(args: string[]): G14SecretOptions {
|
|
const [actionRaw] = args;
|
|
if (actionRaw !== "status" && actionRaw !== "ensure" && actionRaw !== "delete") {
|
|
throw new Error("secret usage: status|ensure --lane v02|v03 --name hwlab-v0x-openfga|hwlab-v0x-master-server-admin-api-key [--dry-run|--confirm] | delete --lane v02 --name <obsolete-secret> [--dry-run|--confirm]");
|
|
}
|
|
const laneRaw = optionValue(args, "--lane") ?? "v02";
|
|
if (!isHwlabRuntimeLane(laneRaw)) throw new Error(`secret --lane must be one of ${hwlabRuntimeLaneIds().join(", ")}`);
|
|
const lane = laneRaw;
|
|
const spec = hwlabRuntimeLaneSpec(lane);
|
|
const openFgaSecret = runtimeLaneOpenFgaSecretName(spec);
|
|
const masterAdminSecret = runtimeLaneMasterAdminApiKeySecretName(spec);
|
|
const name = optionValue(args, "--name") ?? openFgaSecret;
|
|
const key = optionValue(args, "--key");
|
|
const confirm = args.includes("--confirm");
|
|
const explicitDryRun = args.includes("--dry-run");
|
|
if (confirm && explicitDryRun) throw new Error("secret accepts only one of --confirm or --dry-run");
|
|
if (actionRaw === "delete") {
|
|
if (lane !== "v02") throw new Error("secret delete is currently limited to --lane v02 obsolete cleanup");
|
|
if (key !== undefined) throw new Error("secret delete does not accept --key; it deletes a whole obsolete Secret object");
|
|
if (!/^hwlab-v02-[a-z0-9-]+$/u.test(name)) throw new Error("secret delete requires a hwlab-v02-* Secret name");
|
|
if (name === V02_OPENFGA_SECRET || name === V02_MASTER_ADMIN_API_KEY_SECRET || name === "hwlab-v02-postgres") throw new Error(`secret delete refuses required v0.2 Secret ${name}`);
|
|
return {
|
|
action: actionRaw,
|
|
lane,
|
|
confirm,
|
|
dryRun: explicitDryRun || !confirm,
|
|
name,
|
|
preset: "generic-delete",
|
|
timeoutSeconds: positiveIntegerOption(args, "--timeout-seconds", 120, 600),
|
|
};
|
|
}
|
|
if (name === masterAdminSecret) {
|
|
if (key !== undefined && key !== V02_MASTER_ADMIN_API_KEY_SECRET_KEY) throw new Error(`secret ${masterAdminSecret} supports only key ${V02_MASTER_ADMIN_API_KEY_SECRET_KEY}`);
|
|
return {
|
|
action: actionRaw,
|
|
lane,
|
|
confirm,
|
|
dryRun: actionRaw === "status" ? true : explicitDryRun || !confirm,
|
|
name,
|
|
key: key ?? V02_MASTER_ADMIN_API_KEY_SECRET_KEY,
|
|
preset: "master-server-admin-api-key",
|
|
timeoutSeconds: positiveIntegerOption(args, "--timeout-seconds", 120, 600),
|
|
};
|
|
}
|
|
if (name !== openFgaSecret) {
|
|
throw new Error(`secret status/ensure currently supports only --name ${openFgaSecret} or ${masterAdminSecret} for --lane ${lane}; use secret delete for obsolete v02 Secret objects`);
|
|
}
|
|
if (key !== undefined && key !== V02_OPENFGA_AUTHN_SECRET_KEY && key !== V02_OPENFGA_DATASTORE_URI_SECRET_KEY && key !== V02_OPENFGA_POSTGRES_PASSWORD_SECRET_KEY) {
|
|
throw new Error(`secret ${openFgaSecret} supports keys ${V02_OPENFGA_AUTHN_SECRET_KEY}, ${V02_OPENFGA_DATASTORE_URI_SECRET_KEY}, and ${V02_OPENFGA_POSTGRES_PASSWORD_SECRET_KEY}`);
|
|
}
|
|
if (lane !== "v02" && key === V02_OPENFGA_POSTGRES_PASSWORD_SECRET_KEY) throw new Error(`${openFgaSecret}/${V02_OPENFGA_POSTGRES_PASSWORD_SECRET_KEY} is derived from ${postgresSecret}/POSTGRES_PASSWORD`);
|
|
return {
|
|
action: actionRaw,
|
|
lane,
|
|
confirm,
|
|
dryRun: actionRaw === "status" ? true : explicitDryRun || !confirm,
|
|
name,
|
|
key,
|
|
preset: "openfga",
|
|
timeoutSeconds: positiveIntegerOption(args, "--timeout-seconds", 120, 600),
|
|
};
|
|
}
|