Merge pull request #2665 from pikasTech/fix/public-edge-compact-status
Pipelines as Code CI / hwlab-web-probe-sentinel-nc01- Success
Pipelines as Code CI / platform-infra-gitea-nc01- Success
Pipelines as Code CI / unidesk-host- Success

收敛 public-edge status 默认输出并提供显式下钻
This commit is contained in:
Lyon
2026-07-20 17:10:04 +08:00
committed by GitHub
3 changed files with 150 additions and 2 deletions
+5
View File
@@ -182,6 +182,11 @@ bun scripts/cli.ts hwlab nodes control-plane legacy-cicd --help
health path 或 expected A 与稳定错误码;
- public-edge 失败定位只执行一次 `status`
禁止再串联 plan、临时脚本或额外 `trans` probe
- 默认 target status 必须返回有界摘要并直接披露 `firstFailure`
禁止触发 `outputTruncated` 或依赖临时 dump
- 默认摘要必须包含 authority、installed/desired commit、source relation、
managed/Compose fingerprint、endpoint 健康和 warning 计数;
- 站点详情使用 `--site <site-id>`,完整对象只通过显式 `--full` 获取;
- 不披露 Secret、token 或 archive payload。
- public-edge 专站状态:
- 领域服务统一调用
+6
View File
@@ -129,6 +129,10 @@
health path 或 expected A 与稳定错误码;
- `status` 是失败站点定位的唯一首选入口,
禁止再组合 plan、临时脚本和额外 `trans` probe
- 默认 target 状态只返回有界摘要,必须直接包含 authority、运行 commit、
managed/Compose fingerprint、endpoint 健康和 `firstFailure`
不得触发 `outputTruncated`
- 单站点详情使用 `--site <site-id>`,一次性完整对象使用显式 `--full`
- 输出保持 `valuesPrinted=false`,不披露 Secret 或 archive payload。
- 受控入口:
@@ -136,6 +140,8 @@
bun scripts/cli.ts platform-infra public-edge plan --target NC01
bun scripts/cli.ts platform-infra public-edge apply --target NC01 --dry-run
bun scripts/cli.ts platform-infra public-edge status --target NC01
bun scripts/cli.ts platform-infra public-edge status --target NC01 --site <site-id>
bun scripts/cli.ts platform-infra public-edge status --target NC01 --full
```
### 异地节点只读 Artifact Registry
+139 -2
View File
@@ -30,6 +30,7 @@ interface PublicEdgeOptions {
authorityId: string | null;
sourceCommit: string | null;
ancestryFile: string | null;
full: boolean;
}
interface PublicEdgeDelivery {
@@ -127,6 +128,7 @@ export function publicEdgeHelp(): Record<string, unknown> {
"bun scripts/cli.ts platform-infra public-edge apply --target NC01 --dry-run",
"bun scripts/cli.ts platform-infra public-edge status --target NC01",
"bun scripts/cli.ts platform-infra public-edge status --target NC01 --site superapi",
"bun scripts/cli.ts platform-infra public-edge status --target NC01 --full",
],
source: "config/platform-infra/public-edge.yaml",
guarantees: [
@@ -161,7 +163,11 @@ export async function runPlatformInfraPublicEdgeCommand(config: UniDeskConfig, a
}
if (options.action === "status") {
const result = await status(config, options.configPath, delivery, target, resolution);
const visible = options.siteId === null ? result : narrowStatusToSite(result, options.siteId);
const visible = options.siteId !== null
? narrowStatusToSite(result, options.siteId)
: options.full
? result
: compactStatus(result);
return renderMachine("platform-infra public-edge status", visible, "json", visible.ok === true);
}
if (resolution.sites.length === 0) {
@@ -191,6 +197,7 @@ function parseOptions(args: string[]): PublicEdgeOptions {
let authorityId: string | null = null;
let sourceCommit: string | null = null;
let ancestryFile: string | null = null;
let full = false;
for (let index = 1; index < args.length; index += 1) {
const arg = args[index]!;
if (arg === "--config" || arg === "--target" || arg === "--site" || arg === "--authority" || arg === "--source-commit" || arg === "--ancestry-file") {
@@ -207,6 +214,8 @@ function parseOptions(args: string[]): PublicEdgeOptions {
dryRun = true;
} else if (arg === "--confirm") {
confirm = true;
} else if (arg === "--full") {
full = true;
} else {
throw inputError(`不支持的 public-edge 参数:${arg}`, "unsupported-option", arg);
}
@@ -226,7 +235,10 @@ function parseOptions(args: string[]): PublicEdgeOptions {
if (siteId !== null && action !== "status") {
throw inputError("--site 只适用于 status", "site-filter-not-allowed", "--site");
}
return { action, configPath, targetId, siteId, dryRun, confirm, authorityId, sourceCommit, ancestryFile };
if (full && action !== "status") {
throw inputError("--full 只适用于 status", "full-disclosure-not-allowed", "--full");
}
return { action, configPath, targetId, siteId, dryRun, confirm, authorityId, sourceCommit, ancestryFile, full };
}
function readConfig(configPath: string): Record<string, unknown> {
@@ -698,6 +710,131 @@ function narrowStatusToSite(result: Record<string, unknown>, siteId: string): Re
};
}
function compactStatus(result: Record<string, unknown>): Record<string, unknown> {
const runtime = optionalRecord(result.runtime) ?? {};
const target = optionalRecord(result.target) ?? {};
const authority = optionalRecord(result.authority) ?? {};
const provenance = optionalRecord(runtime.provenance) ?? {};
const installed = optionalRecord(provenance.installed) ?? {};
const config = optionalRecord(runtime.config) ?? {};
const dns = optionalRecord(runtime.dns) ?? {};
const probes = optionalRecord(runtime.siteProbes) ?? {};
const sites = Array.isArray(result.sites) ? result.sites : [];
const unresolved = Array.isArray(result.unresolved) ? result.unresolved : [];
const dnsFailures = Array.isArray(dns.failures) ? dns.failures : [];
const probeFailures = Array.isArray(probes.failures) ? probes.failures : [];
const declaredWarnings = Array.isArray(result.warnings) ? result.warnings : [];
const warnings = [...declaredWarnings, ...dnsFailures];
const listenerPorts = Array.isArray(target.listeners) ? target.listeners : [];
const check = (key: string): { ok: boolean; exitCode: number | null } => {
const exitCode = optionalRecord(runtime[key])?.exitCode;
return { ok: exitCode === 0, exitCode: typeof exitCode === "number" ? exitCode : null };
};
return {
ok: result.ok === true,
action: result.action,
mutation: false,
target: {
id: target.id ?? null,
publicAddress: target.publicAddress ?? null,
containerName: target.containerName ?? null,
endpoints: listenerPorts.map((port) => ({
scheme: port === 443 ? "https" : port === 80 ? "http" : "tcp",
port,
listenerReady: check("tcpListeners").ok,
})),
},
authority: {
id: authority.id ?? null,
sourceBranch: authority.sourceBranch ?? null,
pipelineId: authority.pipelineId ?? null,
},
configRef: result.configRef,
complete: result.complete === true,
siteSummary: {
resolved: sites.length,
unresolved: unresolved.length,
probeFailures: probeFailures.length,
dnsWarnings: dnsFailures.length,
},
runtime: {
ok: runtime.ok === true,
checks: {
container: check("container"),
tcpListeners: check("tcpListeners"),
preservedUdpListeners: check("preservedUdpListeners"),
caddyValidate: check("validate"),
legacyRetired: optionalRecord(runtime.legacyContainer)?.retired === true,
siteProbes: { ok: probes.exitCode === 0, exitCode: typeof probes.exitCode === "number" ? probes.exitCode : null },
},
source: {
installedCommit: installed.sourceCommit ?? null,
desiredCommit: provenance.desiredSourceCommit ?? null,
relation: provenance.sourceRelation ?? null,
acceptable: provenance.sourceAcceptable === true,
matchesRuntime: provenance.matchesRuntime === true,
},
fingerprints: {
managedConfig: {
current: config.currentManagedFingerprint ?? null,
desired: config.desiredManagedFingerprint ?? null,
},
compose: {
current: config.currentComposeFingerprint ?? null,
desired: config.desiredComposeFingerprint ?? null,
},
matchesDesired: config.matchesDesired === true,
},
},
firstFailure: firstStatusFailure(runtime),
warningCount: warnings.length,
warning: warnings.length === 0 ? null : compactStatusWarning(warnings[0]),
next: {
site: `bun scripts/cli.ts platform-infra public-edge status --target ${String(target.id ?? "NC01")} --site <site-id>`,
full: `bun scripts/cli.ts platform-infra public-edge status --target ${String(target.id ?? "NC01")} --full`,
},
valuesPrinted: false,
};
}
function firstStatusFailure(runtime: Record<string, unknown>): Record<string, unknown> | null {
if (optionalRecord(runtime.capture) !== null) {
return { stage: "remoteStatus", code: "public-edge-status-capture-failed" };
}
const failures: Array<[string, boolean, string]> = [
["container", optionalRecord(runtime.container)?.exitCode !== 0, "public-edge-container-not-running"],
["legacyContainer", optionalRecord(runtime.legacyContainer)?.retired !== true, "public-edge-legacy-container-active"],
["tcpListeners", optionalRecord(runtime.tcpListeners)?.exitCode !== 0, "public-edge-tcp-listener-not-ready"],
["preservedUdpListeners", optionalRecord(runtime.preservedUdpListeners)?.exitCode !== 0, "public-edge-preserved-udp-listener-not-ready"],
["caddyValidate", optionalRecord(runtime.validate)?.exitCode !== 0, "public-edge-caddy-invalid"],
["provenance", optionalRecord(runtime.provenance)?.matchesRuntime !== true, "public-edge-runtime-provenance-mismatch"],
["sourceCommit", optionalRecord(runtime.provenance)?.sourceAcceptable !== true, "public-edge-source-commit-unacceptable"],
["config", optionalRecord(runtime.config)?.matchesDesired !== true, "public-edge-config-drift"],
["siteProbes", optionalRecord(runtime.siteProbes)?.exitCode !== 0, "public-edge-site-probe-failed"],
];
const failure = failures.find(([, failed]) => failed);
if (failure === undefined) return null;
const result: Record<string, unknown> = { stage: failure[0], code: failure[2] };
if (failure[0] === "siteProbes") {
const failures = optionalRecord(runtime.siteProbes)?.failures;
if (Array.isArray(failures) && failures.length > 0) result.site = compactStatusWarning(failures[0]);
}
return result;
}
function compactStatusWarning(value: unknown): Record<string, unknown> {
const warning = optionalRecord(value) ?? {};
return {
code: warning.code ?? "public-edge-warning",
siteId: warning.siteId ?? warning.id ?? null,
hostname: warning.hostname ?? null,
path: warning.path ?? null,
expectedA: warning.expectedA ?? null,
detail: typeof warning.detail === "string" ? warning.detail.slice(0, 240) : null,
blocking: warning.blocking === true,
};
}
export function renderPublicEdgeArtifacts(target: PublicEdgeTarget, sites: ResolvedSite[]): { caddyfile: string; compose: string } {
const protocols = target.listener.protocols.join(" ");
const global = renderManagedBlock("public-edge-global", `\{\n\tservers \{\n\t\tprotocols ${protocols}\n\t\}\n\}`);