feat: migrate jd01 hwlab ci to gitea pac
This commit is contained in:
@@ -95,6 +95,7 @@ export function nodeRuntimeGitopsRoot(spec: HwlabRuntimeLaneSpec): string {
|
||||
}
|
||||
|
||||
export function resolveNodeRuntimeLaneHead(spec: HwlabRuntimeLaneSpec): { sourceCommit: string | null; result: CommandResult; stdoutRecovery?: Record<string, unknown> } {
|
||||
if (spec.sourceAuthority?.mode === "giteaSnapshot") return resolveNodeRuntimeGiteaLaneHead(spec);
|
||||
const mirror = nodeRuntimeSourceMirrorTarget(spec);
|
||||
const script = [
|
||||
"set +e",
|
||||
@@ -134,6 +135,52 @@ export function resolveNodeRuntimeLaneHead(spec: HwlabRuntimeLaneSpec): { source
|
||||
return { sourceCommit: result.exitCode === 0 ? match : null, result, stdoutRecovery: payloadResolution.diagnostics };
|
||||
}
|
||||
|
||||
function resolveNodeRuntimeGiteaLaneHead(spec: HwlabRuntimeLaneSpec): { sourceCommit: string | null; result: CommandResult; stdoutRecovery?: Record<string, unknown> } {
|
||||
const repoKey = nodeRuntimeGiteaRepoKey(spec);
|
||||
const result = runCommand([
|
||||
"bun",
|
||||
"scripts/cli.ts",
|
||||
"platform-infra",
|
||||
"gitea",
|
||||
"mirror",
|
||||
"status",
|
||||
"--target",
|
||||
spec.nodeId,
|
||||
"--repo",
|
||||
repoKey,
|
||||
"--raw",
|
||||
], repoRoot, { timeoutMs: 60_000 });
|
||||
const payloadResolution = resolveRuntimeCleanupJson(result, "platform-infra gitea mirror status JSON");
|
||||
const payload = runtimeRecord(payloadResolution.parsed?.data ?? payloadResolution.parsed);
|
||||
const repositories = Array.isArray(payload.repositories)
|
||||
? payload.repositories.filter((item): item is Record<string, unknown> => typeof item === "object" && item !== null && !Array.isArray(item))
|
||||
: [];
|
||||
const repo = repositories[0] ?? {};
|
||||
const branchCommit = typeof repo.branchCommit === "string" && /^[0-9a-f]{40}$/iu.test(repo.branchCommit) ? repo.branchCommit.toLowerCase() : null;
|
||||
const snapshotCommit = typeof repo.snapshotCommit === "string" && /^[0-9a-f]{40}$/iu.test(repo.snapshotCommit) ? repo.snapshotCommit.toLowerCase() : null;
|
||||
const snapshotRef = typeof repo.snapshotRef === "string" ? repo.snapshotRef : null;
|
||||
const sourceCommit = branchCommit !== null && snapshotCommit === branchCommit ? branchCommit : null;
|
||||
return {
|
||||
sourceCommit: result.exitCode === 0 ? sourceCommit : null,
|
||||
result,
|
||||
stdoutRecovery: {
|
||||
...payloadResolution.diagnostics,
|
||||
sourceAuthority: "gitea-snapshot",
|
||||
repoKey,
|
||||
branchCommit,
|
||||
snapshotCommit,
|
||||
snapshotRef,
|
||||
snapshotPresent: sourceCommit !== null,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function nodeRuntimeGiteaRepoKey(spec: HwlabRuntimeLaneSpec): string {
|
||||
const repoKey = spec.sourceAuthority?.giteaMirrorRepoKey;
|
||||
if (typeof repoKey !== "string" || repoKey.length === 0) throw new Error(`giteaMirrorRepoKey is required for node=${spec.nodeId} lane=${spec.lane} gitea source authority`);
|
||||
return repoKey;
|
||||
}
|
||||
|
||||
function resolveRuntimeCleanupJson(result: CommandResult, requestedStdoutType: string): { parsed: Record<string, unknown> | null; diagnostics: Record<string, unknown> } {
|
||||
const resolved = resolveCliChildJsonCommandResult({
|
||||
result,
|
||||
|
||||
@@ -35,7 +35,7 @@ import { parseNodeScopedDelegatedOptions } from "./plan";
|
||||
import { compactNodeRuntimeTaskRunDiagnostic, nodeRuntimeControlPlaneStatus, nodeRuntimePipelineFailureSummary } from "./render";
|
||||
import { compactRuntimeCommand } from "./runtime-common";
|
||||
import { compactNodeRuntimeGitMirrorObservation, compactNodeRuntimeGitMirrorRun, nodeRuntimeEnsureGitMirrorFlushed, nodeRuntimeEnsureGitMirrorSourceCurrent, nodeRuntimeExternalPostgresSecretRows, nodeRuntimeGitMirrorRun, nodeRuntimeGitMirrorStatus, nodeRuntimeOpportunisticGitMirrorFlush, nodeRuntimeOpportunisticGitMirrorSync, nodeScopedFullOutput, type NodeRuntimeGitMirrorRunOptions } from "./status";
|
||||
import { record } from "./utils";
|
||||
import { parseJsonObject, record, statusText } from "./utils";
|
||||
import { webObserveTable } from "./web-observe-render";
|
||||
import { createNodeRuntimePipelineRun, getNodeRuntimePipelineRun, nodeRuntimePipelineRunManifest, printNodeRuntimeTriggerProgress, waitForNodeRuntimePipelineRunTerminal } from "./web-probe";
|
||||
import { webObserveShort, webObserveText } from "./web-probe-observe";
|
||||
@@ -53,20 +53,7 @@ export function nodeRuntimeTriggerCurrent(scoped: ReturnType<typeof parseNodeSco
|
||||
const triggerDeadlineMs = triggerStartedAt + (nodeRuntimeCicdWaitSeconds(scoped) * 1000);
|
||||
const remainingTriggerSeconds = () => Math.max(0, Math.floor((triggerDeadlineMs - Date.now()) / 1000));
|
||||
const triggerGitMirrorOptions = nodeRuntimeTriggerGitMirrorOptions(triggerDeadlineMs);
|
||||
const sourceSyncScoped = nodeRuntimeScopedForTriggerDeadline(scoped, triggerDeadlineMs);
|
||||
const sourceSnapshotSync = scoped.dryRun
|
||||
? null
|
||||
: sourceSyncScoped === null
|
||||
? nodeRuntimeTriggerBudgetExhausted(scoped, "source-snapshot-sync", "-", "-")
|
||||
: nodeRuntimeGitMirrorRun({
|
||||
...sourceSyncScoped,
|
||||
domain: "git-mirror",
|
||||
action: "sync",
|
||||
confirm: true,
|
||||
dryRun: false,
|
||||
wait: true,
|
||||
discardStaleGitops: scoped.discardStaleGitops === true || scoped.rerun === true,
|
||||
}, triggerGitMirrorOptions);
|
||||
const sourceSnapshotSync = scoped.dryRun ? null : nodeRuntimeSourceSnapshotSync(scoped, triggerDeadlineMs, triggerGitMirrorOptions);
|
||||
if (sourceSnapshotSync !== null && sourceSnapshotSync.ok !== true) {
|
||||
return {
|
||||
ok: false,
|
||||
@@ -184,7 +171,9 @@ export function nodeRuntimeTriggerCurrent(scoped: ReturnType<typeof parseNodeSco
|
||||
};
|
||||
}
|
||||
printNodeRuntimeTriggerProgress(spec, { stage: "git-mirror-pre-sync", status: "started", sourceCommit, pipelineRun });
|
||||
const gitMirror = nodeRuntimeEnsureGitMirrorSourceCurrent(scoped, sourceCommit, pipelineRun, triggerGitMirrorOptions);
|
||||
const gitMirror = spec.sourceAuthority?.mode === "giteaSnapshot"
|
||||
? nodeRuntimeEnsureGiteaSourceCurrent(scoped, sourceCommit, pipelineRun, triggerGitMirrorOptions)
|
||||
: nodeRuntimeEnsureGitMirrorSourceCurrent(scoped, sourceCommit, pipelineRun, triggerGitMirrorOptions);
|
||||
if (gitMirror.ok !== true) {
|
||||
printNodeRuntimeTriggerProgress(spec, { stage: "git-mirror-pre-sync", status: "failed", sourceCommit, pipelineRun, reason: gitMirror.degradedReason ?? null });
|
||||
return {
|
||||
@@ -320,6 +309,92 @@ function nodeRuntimeTriggerGitMirrorOptions(deadlineMs: number): NodeRuntimeGitM
|
||||
return { deadlineMs, maxAttempts: 1 };
|
||||
}
|
||||
|
||||
function nodeRuntimeSourceSnapshotSync(
|
||||
scoped: ReturnType<typeof parseNodeScopedDelegatedOptions>,
|
||||
deadlineMs: number,
|
||||
gitMirrorOptions: NodeRuntimeGitMirrorRunOptions,
|
||||
): Record<string, unknown> {
|
||||
const syncScoped = nodeRuntimeScopedForTriggerDeadline(scoped, deadlineMs);
|
||||
if (syncScoped === null) return nodeRuntimeTriggerBudgetExhausted(scoped, "source-snapshot-sync", "-", "-");
|
||||
if (scoped.spec.sourceAuthority?.mode === "giteaSnapshot") return nodeRuntimeGiteaMirrorSync(scoped, deadlineMs);
|
||||
return nodeRuntimeGitMirrorRun({
|
||||
...syncScoped,
|
||||
domain: "git-mirror",
|
||||
action: "sync",
|
||||
confirm: true,
|
||||
dryRun: false,
|
||||
wait: true,
|
||||
discardStaleGitops: scoped.discardStaleGitops === true || scoped.rerun === true,
|
||||
}, gitMirrorOptions);
|
||||
}
|
||||
|
||||
function nodeRuntimeGiteaMirrorSync(scoped: ReturnType<typeof parseNodeScopedDelegatedOptions>, deadlineMs: number): Record<string, unknown> {
|
||||
const repoKey = scoped.spec.sourceAuthority?.giteaMirrorRepoKey;
|
||||
if (typeof repoKey !== "string" || repoKey.length === 0) {
|
||||
return {
|
||||
ok: false,
|
||||
action: "platform-infra-gitea-mirror-sync",
|
||||
mode: "gitea-controlled-snapshot-sync",
|
||||
mutation: false,
|
||||
degradedReason: "node-runtime-gitea-repo-key-missing",
|
||||
};
|
||||
}
|
||||
const timeoutMs = Math.max(1_000, Math.min(120_000, deadlineMs - Date.now()));
|
||||
const result = runCommand([
|
||||
"bun",
|
||||
"scripts/cli.ts",
|
||||
"platform-infra",
|
||||
"gitea",
|
||||
"mirror",
|
||||
"sync",
|
||||
"--target",
|
||||
scoped.node,
|
||||
"--repo",
|
||||
repoKey,
|
||||
"--confirm",
|
||||
"--raw",
|
||||
], repoRoot, { timeoutMs });
|
||||
const parsedRoot = record(parseJsonObject(statusText(result)));
|
||||
const parsed = record(parsedRoot.data ?? parsedRoot);
|
||||
const ok = result.exitCode === 0 && (parsed.ok === true || parsedRoot.ok === true);
|
||||
return {
|
||||
ok,
|
||||
action: "platform-infra-gitea-mirror-sync",
|
||||
mode: "gitea-controlled-snapshot-sync",
|
||||
mutation: ok,
|
||||
repoKey,
|
||||
sourceAuthority: "gitea-snapshot",
|
||||
sourceBundles: parsed.sourceBundles ?? null,
|
||||
repositories: parsed.repositories ?? null,
|
||||
result: compactRuntimeCommand(result),
|
||||
remote: parsed,
|
||||
degradedReason: ok ? undefined : "node-runtime-gitea-source-snapshot-sync-failed",
|
||||
next: ok ? undefined : {
|
||||
status: `bun scripts/cli.ts platform-infra gitea mirror status --target ${scoped.node} --repo ${repoKey}`,
|
||||
retry: `bun scripts/cli.ts platform-infra gitea mirror sync --target ${scoped.node} --repo ${repoKey} --confirm`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function nodeRuntimeEnsureGiteaSourceCurrent(
|
||||
scoped: ReturnType<typeof parseNodeScopedDelegatedOptions>,
|
||||
sourceCommit: string,
|
||||
pipelineRun: string,
|
||||
options: NodeRuntimeGitMirrorRunOptions,
|
||||
): Record<string, unknown> {
|
||||
const flush = nodeRuntimeEnsureGitMirrorFlushed(scoped, "pre", sourceCommit, pipelineRun, null, options);
|
||||
return {
|
||||
ok: flush.ok === true,
|
||||
mode: "gitea-source-authority",
|
||||
sourceAuthority: "gitea-snapshot",
|
||||
sourceCommit,
|
||||
sourceCurrent: true,
|
||||
legacyGitMirrorRole: "gitops-flush-only",
|
||||
flush,
|
||||
degradedReason: flush.ok === true ? undefined : "node-runtime-git-mirror-pre-flush-failed",
|
||||
};
|
||||
}
|
||||
|
||||
function nodeRuntimeScopedForTriggerDeadline(
|
||||
scoped: ReturnType<typeof parseNodeScopedDelegatedOptions>,
|
||||
deadlineMs: number,
|
||||
|
||||
@@ -61,6 +61,111 @@ export function nodeRuntimeExternalPostgresSecretRows(secrets: Record<string, un
|
||||
}
|
||||
|
||||
export function nodeRuntimeGitMirrorStatus(scoped: ReturnType<typeof parseNodeScopedDelegatedOptions>): Record<string, unknown> {
|
||||
if (scoped.spec.sourceAuthority?.mode === "giteaSnapshot") return nodeRuntimeGiteaSourceStatus(scoped);
|
||||
return nodeRuntimeLegacyGitMirrorStatus(scoped);
|
||||
}
|
||||
|
||||
function nodeRuntimeGiteaSourceStatus(scoped: ReturnType<typeof parseNodeScopedDelegatedOptions>): Record<string, unknown> {
|
||||
const repoKey = scoped.spec.sourceAuthority?.giteaMirrorRepoKey;
|
||||
if (typeof repoKey !== "string" || repoKey.length === 0) {
|
||||
return {
|
||||
ok: false,
|
||||
command: `hwlab nodes git-mirror status --node ${scoped.node} --lane ${scoped.lane}`,
|
||||
node: scoped.node,
|
||||
lane: scoped.lane,
|
||||
mode: "gitea-source+legacy-gitops-flush",
|
||||
mutation: false,
|
||||
degradedReason: "node-runtime-gitea-repo-key-missing",
|
||||
};
|
||||
}
|
||||
const legacy = nodeRuntimeLegacyGitMirrorStatus(scoped);
|
||||
const result = runCommand([
|
||||
"bun",
|
||||
"scripts/cli.ts",
|
||||
"platform-infra",
|
||||
"gitea",
|
||||
"mirror",
|
||||
"status",
|
||||
"--target",
|
||||
scoped.node,
|
||||
"--repo",
|
||||
repoKey,
|
||||
"--raw",
|
||||
], repoRoot, { timeoutMs: scoped.timeoutSeconds * 1000 });
|
||||
const parsedRoot = record(parseJsonObject(statusText(result)));
|
||||
const parsed = record(parsedRoot.data ?? parsedRoot);
|
||||
const repositories = Array.isArray(parsed.repositories)
|
||||
? parsed.repositories.filter((item): item is Record<string, unknown> => typeof item === "object" && item !== null && !Array.isArray(item))
|
||||
: [];
|
||||
const repo = repositories[0] ?? {};
|
||||
const branchCommit = typeof repo.branchCommit === "string" && /^[0-9a-f]{40}$/iu.test(repo.branchCommit) ? repo.branchCommit.toLowerCase() : null;
|
||||
const snapshotCommit = typeof repo.snapshotCommit === "string" && /^[0-9a-f]{40}$/iu.test(repo.snapshotCommit) ? repo.snapshotCommit.toLowerCase() : null;
|
||||
const snapshotRef = typeof repo.snapshotRef === "string" ? repo.snapshotRef : null;
|
||||
const sourceReady = branchCommit !== null && snapshotCommit === branchCommit;
|
||||
const legacySummary = compactNodeRuntimeGitMirrorStatus(legacy);
|
||||
const localGitops = typeof legacySummary.localGitops === "string" ? legacySummary.localGitops : null;
|
||||
const githubGitops = typeof legacySummary.githubGitops === "string" ? legacySummary.githubGitops : null;
|
||||
const pendingFlush = legacySummary.pendingFlush === true || (localGitops !== null && githubGitops !== null && localGitops !== githubGitops);
|
||||
const summary = {
|
||||
localSource: branchCommit,
|
||||
githubSource: branchCommit,
|
||||
sourceAuthority: "gitea-snapshot",
|
||||
sourceStageRef: snapshotRef,
|
||||
sourceSnapshot: snapshotCommit,
|
||||
localGitops,
|
||||
githubGitops,
|
||||
pendingFlush,
|
||||
flushNeeded: pendingFlush,
|
||||
githubInSync: sourceReady && localGitops !== null && githubGitops !== null && localGitops === githubGitops,
|
||||
refSources: {
|
||||
localSource: `gitea:${repoKey}:refs/heads/${scoped.spec.sourceBranch}`,
|
||||
githubSource: `gitea:${repoKey}:refs/heads/${scoped.spec.sourceBranch}`,
|
||||
sourceSnapshot: snapshotRef,
|
||||
localGitops: "legacy-git-mirror:refs/heads/" + scoped.spec.gitopsBranch,
|
||||
githubGitops: "legacy-git-mirror:refs/mirror-stage/heads/" + scoped.spec.gitopsBranch,
|
||||
githubFieldsAreMirrorStageCache: false,
|
||||
},
|
||||
};
|
||||
const ok = result.exitCode === 0 && (parsed.ok === true || parsedRoot.ok === true) && sourceReady;
|
||||
return {
|
||||
ok,
|
||||
command: `hwlab nodes git-mirror status --node ${scoped.node} --lane ${scoped.lane}`,
|
||||
node: scoped.node,
|
||||
lane: scoped.lane,
|
||||
mode: "gitea-source+legacy-gitops-flush",
|
||||
mutation: false,
|
||||
namespace: record(legacy).namespace ?? null,
|
||||
readUrl: scoped.spec.gitReadUrl,
|
||||
writeUrl: scoped.spec.gitWriteUrl,
|
||||
sourceBranch: scoped.spec.sourceBranch,
|
||||
gitopsBranch: scoped.spec.gitopsBranch,
|
||||
resources: record(legacy.resources),
|
||||
githubTransport: record(legacy.githubTransport),
|
||||
summary,
|
||||
gitea: {
|
||||
repoKey,
|
||||
repository: repo,
|
||||
status: parsed,
|
||||
result: compactRuntimeCommand(result),
|
||||
},
|
||||
legacyGitMirror: {
|
||||
role: "gitops-flush-only",
|
||||
ok: legacy.ok === true,
|
||||
summary: legacySummary,
|
||||
degradedReason: legacy.degradedReason ?? null,
|
||||
},
|
||||
refSources: summary.refSources,
|
||||
result: compactRuntimeCommand(result),
|
||||
degradedReason: ok ? undefined : "node-runtime-gitea-source-snapshot-not-ready",
|
||||
valuesPrinted: false,
|
||||
next: {
|
||||
sync: `bun scripts/cli.ts platform-infra gitea mirror sync --target ${scoped.node} --repo ${repoKey} --confirm`,
|
||||
flush: `bun scripts/cli.ts hwlab nodes git-mirror flush --node ${scoped.node} --lane ${scoped.lane} --confirm`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function nodeRuntimeLegacyGitMirrorStatus(scoped: ReturnType<typeof parseNodeScopedDelegatedOptions>): Record<string, unknown> {
|
||||
const spec = scoped.spec;
|
||||
const mirror = nodeRuntimeGitMirrorTarget(spec);
|
||||
const script = [
|
||||
@@ -703,6 +808,7 @@ export function nodeRuntimeOpportunisticGitMirrorSync(
|
||||
pipelineRun: string,
|
||||
options: NodeRuntimeGitMirrorRunOptions = {},
|
||||
): Record<string, unknown> | null {
|
||||
if (scoped.spec.sourceAuthority?.mode === "giteaSnapshot") return null;
|
||||
const stage = "git-mirror-parallel-sync";
|
||||
const syncScoped = nodeRuntimeScopedWithinDeadline(scoped, options.deadlineMs);
|
||||
if (syncScoped === null) return null;
|
||||
|
||||
Reference in New Issue
Block a user