fix: use k8s git mirror source snapshots

This commit is contained in:
Codex
2026-07-01 11:03:18 +00:00
parent b972ebd290
commit d687bdf668
20 changed files with 465 additions and 57 deletions
+24 -3
View File
@@ -201,11 +201,31 @@ export async function triggerCurrentYamlLaneConfirmedSteps(config: UniDeskConfig
async function resolveTriggerCurrentSource(config: UniDeskConfig, spec: AgentRunLaneSpec, configPath: string, waited: boolean): Promise<Record<string, unknown> & { ok: boolean; sourceCommit?: string | null; sourcePayload?: Record<string, unknown> }> {
if (spec.source.statusMode === "k3s-git-mirror") {
progressEvent("agentrun.yaml-lane.source-snapshot.progress", {
node: spec.nodeId,
lane: spec.lane,
statusMode: spec.source.statusMode,
status: "syncing",
});
const sourceSync = await runYamlLaneGitMirrorSyncJob(config, spec);
if (sourceSync.ok !== true) {
return {
ok: false,
command: "agentrun control-plane trigger-current",
mode: waited ? "confirmed-waited" : "confirmed-trigger",
configPath,
target: agentRunLaneSummary(spec),
phase: "source-snapshot-sync",
degradedReason: "yaml-lane-k3s-source-snapshot-sync-failed",
result: sourceSync,
valuesPrinted: false,
};
}
progressEvent("agentrun.yaml-lane.source-status.progress", {
node: spec.nodeId,
lane: spec.lane,
statusMode: spec.source.statusMode,
status: "probing",
status: "probing-snapshot",
});
const sourceStatus = await capture(config, spec.nodeKubeRoute, ["sh", "--", yamlLaneK3sSourceStatusScript(spec)]);
const sourcePayload = captureJsonPayload(sourceStatus);
@@ -218,13 +238,14 @@ async function resolveTriggerCurrentSource(config: UniDeskConfig, spec: AgentRun
configPath,
target: agentRunLaneSummary(spec),
phase: "source-status",
degradedReason: "yaml-lane-k3s-source-status-failed",
degradedReason: stringOrNull(sourcePayload.degradedReason) ?? "yaml-lane-k3s-source-status-failed",
gitMirrorSync: sourceSync,
result: sourcePayload,
capture: compactCapture(sourceStatus, { full: true, stdoutTailChars: 5000, stderrTailChars: 5000 }),
valuesPrinted: false,
};
}
return { ok: true, sourceCommit, sourcePayload, valuesPrinted: false };
return { ok: true, sourceCommit, sourcePayload: { ...sourcePayload, gitMirrorSync: sourceSync, valuesPrinted: false }, valuesPrinted: false };
}
progressEvent("agentrun.yaml-lane.source-bootstrap.progress", {
+11 -1
View File
@@ -234,7 +234,7 @@ export async function controlPlanePlan(_config: UniDeskConfig, options: StatusOp
target: agentRunLaneSummary(spec),
plannedChecks: [
"source-branch-exists",
"source-worktree-exists-and-clean",
spec.source.statusMode === "k3s-git-mirror" ? "source-git-mirror-snapshot-present" : "source-worktree-exists-and-clean",
"git-mirror-services-ready",
"ci-namespace-pipeline-serviceaccount",
"argo-application-alignment",
@@ -369,9 +369,11 @@ export async function statusYamlLane(config: UniDeskConfig, options: StatusOptio
...(sourceWorktreeDetached ? ["source-worktree-detached"] : []),
...(sourceBranchAdvanced ? ["source-branch-advanced-after-target"] : []),
];
const sourceSnapshotRequired = spec.source.statusMode === "k3s-git-mirror";
const blockers = [
...(sourceWorkspaceRequired && sourcePayload.workspaceExists !== true ? ["source-worktree-missing"] : []),
...(sourcePayload.remoteBranchExists === true ? [] : ["source-branch-missing"]),
...(sourceSnapshotRequired && sourcePayload.snapshotPresent !== true ? ["source-snapshot-missing"] : []),
...(sourceWorkspaceRequired && sourcePayload.workspaceClean !== true && sourcePayload.workspaceExists === true ? ["source-worktree-dirty"] : []),
...(mirrorPayload.readReady === true ? [] : ["git-mirror-read-not-ready"]),
...(mirrorPayload.writeReady === true ? [] : ["git-mirror-write-not-ready"]),
@@ -426,11 +428,15 @@ export async function statusYamlLane(config: UniDeskConfig, options: StatusOptio
: { code: "inspect-full-status", summary: "alignment is inconclusive; inspect full status details", command: statusFullCommand };
const compactSourceStatus = {
statusMode: spec.source.statusMode,
sourceAuthority: sourcePayload.sourceAuthority ?? spec.source.sourceAuthority?.mode ?? null,
workspaceExists: sourcePayload.workspaceExists ?? false,
workspaceClean: sourcePayload.workspaceClean ?? null,
branch: sourcePayload.branch ?? null,
remoteBranchExists: sourcePayload.remoteBranchExists ?? false,
remoteBranchCommit: sourcePayload.remoteBranchCommit ?? null,
sourceRef: sourcePayload.sourceRef ?? null,
sourceStageRef: sourcePayload.sourceStageRef ?? null,
snapshotPresent: sourcePayload.snapshotPresent ?? null,
workspaceDetached: sourceWorktreeDetached,
};
const compactGitMirrorStatus = {
@@ -499,6 +505,10 @@ export async function statusYamlLane(config: UniDeskConfig, options: StatusOptio
localHead: sourcePayload.localHead ?? null,
remoteBranchExists: sourcePayload.remoteBranchExists ?? false,
remoteBranchCommit: sourcePayload.remoteBranchCommit ?? null,
sourceAuthority: sourcePayload.sourceAuthority ?? spec.source.sourceAuthority?.mode ?? null,
sourceRef: sourcePayload.sourceRef ?? null,
sourceStageRef: sourcePayload.sourceStageRef ?? null,
snapshotPresent: sourcePayload.snapshotPresent ?? null,
},
gitMirror: {
readReady: mirrorPayload.readReady ?? false,
+6 -1
View File
@@ -69,6 +69,11 @@ export function renderAgentRunControlPlaneStatusSummary(result: Record<string, u
const timings = record(result.timings);
const blockers = Array.isArray(summary.blockers) ? summary.blockers.map(String) : [];
const warnings = Array.isArray(summary.warnings) ? summary.warnings.map(String) : [];
const sourceSnapshotMode = source.sourceAuthority === "git-mirror-snapshot" || source.snapshotPresent !== null;
const sourceStatus = sourceSnapshotMode ? yesNo(source.snapshotPresent) : yesNo(source.workspaceClean);
const sourceDetail = sourceSnapshotMode
? `branch=${displayValue(source.branch)} commit=${shortSha(source.remoteBranchCommit)} snapshot=${yesNo(source.snapshotPresent)} stage=${displayValue(source.sourceStageRef ?? "-")}`
: `branch=${displayValue(source.branch)} commit=${shortSha(source.remoteBranchCommit)} detached=${yesNo(source.workspaceDetached)}`;
const lines = [
"AGENTRUN CONTROL-PLANE STATUS",
renderTable(
@@ -88,7 +93,7 @@ export function renderAgentRunControlPlaneStatusSummary(result: Record<string, u
renderTable(
["COMPONENT", "STATUS", "DETAIL"],
[
["source", yesNo(source.workspaceClean), `branch=${displayValue(source.branch)} commit=${shortSha(source.remoteBranchCommit)} detached=${yesNo(source.workspaceDetached)}`],
["source", sourceStatus, sourceDetail],
["git-mirror", yesNo(gitMirror.alreadySynced), `read=${yesNo(gitMirror.readReady)} write=${yesNo(gitMirror.writeReady)} gitops=${shortSha(gitMirror.gitopsCommit)}`],
["ci", yesNo(ciRun.status === "True"), `run=${displayValue(ciRun.name)} status=${displayValue(ciRun.status)} reason=${displayValue(ciRun.reason)} evidenceMissing=${yesNo(ci.evidenceMissing)}`],
["argo", yesNo(argo.syncedToGitops), `sync=${displayValue(argo.syncStatus)} health=${displayValue(argo.healthStatus)} revision=${shortSha(argo.revision)}`],
+12 -2
View File
@@ -18,6 +18,8 @@ import {
agentRunLaneSummary,
agentRunPipelineRunName,
agentRunProviderCredentialRefs,
agentRunSourceSnapshotRef,
agentRunSourceSnapshotStageRefPrefix,
resolveAgentRunLaneTarget,
type AgentRunCancelLifecycleSpec,
type AgentRunLaneSpec,
@@ -294,6 +296,7 @@ export async function runYamlLaneGitMirrorJob(config: UniDeskConfig, spec: Agent
}
export function yamlLanePipelineRunCreateScript(spec: AgentRunLaneSpec, sourceCommit: string, pipelineRun: string): string {
const sourceStageRef = spec.source.statusMode === "k3s-git-mirror" ? agentRunSourceSnapshotRef(spec, sourceCommit) : null;
const manifest = {
apiVersion: "tekton.dev/v1",
kind: "PipelineRun",
@@ -307,6 +310,10 @@ export function yamlLanePipelineRunCreateScript(spec: AgentRunLaneSpec, sourceCo
"agentrun.pikastech.local/source-commit": sourceCommit,
"agentrun.pikastech.local/trigger": "unidesk-yaml-only",
},
annotations: sourceStageRef === null ? undefined : {
"agentrun.pikastech.local/source-authority": "git-mirror-snapshot",
"agentrun.pikastech.local/source-stage-ref": sourceStageRef,
},
},
spec: {
pipelineRef: { name: spec.ci.pipeline },
@@ -545,6 +552,7 @@ export function yamlLaneGitMirrorSyncShell(spec: AgentRunLaneSpec): string {
...yamlLaneGitMirrorSshSetupShellLines(spec),
`repository=${shQuote(spec.source.repository)}`,
`source_branch=${shQuote(spec.source.branch)}`,
`source_stage_ref_prefix=${shQuote(agentRunSourceSnapshotStageRefPrefix(spec))}`,
`gitops_branch=${shQuote(spec.gitops.branch)}`,
"repo=\"/cache/${repository}.git\"",
"remote=\"ssh://git@ssh.github.com:443/${repository}.git\"",
@@ -562,6 +570,8 @@ export function yamlLaneGitMirrorSyncShell(spec: AgentRunLaneSpec): string {
"git --git-dir=\"$repo\" config http.receivepack true",
"timeout 240 git --git-dir=\"$repo\" fetch origin \"+refs/heads/${source_branch}:refs/mirror-stage/heads/${source_branch}\"",
"source_sha=$(git --git-dir=\"$repo\" rev-parse --verify \"refs/mirror-stage/heads/${source_branch}^{commit}\")",
"source_stage_ref=\"${source_stage_ref_prefix%/}/$source_sha\"",
"git --git-dir=\"$repo\" update-ref \"$source_stage_ref\" \"$source_sha\"",
"git --git-dir=\"$repo\" update-ref \"refs/heads/${source_branch}\" \"$source_sha\"",
"gitops_fetch_err=$(mktemp)",
"gitops_remote_missing=false",
@@ -580,8 +590,8 @@ export function yamlLaneGitMirrorSyncShell(spec: AgentRunLaneSpec): string {
" fi",
"fi",
"git --git-dir=\"$repo\" update-server-info",
"SOURCE_SHA=\"$source_sha\" GITOPS_SHA=\"$gitops_sha\" GITOPS_REMOTE_MISSING=\"$gitops_remote_missing\" node <<'NODE'",
"console.log(JSON.stringify({ ok: true, localSource: process.env.SOURCE_SHA, localGitops: process.env.GITOPS_SHA || null, gitopsRemoteMissing: process.env.GITOPS_REMOTE_MISSING === 'true', valuesPrinted: false }));",
"SOURCE_SHA=\"$source_sha\" SOURCE_STAGE_REF=\"$source_stage_ref\" GITOPS_SHA=\"$gitops_sha\" GITOPS_REMOTE_MISSING=\"$gitops_remote_missing\" node <<'NODE'",
"console.log(JSON.stringify({ ok: true, sourceAuthority: 'git-mirror-snapshot', localSource: process.env.SOURCE_SHA, sourceStageRef: process.env.SOURCE_STAGE_REF, localGitops: process.env.GITOPS_SHA || null, gitopsRemoteMissing: process.env.GITOPS_REMOTE_MISSING === 'true', valuesPrinted: false }));",
"NODE",
].join("\n");
}
+30 -8
View File
@@ -42,7 +42,7 @@ import { pathValue } from "./render";
import { startAsyncAgentRunJob } from "./rest-bridge";
import { collectLaneSecretSources, readSecretSourceValue, restartYamlLaneScript, secretSyncScript } from "./secrets";
import { capture, captureJsonPayload, compactCapture, isGitSha, stringOrNull } from "./utils";
import { yamlLaneSourceBootstrapProbeScript } from "./yaml-lane";
import { yamlLaneK3sSourceStatusScript, yamlLaneSourceBootstrapProbeScript } from "./yaml-lane";
export function renderNextObjectLines(next: Record<string, unknown>): string[] {
return Object.values(next)
@@ -409,18 +409,34 @@ export async function triggerCurrent(config: UniDeskConfig, options: TriggerOpti
export async function triggerCurrentYamlLane(config: UniDeskConfig, options: TriggerOptions, target: { configPath: string; spec: AgentRunLaneSpec }): Promise<Record<string, unknown>> {
const spec = target.spec;
const probe = await capture(config, spec.nodeRoute, ["sh", "--", yamlLaneSourceBootstrapProbeScript(spec)]);
const probe = spec.source.statusMode === "k3s-git-mirror"
? await capture(config, spec.nodeKubeRoute, ["sh", "--", yamlLaneK3sSourceStatusScript(spec)])
: await capture(config, spec.nodeRoute, ["sh", "--", yamlLaneSourceBootstrapProbeScript(spec)]);
const source = captureJsonPayload(probe);
const sourceCommit = stringOrNull(source.sourceCommit);
const remoteBranchExists = source.remoteBranchExists === true;
const pipelineRun = sourceCommit !== null && isGitSha(sourceCommit) ? agentRunPipelineRunName(spec, sourceCommit) : null;
const placeholderImage = sourceCommit === null ? null : placeholderAgentRunImage(spec, sourceCommit);
const renderedFiles = placeholderImage === null ? [] : renderAgentRunGitopsFiles(spec, { sourceCommit, image: placeholderImage });
const plan = {
node: spec.nodeId,
lane: spec.lane,
version: spec.version,
source: {
const sourcePlan = spec.source.statusMode === "k3s-git-mirror"
? {
statusMode: spec.source.statusMode,
sourceAuthority: spec.source.sourceAuthority,
sourceSnapshot: spec.source.sourceSnapshot,
repository: spec.source.repository,
branch: spec.source.branch,
remoteBranchExists,
remoteBranchCommit: stringOrNull(source.remoteBranchCommit),
sourceCommit,
sourceRef: stringOrNull(source.sourceRef),
sourceStageRef: stringOrNull(source.sourceStageRef),
snapshotPresent: source.snapshotPresent === true,
degradedReason: stringOrNull(source.degradedReason),
next: source.snapshotPresent === true ? null : `bun scripts/cli.ts agentrun git-mirror sync --node ${spec.nodeId} --lane ${spec.lane} --confirm --wait`,
valuesPrinted: false,
}
: {
statusMode: spec.source.statusMode,
workspace: spec.source.workspace,
remote: spec.source.remote,
branch: spec.source.branch,
@@ -429,7 +445,13 @@ export async function triggerCurrentYamlLane(config: UniDeskConfig, options: Tri
bootstrapPollSeconds: spec.source.bootstrapPollSeconds,
remoteBranchExists,
sourceCommit,
},
valuesPrinted: false,
};
const plan = {
node: spec.nodeId,
lane: spec.lane,
version: spec.version,
source: sourcePlan,
deploymentFormat: spec.deployment.format,
deploymentTruth: "config/agentrun.yaml",
removedServiceDeployJson: true,
+36 -8
View File
@@ -18,6 +18,8 @@ import {
agentRunLaneSummary,
agentRunPipelineRunName,
agentRunProviderCredentialRefs,
agentRunSourceSnapshotRef,
agentRunSourceSnapshotStageRefPrefix,
resolveAgentRunLaneTarget,
type AgentRunCancelLifecycleSpec,
type AgentRunLaneSpec,
@@ -390,21 +392,41 @@ export function yamlLaneK3sSourceStatusScript(spec: AgentRunLaneSpec): string {
`read_deployment=${shQuote(spec.gitMirror.readDeployment)}`,
`repository=${shQuote(spec.source.repository)}`,
`source_branch=${shQuote(spec.source.branch)}`,
`source_stage_ref_prefix=${shQuote(agentRunSourceSnapshotStageRefPrefix(spec))}`,
"repo_path=\"/cache/${repository}.git\"",
"kubectl -n \"$namespace\" get deploy \"$read_deployment\" >/tmp/agentrun-source-read-deploy.txt 2>/dev/null",
"read_exit=$?",
"source_commit=''",
"source_ref=\"refs/mirror-stage/heads/$source_branch\"",
"source_stage_ref=''",
"snapshot_commit=''",
"if [ \"$read_exit\" -eq 0 ]; then",
" source_commit=$(kubectl -n \"$namespace\" exec deploy/\"$read_deployment\" -- sh -lc 'repo_path=\"$1\"; source_branch=\"$2\"; git --git-dir=\"$repo_path\" rev-parse --verify \"refs/heads/$source_branch^{commit}\" 2>/dev/null || true' sh \"$repo_path\" \"$source_branch\" 2>/dev/null | tail -n 1 | tr -d '\\r')",
" refs=$(kubectl -n \"$namespace\" exec deploy/\"$read_deployment\" -- sh -lc 'repo_path=\"$1\"; source_branch=\"$2\"; stage_ref_prefix=\"$3\"; source_ref=\"refs/mirror-stage/heads/$source_branch\"; source_commit=$(git --git-dir=\"$repo_path\" rev-parse --verify \"$source_ref^{commit}\" 2>/dev/null || true); source_stage_ref=\"\"; snapshot_commit=\"\"; if printf \"%s\" \"$source_commit\" | grep -Eq \"^[0-9a-fA-F]{40}$\"; then source_stage_ref=\"${stage_ref_prefix%/}/$source_commit\"; snapshot_commit=$(git --git-dir=\"$repo_path\" rev-parse --verify \"$source_stage_ref^{commit}\" 2>/dev/null || true); fi; printf \"source_commit=%s\\nsource_stage_ref=%s\\nsnapshot_commit=%s\\n\" \"$source_commit\" \"$source_stage_ref\" \"$snapshot_commit\"' sh \"$repo_path\" \"$source_branch\" \"$source_stage_ref_prefix\" 2>/dev/null)",
" source_commit=$(printf '%s\\n' \"$refs\" | sed -n 's/^source_commit=//p' | tail -n 1 | tr -d '\\r')",
" source_stage_ref=$(printf '%s\\n' \"$refs\" | sed -n 's/^source_stage_ref=//p' | tail -n 1 | tr -d '\\r')",
" snapshot_commit=$(printf '%s\\n' \"$refs\" | sed -n 's/^snapshot_commit=//p' | tail -n 1 | tr -d '\\r')",
"fi",
"export namespace read_deployment repository source_branch read_exit source_commit",
"export namespace read_deployment repository source_branch read_exit source_commit source_ref source_stage_ref snapshot_commit",
"python3 - <<'PY'",
"import json, os",
"source_commit = os.environ.get('source_commit') or None",
"import json, os, re",
"def sha(value):",
" return (value or '').lower() if re.match(r'^[0-9a-f]{40}$', value or '', re.I) else None",
"source_commit = sha(os.environ.get('source_commit'))",
"snapshot_commit = sha(os.environ.get('snapshot_commit'))",
"source_stage_ref = os.environ.get('source_stage_ref') or None",
"read_ready = os.environ.get('read_exit') == '0'",
"snapshot_present = source_commit is not None and snapshot_commit == source_commit",
"degraded_reason = None",
"if not read_ready:",
" degraded_reason = 'git-mirror-read-not-ready'",
"elif source_commit is None:",
" degraded_reason = 'source-branch-missing'",
"elif not snapshot_present:",
" degraded_reason = 'source-snapshot-missing'",
"print(json.dumps({",
" 'ok': read_ready and source_commit is not None,",
" 'ok': read_ready and snapshot_present,",
" 'statusMode': 'k3s-git-mirror',",
" 'sourceAuthority': 'git-mirror-snapshot',",
" 'expectedWorkspace': None,",
" 'actualWorkspace': None,",
" 'workspaceExists': None,",
@@ -415,12 +437,18 @@ export function yamlLaneK3sSourceStatusScript(spec: AgentRunLaneSpec): string {
" 'remoteBranch': os.environ.get('source_branch'),",
" 'remoteBranchExists': source_commit is not None,",
" 'remoteBranchCommit': source_commit,",
" 'sourceCommit': source_commit,",
" 'sourceCommit': snapshot_commit if snapshot_present else source_commit,",
" 'sourceRef': os.environ.get('source_ref'),",
" 'sourceStageRef': source_stage_ref,",
" 'snapshotPresent': snapshot_present,",
" 'degradedReason': degraded_reason,",
" 'gitMirror': {",
" 'namespace': os.environ.get('namespace'),",
" 'readDeployment': os.environ.get('read_deployment'),",
" 'readReady': read_ready,",
" 'repository': os.environ.get('repository'),",
" 'sourceRef': os.environ.get('source_ref'),",
" 'sourceStageRef': source_stage_ref,",
" },",
" 'statusShort': None,",
" 'valuesPrinted': False,",
@@ -1168,12 +1196,12 @@ function yamlLaneK3sBuildSourceShell(spec: AgentRunLaneSpec, sourceCommit: strin
return [
"set -eu",
`read_url=${shQuote(spec.gitMirror.readUrl)}`,
`source_branch=${shQuote(spec.source.branch)}`,
`source_commit=${shQuote(sourceCommit)}`,
`source_stage_ref=${shQuote(agentRunSourceSnapshotRef(spec, sourceCommit))}`,
"rm -rf /workspace/repo",
"git clone --no-checkout \"$read_url\" /workspace/repo",
"cd /workspace/repo",
"git fetch origin \"refs/heads/$source_branch:refs/remotes/origin/$source_branch\"",
"git fetch origin \"+$source_stage_ref:refs/remotes/origin/unidesk-source-snapshot\"",
"git checkout --detach \"$source_commit\"",
"actual=$(git rev-parse HEAD)",
"test \"$actual\" = \"$source_commit\"",