fix: unify node git mirror apply path
This commit is contained in:
@@ -30,6 +30,7 @@ bun scripts/cli.ts hwlab g14 control-plane apply --lane v02 [--dry-run|--confirm
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
bun scripts/cli.ts hwlab nodes control-plane status --node <node> --lane v03 [--pipeline-run <name>|--source-commit <sha>] [--full|--raw]
|
bun scripts/cli.ts hwlab nodes control-plane status --node <node> --lane v03 [--pipeline-run <name>|--source-commit <sha>] [--full|--raw]
|
||||||
|
bun scripts/cli.ts hwlab nodes control-plane apply --node <node> --lane v03 --confirm
|
||||||
bun scripts/cli.ts hwlab nodes control-plane trigger-current --node <node> --lane v03 --confirm --wait
|
bun scripts/cli.ts hwlab nodes control-plane trigger-current --node <node> --lane v03 --confirm --wait
|
||||||
bun scripts/cli.ts hwlab nodes control-plane sync --node <node> --lane v03 --confirm
|
bun scripts/cli.ts hwlab nodes control-plane sync --node <node> --lane v03 --confirm
|
||||||
bun scripts/cli.ts hwlab nodes control-plane refresh --node <node> --lane v03 --confirm
|
bun scripts/cli.ts hwlab nodes control-plane refresh --node <node> --lane v03 --confirm
|
||||||
@@ -37,6 +38,8 @@ bun scripts/cli.ts hwlab nodes control-plane refresh --node <node> --lane v03 --
|
|||||||
|
|
||||||
`status` 默认返回 compact summary,只保留 source commit、PipelineRun、Argo、runtime readiness、public probe、git mirror 和 next action;完整 expected YAML/render target、kubectl result tail、Secret/sourceRef 详情和 probe 原始结果只在 `--full` 或 `--raw` 下展开。
|
`status` 默认返回 compact summary,只保留 source commit、PipelineRun、Argo、runtime readiness、public probe、git mirror 和 next action;完整 expected YAML/render target、kubectl result tail、Secret/sourceRef 详情和 probe 原始结果只在 `--full` 或 `--raw` 下展开。
|
||||||
|
|
||||||
|
`control-plane apply --confirm` 是 node/lane 控制面更新入口:先从 `config/hwlab-node-control-plane.yaml` 收敛 node-local infra、git-mirror ConfigMap、RBAC、Tekton/Argo 前置对象,再应用 source-render 的 runtime namespace、Pipeline 和 Argo Application。node-scoped git-mirror 脚本只有 infra manifest 是权威来源;不要把 HWLAB source render 生成的 `devops-infra/git-mirror.yaml` 当作正式 apply 来源。
|
||||||
|
|
||||||
`trigger-current --confirm --wait` 是 node/lane CI/CD 一键入口:按 YAML 解析 source head,执行 k8s git-mirror source snapshot sync 和必要 pre-flush,刷新 control-plane,创建或复用 commit-pinned PipelineRun,等待 PipelineRun 终态,并在终态成功后继续执行 post-flush、GitOps/Argo、runtime readiness 和 public `/health` closeout。
|
`trigger-current --confirm --wait` 是 node/lane CI/CD 一键入口:按 YAML 解析 source head,执行 k8s git-mirror source snapshot sync 和必要 pre-flush,刷新 control-plane,创建或复用 commit-pinned PipelineRun,等待 PipelineRun 终态,并在终态成功后继续执行 post-flush、GitOps/Argo、runtime readiness 和 public `/health` closeout。
|
||||||
|
|
||||||
120 秒是同一条 CLI 的端到端预算,不是每个子阶段各等一轮。嵌套 git-mirror sync/flush 必须按剩余预算裁剪并跳过多次 retry;状态探测也必须按剩余预算收窄。超预算时 CLI 返回 `pending` warning,输出阶段分解、PipelineRun 状态、GitOps flush revision、Argo observed/target revision、runtime/public readiness、pending TaskRun/Pod drill-down 和下一条受控 status 命令。PipelineRun 已成功但 Argo/runtime/public 仍在收敛时,状态原因应落在 Argo/runtime closeout,不再泛化成 CI TaskRun pending。
|
120 秒是同一条 CLI 的端到端预算,不是每个子阶段各等一轮。嵌套 git-mirror sync/flush 必须按剩余预算裁剪并跳过多次 retry;状态探测也必须按剩余预算收窄。超预算时 CLI 返回 `pending` warning,输出阶段分解、PipelineRun 状态、GitOps flush revision、Argo observed/target revision、runtime/public readiness、pending TaskRun/Pod drill-down 和下一条受控 status 命令。PipelineRun 已成功但 Argo/runtime/public 仍在收敛时,状态原因应落在 Argo/runtime closeout,不再泛化成 CI TaskRun pending。
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ bun scripts/cli.ts hwlab nodes git-mirror flush --node <node> --lane v03 --confi
|
|||||||
- `sync`: 把当前配置声明的 GitHub refs 拉入本地 mirror,并为 source branch tip 创建不可变 snapshot ref。
|
- `sync`: 把当前配置声明的 GitHub refs 拉入本地 mirror,并为 source branch tip 创建不可变 snapshot ref。
|
||||||
- `flush`: 把本地 lane GitOps ref 快进推回 GitHub。
|
- `flush`: 把本地 lane GitOps ref 快进推回 GitHub。
|
||||||
|
|
||||||
|
Node-scoped `hwlab nodes control-plane apply --node <node> --lane v03 --confirm` 会先执行同一套 YAML-first infra apply 来收敛 git-mirror;git-mirror ConfigMap、proxy、transport 和 Secret 绑定以 `config/hwlab-node-control-plane.yaml` 为权威。HWLAB source render 产出的 `devops-infra/git-mirror.yaml` 不是 node-scoped 正式 apply 来源,旧产物漂移时修 infra render 或显式 `hwlab nodes control-plane infra apply`,不要补 host worktree 或双路径兼容。
|
||||||
|
|
||||||
`cicd branch-follower` 已把 `sync` 和 `flush` 作为通用 Kubernetes-native stage 接入 HWLAB/AgentRun 自动跟随:trigger 前先 sync 并创建 immutable source snapshot,GitOps publish 后必须 flush,status/closeout 以 mirror cache ref 和 `pendingFlush=false`/`githubInSync=true` 为准。正常自动跟随不需要 operator 手动清理旧状态;旧 ConfigMap 或历史 PipelineRun 干扰时只用 `cicd branch-follower cleanup-state --follower <id> --confirm` 做显式 cleanup。
|
`cicd branch-follower` 已把 `sync` 和 `flush` 作为通用 Kubernetes-native stage 接入 HWLAB/AgentRun 自动跟随:trigger 前先 sync 并创建 immutable source snapshot,GitOps publish 后必须 flush,status/closeout 以 mirror cache ref 和 `pendingFlush=false`/`githubInSync=true` 为准。正常自动跟随不需要 operator 手动清理旧状态;旧 ConfigMap 或历史 PipelineRun 干扰时只用 `cicd branch-follower cleanup-state --follower <id> --confirm` 做显式 cleanup。
|
||||||
|
|
||||||
GitHub/GitHub SSH/HTTPS 相关 egress 必须从 YAML-first host proxy 配置进入 git-mirror Job/Pod:node-scoped lanes 读取 `config/hwlab-node-control-plane.yaml#nodes.<NODE>.egressProxy` 和 `targets.<id>.gitMirror.egressProxy/githubTransport`,branch-follower controller 读取 `config/cicd-branch-followers.yaml#controller.source.githubSsh`。状态和日志应披露 `source=yaml`、proxy mode、object/key/sourceRef 摘要和 redacted fingerprint,不得依赖未声明的 host env、trans proxy、裸直连 GitHub 或 CLI 输出解析。
|
GitHub/GitHub SSH/HTTPS 相关 egress 必须从 YAML-first host proxy 配置进入 git-mirror Job/Pod:node-scoped lanes 读取 `config/hwlab-node-control-plane.yaml#nodes.<NODE>.egressProxy` 和 `targets.<id>.gitMirror.egressProxy/githubTransport`,branch-follower controller 读取 `config/cicd-branch-followers.yaml#controller.source.githubSsh`。状态和日志应披露 `source=yaml`、proxy mode、object/key/sourceRef 摘要和 redacted fingerprint,不得依赖未声明的 host env、trans proxy、裸直连 GitHub 或 CLI 输出解析。
|
||||||
|
|||||||
@@ -2039,6 +2039,7 @@ function compactNativePayload(payload: Record<string, unknown> | null): Record<s
|
|||||||
if (payload === null) return null;
|
if (payload === null) return null;
|
||||||
return {
|
return {
|
||||||
source: compactSourcePayload(asOptionalRecord(payload.source)),
|
source: compactSourcePayload(asOptionalRecord(payload.source)),
|
||||||
|
sourceSync: compactSourceSyncPayload(asOptionalRecord(payload.sourceSync)),
|
||||||
gitMirror: asOptionalRecord(payload.gitMirror),
|
gitMirror: asOptionalRecord(payload.gitMirror),
|
||||||
tekton: asOptionalRecord(payload.tekton),
|
tekton: asOptionalRecord(payload.tekton),
|
||||||
taskRuns: compactTaskRunsPayload(asOptionalRecord(payload.taskRuns)),
|
taskRuns: compactTaskRunsPayload(asOptionalRecord(payload.taskRuns)),
|
||||||
@@ -2051,6 +2052,25 @@ function compactNativePayload(payload: Record<string, unknown> | null): Record<s
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function compactSourceSyncPayload(value: Record<string, unknown> | null): Record<string, unknown> | null {
|
||||||
|
if (value === null) return null;
|
||||||
|
return {
|
||||||
|
ok: value.ok === true,
|
||||||
|
completed: value.completed === true,
|
||||||
|
failed: value.failed === true,
|
||||||
|
timedOut: value.timedOut === true,
|
||||||
|
created: value.created === true,
|
||||||
|
reused: value.reused === true,
|
||||||
|
jobName: stringOrNull(value.jobName),
|
||||||
|
namespace: stringOrNull(value.namespace),
|
||||||
|
elapsedMs: numberOrNull(value.elapsedMs),
|
||||||
|
conditionReason: stringOrNull(value.conditionReason),
|
||||||
|
conditionMessage: stringOrNull(value.conditionMessage),
|
||||||
|
statusAuthority: stringOrNull(value.statusAuthority),
|
||||||
|
parsedDownstreamCliOutput: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function compactTaskRunsPayload(taskRuns: Record<string, unknown> | null): Record<string, unknown> | null {
|
function compactTaskRunsPayload(taskRuns: Record<string, unknown> | null): Record<string, unknown> | null {
|
||||||
if (taskRuns === null) return null;
|
if (taskRuns === null) return null;
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -343,6 +343,29 @@ export function nodeRuntimeApply(scoped: ReturnType<typeof parseNodeScopedDelega
|
|||||||
degradedReason: "node-runtime-base-image-seed-failed",
|
degradedReason: "node-runtime-base-image-seed-failed",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
const infra = runHwlabNodeControlPlaneInfra([
|
||||||
|
"apply",
|
||||||
|
"--node", scoped.node,
|
||||||
|
"--lane", scoped.lane,
|
||||||
|
scoped.dryRun ? "--dry-run" : "--confirm",
|
||||||
|
"--timeout-seconds", String(scoped.timeoutSeconds),
|
||||||
|
]);
|
||||||
|
if (!infraResultOk(infra)) {
|
||||||
|
return {
|
||||||
|
ok: false,
|
||||||
|
command: `hwlab nodes control-plane apply --node ${scoped.node} --lane ${scoped.lane}`,
|
||||||
|
node: scoped.node,
|
||||||
|
lane: scoped.lane,
|
||||||
|
mode: scoped.dryRun ? "dry-run" : "confirmed-apply",
|
||||||
|
phase: "control-plane-infra-apply",
|
||||||
|
sourceCommit,
|
||||||
|
localPostgres,
|
||||||
|
secrets,
|
||||||
|
baseImage,
|
||||||
|
infra,
|
||||||
|
degradedReason: "node-runtime-control-plane-infra-apply-failed",
|
||||||
|
};
|
||||||
|
}
|
||||||
const render = renderNodeRuntimeControlPlane(spec, sourceCommit, scoped.timeoutSeconds);
|
const render = renderNodeRuntimeControlPlane(spec, sourceCommit, scoped.timeoutSeconds);
|
||||||
if (!isCommandSuccess(render.result)) {
|
if (!isCommandSuccess(render.result)) {
|
||||||
return {
|
return {
|
||||||
@@ -356,6 +379,7 @@ export function nodeRuntimeApply(scoped: ReturnType<typeof parseNodeScopedDelega
|
|||||||
localPostgres,
|
localPostgres,
|
||||||
secrets,
|
secrets,
|
||||||
baseImage,
|
baseImage,
|
||||||
|
infra,
|
||||||
renderDir: render.renderDir,
|
renderDir: render.renderDir,
|
||||||
renderLocation: render.location,
|
renderLocation: render.location,
|
||||||
render: compactRuntimeCommand(render.result),
|
render: compactRuntimeCommand(render.result),
|
||||||
@@ -381,6 +405,7 @@ export function nodeRuntimeApply(scoped: ReturnType<typeof parseNodeScopedDelega
|
|||||||
localPostgres,
|
localPostgres,
|
||||||
secrets,
|
secrets,
|
||||||
baseImage,
|
baseImage,
|
||||||
|
infra,
|
||||||
renderDir: render.renderDir,
|
renderDir: render.renderDir,
|
||||||
renderLocation: render.location,
|
renderLocation: render.location,
|
||||||
render: compactRuntimeCommand(render.result),
|
render: compactRuntimeCommand(render.result),
|
||||||
@@ -397,6 +422,10 @@ export function nodeRuntimeApply(scoped: ReturnType<typeof parseNodeScopedDelega
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function infraResultOk(value: Record<string, unknown> | RenderedCliResult): boolean {
|
||||||
|
return value.ok !== false;
|
||||||
|
}
|
||||||
|
|
||||||
export function nodeRuntimeRefresh(scoped: ReturnType<typeof parseNodeScopedDelegatedOptions>): Record<string, unknown> {
|
export function nodeRuntimeRefresh(scoped: ReturnType<typeof parseNodeScopedDelegatedOptions>): Record<string, unknown> {
|
||||||
const spec = scoped.spec;
|
const spec = scoped.spec;
|
||||||
const script = [
|
const script = [
|
||||||
|
|||||||
@@ -176,7 +176,6 @@ export function httpProxyEndpoint(value: string): { host: string; port: number }
|
|||||||
|
|
||||||
export function nodeRuntimeControlPlaneFiles(spec: HwlabRuntimeLaneSpec, renderDir: string): string[] {
|
export function nodeRuntimeControlPlaneFiles(spec: HwlabRuntimeLaneSpec, renderDir: string): string[] {
|
||||||
return [
|
return [
|
||||||
`${renderDir}/devops-infra/git-mirror.yaml`,
|
|
||||||
`${renderDir}/${spec.runtimeRenderDir}/namespace.yaml`,
|
`${renderDir}/${spec.runtimeRenderDir}/namespace.yaml`,
|
||||||
`${renderDir}/${spec.tektonDir}/rbac.yaml`,
|
`${renderDir}/${spec.tektonDir}/rbac.yaml`,
|
||||||
`${renderDir}/${spec.tektonDir}/pipeline.yaml`,
|
`${renderDir}/${spec.tektonDir}/pipeline.yaml`,
|
||||||
|
|||||||
Reference in New Issue
Block a user