fix: 收敛 PostgreSQL 计划输出

This commit is contained in:
Codex
2026-07-13 20:57:05 +02:00
parent 9b6921adeb
commit 88eb8fdb6c
2 changed files with 138 additions and 3 deletions
@@ -39,6 +39,9 @@
#### R1.5.2 [in_progress]
扩展 NC01 共享 observability,使 OTel/Tempo 与 Prometheus 由同一 YAML-first 控制面管理并提供跨 namespace 指标抓取和有界查询,执行记录见 [pikasTech/unidesk#1956](https://github.com/pikasTech/unidesk/issues/1956),完成任务后将详细报告写入[任务报告](./details/pikaoa-enterprise-platform/R1.5.2_Task_Report.md)。
#### R1.5.3 [in_progress]
修复 `platform-db postgres plan` 默认 30KB JSON 被输出预算截断的可见性缺陷,提供数据库、角色、Secret/export、检查与下一步的紧凑 CLI-first 投影,执行记录沿用 [主 issue](https://github.com/pikasTech/unidesk/issues/1952),完成任务后将详细报告写入[任务报告](./details/pikaoa-enterprise-platform/R1.5.3_Task_Report.md)。
### R1.6
审核并合并双仓 PR,执行首次受控 PaC bootstrap,由正常 source PR merge 自动发布,依赖 R1.3-R1.5,完成任务后将详细报告写入[任务报告](./details/pikaoa-enterprise-platform/R1.6_Task_Report.md)。
+135 -3
View File
@@ -302,7 +302,7 @@ export function platformDbHelp(): unknown {
command: "platform-db postgres plan|status|apply|export-secrets",
output: "json",
usage: [
`bun scripts/cli.ts platform-db postgres plan --config ${defaultConfigPath}`,
`bun scripts/cli.ts platform-db postgres plan --config ${defaultConfigPath} [--full|--raw]`,
`bun scripts/cli.ts platform-db postgres status --config ${defaultConfigPath} [--full|--raw]`,
`bun scripts/cli.ts platform-db postgres export-secrets --config ${defaultConfigPath} --dry-run`,
`bun scripts/cli.ts platform-db postgres export-secrets --config ${defaultConfigPath} --confirm`,
@@ -439,14 +439,15 @@ async function plan(config: UniDeskConfig, options: PlatformDbOptions): Promise<
const remote = await remoteFacts(config, pg, null);
const facts = remote.parsed;
const checks = facts === null ? [] : desiredChecks(pg, facts, secrets);
return {
const desired = desiredSummary(pg, secrets, facts);
const result = {
ok: remote.capture.exitCode === 0 && facts !== null && secrets.ok,
action: "platform-db-postgres-plan",
mutation: false,
config: configSummary(pg),
secrets: secretSummary(secrets),
remoteFacts: facts,
desired: desiredSummary(pg, secrets, facts),
desired,
checks,
next: {
apply: `bun scripts/cli.ts platform-db postgres apply --config ${pg.configPath} --confirm`,
@@ -456,6 +457,8 @@ async function plan(config: UniDeskConfig, options: PlatformDbOptions): Promise<
remote: compactCapture(remote.capture, { full: options.full || remote.capture.exitCode !== 0 }),
...(options.raw ? { raw: remote.capture } : {}),
};
if (options.full) return result;
return compactPlan(pg, secrets, facts, checks, remote.capture, result.ok);
}
async function status(config: UniDeskConfig, options: PlatformDbOptions): Promise<Record<string, unknown>> {
@@ -1071,6 +1074,135 @@ function configSummary(pg: PostgresHostConfig): Record<string, unknown> {
};
}
function compactPlan(
pg: PostgresHostConfig,
secrets: SecretInspection,
facts: RemoteFacts | null,
checks: Array<Record<string, unknown>>,
capture: SshCaptureResult,
ok: boolean,
): Record<string, unknown> {
const observedRoles = facts?.postgres.roles ?? [];
const observedDatabases = facts?.postgres.databases ?? [];
return {
ok,
action: "platform-db-postgres-plan",
mutation: false,
target: {
node: pg.node.id,
route: pg.node.route,
mode: pg.node.mode,
},
config: {
path: pg.configPath,
id: pg.metadata.id,
pgVersion: pg.postgres.package.version,
connectionHost: pg.postgres.network.connectionHost,
publicDns: pg.postgres.network.publicDns,
port: pg.postgres.network.port,
transport: pg.postgres.network.transport,
sslmode: pg.postgres.network.sslmode,
},
roles: pg.objects.roles.map((role) => ({
name: role.name,
exists: observedRoles.find((item) => item.name === role.name)?.exists ?? null,
action: observedRoles.some((item) => item.name === role.name && item.exists) ? "none" : "create-or-update",
})),
databases: pg.objects.databases.map((database) => ({
name: database.name,
owner: database.owner,
exists: observedDatabases.find((item) => item.name === database.name)?.exists ?? null,
action: observedDatabases.some((item) => item.name === database.name && item.exists) ? "none" : "create",
})),
secrets: {
ok: secrets.ok,
root: secrets.root,
entries: secrets.entries.map((entry) => ({
sourceRef: entry.sourceRef,
missingKeys: entry.missingKeys,
action: entry.action,
})),
valuesPrinted: false,
},
exports: pg.exports.connectionStrings.map((item) => ({
name: item.name,
sourceRef: item.sourceSecretRef,
targetRef: item.writeToSecretSource.sourceRef,
key: item.writeToSecretSource.key,
consumerScopes: item.consumers.map((consumer) => consumer.scope),
})),
checks: {
ok: checks.every((check) => check.ok === true),
passed: checks.filter((check) => check.ok === true).length,
total: checks.length,
items: checks.map((check) => ({
name: check.name,
ok: check.ok,
...(check.ok === true ? {} : { detail: compactText(JSON.stringify(check)).slice(0, 480) }),
})),
},
remoteFacts: compactRemoteFacts(facts),
remote: compactPlanCapture(capture),
next: {
apply: `bun scripts/cli.ts platform-db postgres apply --config ${pg.configPath} --confirm`,
status: `bun scripts/cli.ts platform-db postgres status --config ${pg.configPath}`,
full: `bun scripts/cli.ts platform-db postgres plan --config ${pg.configPath} --full`,
raw: `bun scripts/cli.ts platform-db postgres plan --config ${pg.configPath} --raw`,
issues: pg.metadata.relatedIssues.map((issue) => `https://github.com/pikasTech/unidesk/issues/${issue}`),
},
disclosure: {
compact: true,
omitted: ["package", "host policy", "TLS paths", "backup", "full remote facts"],
fullAvailable: true,
rawAvailable: true,
},
valuesPrinted: false,
};
}
function compactRemoteFacts(facts: RemoteFacts | null): Record<string, unknown> | null {
if (facts === null) return null;
return {
ok: facts.ok,
observedAt: facts.observedAt,
host: {
hostname: facts.host.hostname,
cpuCount: facts.host.cpuCount,
memoryTotalMiB: facts.host.memoryTotalMiB,
swapTotalMiB: facts.host.swapTotalMiB,
rootAvailGiB: facts.host.rootAvailGiB,
targetDataAvailGiB: facts.host.targetDataAvailGiB,
},
network: {
port5432Listening: facts.network.port5432Listening,
dns: facts.network.dns,
},
repo: {
reachable: facts.repo.reachable,
releaseUrl: facts.repo.releaseUrl,
},
postgres: {
packageInstalled: facts.postgres.packageInstalled,
serviceActive: facts.postgres.serviceActive,
serviceEnabled: facts.postgres.serviceEnabled,
roleCount: facts.postgres.roles.length,
databaseCount: facts.postgres.databases.length,
serverVersion: facts.postgres.serverVersion,
sslOn: facts.postgres.sslOn,
},
};
}
function compactPlanCapture(result: SshCaptureResult): Record<string, unknown> {
return {
exitCode: result.exitCode,
stdoutBytes: Buffer.byteLength(result.stdout, "utf8"),
stderrBytes: Buffer.byteLength(result.stderr, "utf8"),
stdoutTail: result.exitCode === 0 ? "" : result.stdout.slice(-1200),
stderrTail: result.exitCode === 0 ? "" : result.stderr.slice(-1200),
};
}
function requireMonitorResetConfig(pg: PostgresHostConfig): MonitorResetConfig {
const target = pg.managedOperations?.monitorReset;
if (target === undefined) throw new Error(`${pg.configPath}.managedOperations.monitorReset is required`);