ci: migrate sentinel to gitea pac

This commit is contained in:
Codex
2026-07-05 14:06:37 +00:00
parent 79e9288d5f
commit 376ab626be
15 changed files with 561 additions and 158 deletions
+74 -7
View File
@@ -82,6 +82,7 @@ import {
type WebProbeSentinelDashboardAction,
type WebProbeSentinelOptions,
type WebProbeSentinelReportView,
type WebProbeSentinelSourceAuthority,
} from "./hwlab-node-web-sentinel-cicd-shared";
import {
applySentinelArgoApplication,
@@ -108,6 +109,8 @@ export type {
WebProbeSentinelDashboardAction,
WebProbeSentinelOptions,
WebProbeSentinelReportView,
WebProbeSentinelSourceAuthority,
WebProbeSentinelSourceOverrideOptions,
} from "./hwlab-node-web-sentinel-cicd-shared";
export {
arrayAt,
@@ -143,13 +146,13 @@ export interface SentinelSourceOverride {
readonly commit: string;
readonly stageRef?: string | null;
readonly mirrorCommit?: string | null;
readonly sourceAuthority: "git-mirror-snapshot";
readonly sourceAuthority: WebProbeSentinelSourceAuthority;
}
export function runWebProbeSentinelCommand(spec: HwlabRuntimeLaneSpec, options: WebProbeSentinelOptions): RenderedCliResult {
if (options.kind === "config") return withWebProbeSentinelConfigRendered(webProbeSentinelConfigPlan(spec, options.action, options.sentinelId));
requireSentinelIdForRegistry(spec, options.sentinelId, `web-probe sentinel ${options.kind}`);
const state = loadSentinelCicdState(spec, options.sentinelId, options.timeoutSeconds, sentinelSourceResolveMode(options));
const state = loadSentinelCicdState(spec, options.sentinelId, options.timeoutSeconds, sentinelSourceResolveMode(options), sentinelSourceOverrideFromOptions(options));
if (options.kind === "image") return runSentinelImage(state, options);
if (options.kind === "control-plane") return runSentinelControlPlane(state, options);
if (options.kind === "publish") return runSentinelPublishCurrent(state, options);
@@ -159,6 +162,19 @@ export function runWebProbeSentinelCommand(spec: HwlabRuntimeLaneSpec, options:
return runSentinelReport(state, options);
}
function sentinelSourceOverrideFromOptions(options: WebProbeSentinelOptions): SentinelSourceOverride | null {
if (options.kind !== "image" && options.kind !== "control-plane" && options.kind !== "publish") return null;
const override = options.sourceOverride;
if (override.sourceCommit === null && override.sourceStageRef === null && override.sourceMirrorCommit === null && override.sourceAuthority === null) return null;
if (override.sourceCommit === null) throw new Error("--source-commit is required when overriding web-probe sentinel source");
return {
commit: override.sourceCommit,
stageRef: override.sourceStageRef,
mirrorCommit: override.sourceMirrorCommit,
sourceAuthority: override.sourceAuthority ?? "git-mirror-snapshot",
};
}
function sentinelSourceResolveMode(options: WebProbeSentinelOptions): SourceResolveMode {
if (options.kind === "image" && options.action === "build" && options.confirm && options.wait) return "sync";
if (options.kind === "control-plane" && options.action === "trigger-current" && options.confirm && options.wait) return "sync";
@@ -347,6 +363,51 @@ function runSentinelPublishCurrentConfirmedInner(state: SentinelCicdState, optio
lane: state.spec.lane,
sentinelId: state.sentinelId,
});
if (process.env.KUBERNETES_SERVICE_HOST && process.env.KUBERNETES_SERVICE_PORT) {
const publish = runSentinelPublishJob(state, true, Math.max(1, remainingBudgetSeconds()), options.rerun);
const elapsedMs = Date.now() - startedAt;
const ok = record(publish).ok === true;
const result = {
ok,
command,
node: state.spec.nodeId,
lane: state.spec.lane,
sentinelId: state.sentinelId,
mode: "pac-publish-only",
mutation: true,
specRef: SPEC_REF,
source: state.sourceHead,
image: state.image,
pipelineRun: record(publish).jobName ?? sentinelPipelineRunName(state, options.rerun),
controlPlane: {
ok,
mode: "pac-publish-only",
mutation: true,
source: state.sourceHead,
publish,
argo: {
namespace: stringAt(state.cicd, "argo.namespace"),
applicationName: stringAt(state.cicd, "argo.applicationName"),
},
valuesRedacted: true,
},
health: { ok: false, skipped: true, reason: "pac-publish-only-runtime-closeout-read-by-platform-infra-status", valuesRedacted: true },
elapsedMs,
timings: publishCurrentStageTimings({ publish }, null, elapsedMs),
slowStages: publishCurrentSlowStages(state, publishCurrentStageTimings({ publish }, null, elapsedMs), budgetSeconds),
withinBudget: elapsedMs <= budgetSeconds * 1000,
warnings: [
"PaC in-cluster publish-current submits the native sentinel publish PipelineRun and leaves Argo/runtime closeout to platform-infra pipelines-as-code status.",
...sentinelCicdElapsedWarnings(record(publish).elapsedMs, "sentinel publish", controlPlaneWaitWarningSeconds(state)),
...sentinelRemoteJobTimeoutWarnings(publish, "sentinel publish"),
],
blocker: ok ? null : { code: "sentinel-pac-publish-not-ready", reason: "sentinel publish PipelineRun did not complete successfully" },
recoveryNext: controlPlaneRecoveryNext(state, ok, publish, null, false),
next: publishCurrentNext(state),
valuesRedacted: true,
};
return rendered(ok, command, renderPublishCurrentResult(result));
}
if (state.configReady && state.sourceHead.ok && remainingBudgetSeconds() >= 5) {
interruptContext.phase = "already-current-registry-probe";
const registryProbe = probeImageRegistry(state, Math.max(1, Math.min(remainingBudgetSeconds(), 5)));
@@ -377,6 +438,7 @@ function runSentinelPublishCurrentConfirmedInner(state: SentinelCicdState, optio
wait: true,
timeoutSeconds: Math.max(1, remainingBudgetSeconds()),
rerun: options.rerun,
sourceOverride: options.sourceOverride,
});
let health: Record<string, unknown>;
let healthElapsedMs: number | null = null;
@@ -632,7 +694,7 @@ function sourceHeadFromOverride(cicd: Record<string, unknown>, override: Sentine
timedOut: false,
stdoutBytes: 0,
stderrBytes: 0,
stdoutPreview: "source supplied by cicd branch-follower k8s git-mirror snapshot",
stdoutPreview: `source supplied by ${override.sourceAuthority}`,
stderrPreview: "",
},
};
@@ -755,8 +817,8 @@ function validateSentinelSourceAuthority(cicd: Record<string, unknown>): void {
const allowHostGit = booleanAt(cicd, "sourceAuthority.allowHostGit");
const allowGithubDirectInPipeline = booleanAt(cicd, "sourceAuthority.allowGithubDirectInPipeline");
const missingObjectPolicy = stringAt(cicd, "sourceSnapshot.missingObjectPolicy");
if (mode !== "gitMirrorSnapshot") throw new Error("sourceAuthority.mode must be gitMirrorSnapshot");
if (resolver !== "k8s-git-mirror") throw new Error("sourceAuthority.resolver must be k8s-git-mirror");
const supported = (mode === "gitMirrorSnapshot" && resolver === "k8s-git-mirror") || (mode === "giteaSnapshot" && resolver === "gitea-mirror");
if (!supported) throw new Error("sourceAuthority must be gitMirrorSnapshot/k8s-git-mirror or giteaSnapshot/gitea-mirror");
if (allowHostGit !== false) throw new Error("sourceAuthority.allowHostGit must be false");
if (allowGithubDirectInPipeline !== false) throw new Error("sourceAuthority.allowGithubDirectInPipeline must be false");
if (missingObjectPolicy !== "fail-fast") throw new Error("sourceSnapshot.missingObjectPolicy must be fail-fast");
@@ -1597,8 +1659,13 @@ function sentinelControlPlaneConfirmedResult(state: SentinelCicdState, options:
const deadline = startedAt + waitBudgetSeconds * 1000;
const remainingCicdWaitSeconds = () => strictRemainingSeconds(deadline, waitBudgetSeconds);
const remainingCommandSeconds = () => Math.max(1, remainingCicdWaitSeconds());
const sourceMirrorProbe = applyOnly ? null : probeSourceMirror(state, Math.min(remainingCommandSeconds(), 20));
const sourceMirrorSync = applyOnly ? null : record(sourceMirrorProbe).ok === true ? sentinelSourceMirrorAlreadyPresentResult(state, sourceMirrorProbe) : runSentinelSourceMirrorSyncJob(state, remainingCommandSeconds());
const giteaSource = state.sourceHead.sourceAuthority === "gitea-snapshot" || state.sourceHead.sourceAuthority === "gitea-cache";
const sourceMirrorProbe = applyOnly || giteaSource ? null : probeSourceMirror(state, Math.min(remainingCommandSeconds(), 20));
const sourceMirrorSync = applyOnly
? null
: giteaSource
? sentinelSourceMirrorAlreadyPresentResult(state, { ok: true, mode: "gitea-controlled-mirror-snapshot", stageRef: state.sourceHead.stageRef, sourceCommit: state.sourceHead.commit, valuesRedacted: true })
: record(sourceMirrorProbe).ok === true ? sentinelSourceMirrorAlreadyPresentResult(state, sourceMirrorProbe) : runSentinelSourceMirrorSyncJob(state, remainingCommandSeconds());
const sourceMirrorReady = applyOnly || record(sourceMirrorSync).ok === true;
const publish = applyOnly
? null