feat: add web probe sentinel cicd visibility (#897)

Co-authored-by: Codex <codex@noreply.local>
This commit is contained in:
Lyon
2026-06-25 22:46:46 +08:00
committed by GitHub
parent 46205321bf
commit 2da1c97e0d
8 changed files with 745 additions and 24 deletions
+25 -9
View File
@@ -17,7 +17,7 @@ import { nodeWebObserveCollectViewNodeScript, parseNodeWebProbeObserveCollectVie
import { withWebObserveCollectRendered, withWebObserveCommandRendered, withWebObserveStatusRendered } from "./hwlab-node-web-observe-render";
import { buildWebObserveWrapperForObserveOptions, webObserveWrapperStateDirFromStatus } from "./hwlab-node-web-observe-wrapper";
import { renderWebObserveWrapperContract } from "./hwlab-node-web-observe-wrapper-render";
import { webProbeSentinelConfigPlan, withWebProbeSentinelConfigRendered, type WebProbeSentinelConfigAction } from "./hwlab-node-web-sentinel-config";
import { runWebProbeSentinelCommand, type WebProbeSentinelOptions } from "./hwlab-node-web-sentinel-cicd";
import { hwlabNodeHelp, hwlabNodeObservabilityHelp, hwlabNodeWebProbeHelp } from "./hwlab-node-help";
import { compactWebProbeResult, compactWebProbeScriptResult } from "./hwlab-node-web-probe-summary";
import { nodeObservabilityRecordingRuleExpression, nodeObservabilityRecordingRuleSummaries, nodeObservabilityWarningAlertExpression, nodeObservabilityWarningAlertSummaries } from "./hwlab-node-observability-promql";
@@ -115,10 +115,9 @@ interface NodeWebProbeObserveOptions {
interface NodeWebProbeSentinelOptions {
action: "sentinel";
sentinelAction: WebProbeSentinelConfigAction;
sentinel: WebProbeSentinelOptions;
node: string;
lane: string;
dryRun: boolean;
}
type NodeWebProbeOptions = NodeWebProbeRunOptions | NodeWebProbeScriptOptions | NodeWebProbeObserveOptions | NodeWebProbeSentinelOptions;
@@ -7439,21 +7438,38 @@ function parseNodeWebProbeOptions(args: string[]): NodeWebProbeOptions {
function parseNodeWebProbeSentinelOptions(args: string[]): NodeWebProbeSentinelOptions {
const [sentinelActionRaw] = args;
if (sentinelActionRaw !== "plan" && sentinelActionRaw !== "status") {
throw new Error("web-probe sentinel usage: sentinel plan|status --node NODE --lane vNN [--dry-run]");
if (sentinelActionRaw !== "plan" && sentinelActionRaw !== "status" && sentinelActionRaw !== "image" && sentinelActionRaw !== "control-plane") {
throw new Error("web-probe sentinel usage: sentinel plan|status|image|control-plane --node NODE --lane vNN [--dry-run|--confirm]");
}
assertKnownOptions(args, new Set(["--node", "--lane"]), new Set(["--dry-run"]));
assertKnownOptions(args, new Set(["--node", "--lane", "--timeout-seconds"]), new Set(["--dry-run", "--confirm", "--wait"]));
const node = requiredOption(args, "--node");
assertNodeId(node);
const lane = requiredOption(args, "--lane");
assertLane(lane);
if (!isHwlabRuntimeLane(lane)) throw new Error(`web-probe only supports HWLAB runtime lanes, got ${lane}`);
const confirm = args.includes("--confirm");
const dryRun = args.includes("--dry-run");
if (confirm && dryRun) throw new Error("web-probe sentinel accepts only one of --confirm or --dry-run");
const timeoutSeconds = positiveIntegerOption(args, "--timeout-seconds", 60, 3600);
let sentinel: WebProbeSentinelOptions;
if (sentinelActionRaw === "plan" || sentinelActionRaw === "status") {
sentinel = { kind: "config", action: sentinelActionRaw, node, lane, dryRun };
} else if (sentinelActionRaw === "image") {
const imageAction = args[1];
if (imageAction !== "status" && imageAction !== "build") throw new Error("web-probe sentinel image usage: image status|build --node NODE --lane vNN [--dry-run|--confirm]");
sentinel = { kind: "image", action: imageAction, node, lane, dryRun: imageAction === "build" ? dryRun || !confirm : dryRun, confirm, wait: args.includes("--wait"), timeoutSeconds };
} else {
const controlPlaneAction = args[1];
if (controlPlaneAction !== "plan" && controlPlaneAction !== "apply" && controlPlaneAction !== "status" && controlPlaneAction !== "trigger-current") {
throw new Error("web-probe sentinel control-plane usage: control-plane plan|apply|status|trigger-current --node NODE --lane vNN [--dry-run|--confirm]");
}
sentinel = { kind: "control-plane", action: controlPlaneAction, node, lane, dryRun: controlPlaneAction === "apply" || controlPlaneAction === "trigger-current" ? dryRun || !confirm : dryRun, confirm, wait: args.includes("--wait"), timeoutSeconds };
}
return {
action: "sentinel",
sentinelAction: sentinelActionRaw,
sentinel,
node,
lane,
dryRun: args.includes("--dry-run"),
};
}
@@ -7687,7 +7703,7 @@ function runNodeWebProbe(options: NodeWebProbeOptions): Record<string, unknown>
const lane = options.lane;
if (!isHwlabRuntimeLane(lane)) throw new Error(`web-probe only supports HWLAB runtime lanes, got ${lane}`);
const spec = hwlabRuntimeLaneSpecForNode(lane, options.node);
if (options.action === "sentinel") return withWebProbeSentinelConfigRendered(webProbeSentinelConfigPlan(spec, options.sentinelAction));
if (options.action === "sentinel") return runWebProbeSentinelCommand(spec, options.sentinel);
if (options.action === "observe" && options.observeAction !== "start") return runNodeWebProbeObserve(options, spec, null, null, null);
const secretSpec = runtimeSecretSpec({ node: options.node, lane });
const material = readBootstrapAdminPasswordMaterial(secretSpec);