fix: expose pac sentinel cicd diagnosis
Pipelines as Code CI / hwlab-web-probe-sentinel-jd01- Success
Pipelines as Code CI / hwlab-web-probe-sentinel-jd01- Success
This commit is contained in:
@@ -482,6 +482,7 @@ export function renderPublishCurrentResult(result: Record<string, unknown>): str
|
||||
const blocker = record(result.blocker);
|
||||
const recoveryNext = record(controlPlane.recoveryNext);
|
||||
const next = record(result.next);
|
||||
const drillDown = record(result.drillDown);
|
||||
const warnings = Array.isArray(result.warnings) ? result.warnings : [];
|
||||
const slowStages = Array.isArray(result.slowStages) ? result.slowStages.map(record) : [];
|
||||
const lines = [
|
||||
@@ -575,6 +576,18 @@ export function renderPublishCurrentResult(result: Record<string, unknown>): str
|
||||
"",
|
||||
Object.keys(blocker).length === 0 ? "BLOCKER\n-" : ["BLOCKER", table(["CODE", "REASON"], [[blocker.code, blocker.reason]])].join("\n"),
|
||||
"",
|
||||
Object.keys(drillDown).length === 0 ? "DRILLDOWN\n-" : [
|
||||
"DRILLDOWN",
|
||||
table(["PIPELINERUN", "ASYNC_FLUSH_JOB"], [[drillDown.pipelineRun ?? "-", drillDown.asyncFlushJob ?? "-"]]),
|
||||
` pipeline-run: ${drillDown.pipelineRunStatus ?? "-"}`,
|
||||
` pipeline-run-full: ${drillDown.pipelineRunFull ?? "-"}`,
|
||||
` pac-status: ${drillDown.consumerStatus ?? "-"}`,
|
||||
` image-status: ${drillDown.imageStatus ?? "-"}`,
|
||||
` git-mirror: ${drillDown.gitMirrorStatus ?? "-"}`,
|
||||
` flush: ${drillDown.gitMirrorFlush ?? "-"}`,
|
||||
` async-flush-status: ${drillDown.asyncFlushStatus ?? "-"}`,
|
||||
].join("\n"),
|
||||
"",
|
||||
Object.keys(recoveryNext).length === 0 ? "RECOVERY_NEXT\n-" : [
|
||||
"RECOVERY_NEXT",
|
||||
table(["REASON", "PIPELINERUN", "DIGEST", "GITOPS"], [[recoveryNext.reason, recoveryNext.pipelineRun ?? "-", short(recoveryNext.digestRef), short(recoveryNext.gitopsCommit)]]),
|
||||
@@ -665,6 +678,7 @@ export function renderControlPlaneResult(result: Record<string, unknown>): strin
|
||||
const argoApply = record(result.argoApply);
|
||||
const blocker = record(result.blocker);
|
||||
const statusDiagnosis = record(result.statusDiagnosis);
|
||||
const drillDown = record(result.drillDown);
|
||||
const targetValidation = record(result.targetValidation);
|
||||
const targetValidationBusiness = record(targetValidation.businessStatus);
|
||||
const recoveryNext = record(result.recoveryNext);
|
||||
@@ -700,6 +714,18 @@ export function renderControlPlaneResult(result: Record<string, unknown>): strin
|
||||
]]),
|
||||
].join("\n"),
|
||||
"",
|
||||
Object.keys(drillDown).length === 0 ? "DRILLDOWN\n-" : [
|
||||
"DRILLDOWN",
|
||||
table(["PIPELINERUN", "ASYNC_FLUSH_JOB"], [[drillDown.pipelineRun ?? "-", drillDown.asyncFlushJob ?? "-"]]),
|
||||
` pipeline-run: ${drillDown.pipelineRunStatus ?? "-"}`,
|
||||
` pipeline-run-full: ${drillDown.pipelineRunFull ?? "-"}`,
|
||||
` pac-status: ${drillDown.consumerStatus ?? "-"}`,
|
||||
` image-status: ${drillDown.imageStatus ?? "-"}`,
|
||||
` git-mirror: ${drillDown.gitMirrorStatus ?? "-"}`,
|
||||
` flush: ${drillDown.gitMirrorFlush ?? "-"}`,
|
||||
` async-flush-status: ${drillDown.asyncFlushStatus ?? "-"}`,
|
||||
].join("\n"),
|
||||
"",
|
||||
Object.keys(sourceMirrorSync).length === 0 ? "SOURCE_MIRROR_SYNC\n-" : table(["OK", "PHASE", "JOB", "COMMIT", "STAGE_REF", "ELAPSED"], [[sourceMirrorSync.ok, sourceMirrorSync.phase, sourceMirrorSync.jobName, short(record(sourceMirrorSync.payload).mirrorCommit), short(record(sourceMirrorSync.payload).stageRef), sourceMirrorSync.elapsedMs ?? "-"]]),
|
||||
"",
|
||||
Object.keys(targetValidation).length === 0 ? "TARGET_VALIDATION\n-" : table(["OK", "STATUS", "BUSINESS", "ERROR_TITLE", "SCENARIO", "RUN", "OBSERVER", "REPORT", "FINDINGS", "ARTIFACTS"], [[
|
||||
|
||||
@@ -280,6 +280,7 @@ function runSentinelControlPlane(state: SentinelCicdState, options: Extract<WebP
|
||||
}),
|
||||
observed,
|
||||
statusDiagnosis,
|
||||
drillDown: controlPlaneDrillDown(state, pipelineRun, null),
|
||||
warnings: mergeWarnings(observedWarnings, record(statusDiagnosis).warning),
|
||||
blocker: observedReady
|
||||
? null
|
||||
@@ -321,6 +322,7 @@ function runSentinelPublishCurrent(state: SentinelCicdState, options: Extract<We
|
||||
budget: publishCurrentBudget(state),
|
||||
validationPlan: publishCurrentHealthValidationPlan(state),
|
||||
stageBudgets: publishCurrentStageBudgets(state),
|
||||
drillDown: controlPlaneDrillDown(state, sentinelPipelineRunName(state, options.rerun), null),
|
||||
blocker: state.configReady && state.sourceHead.ok ? null : { code: "sentinel-publish-current-plan-blocked", reason: "sentinel config or source head is not ready" },
|
||||
next: publishCurrentNext(state),
|
||||
valuesRedacted: true,
|
||||
@@ -396,6 +398,7 @@ function runSentinelPublishCurrentConfirmedInner(state: SentinelCicdState, optio
|
||||
timings: publishCurrentStageTimings({ publish }, null, elapsedMs),
|
||||
slowStages: publishCurrentSlowStages(state, publishCurrentStageTimings({ publish }, null, elapsedMs), budgetSeconds),
|
||||
withinBudget: elapsedMs <= budgetSeconds * 1000,
|
||||
drillDown: controlPlaneDrillDown(state, record(publish).jobName ?? sentinelPipelineRunName(state, options.rerun), null),
|
||||
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)),
|
||||
@@ -481,6 +484,7 @@ function runSentinelPublishCurrentConfirmedInner(state: SentinelCicdState, optio
|
||||
withinBudget,
|
||||
timings,
|
||||
slowStages,
|
||||
drillDown: controlPlaneDrillDown(state, record(controlResult).pipelineRun ?? record(controlResult.publish).jobName ?? sentinelPipelineRunName(state, options.rerun), record(controlResult.flush)),
|
||||
warnings: mergeWarnings(controlResult.warnings, publishCurrentBudgetWarnings(slowStages, withinBudget, budgetSeconds, elapsedMs)),
|
||||
blocker,
|
||||
next: publishCurrentNext(state),
|
||||
@@ -1763,6 +1767,7 @@ function sentinelControlPlaneConfirmedResult(state: SentinelCicdState, options:
|
||||
observed,
|
||||
targetValidation,
|
||||
elapsedMs,
|
||||
drillDown: controlPlaneDrillDown(state, publishPipelineRun, flush),
|
||||
warnings: Array.from(new Set([
|
||||
...sentinelCicdElapsedWarnings(elapsedMs, "sentinel control-plane confirm-wait", cicdWaitWarningSeconds),
|
||||
...sentinelCicdElapsedWarnings(record(sourceMirrorSync).elapsedMs, "sentinel source mirror sync", cicdWaitWarningSeconds),
|
||||
@@ -2462,6 +2467,31 @@ function controlPlaneRecoveryNext(state: SentinelCicdState, ok: boolean, publish
|
||||
};
|
||||
}
|
||||
|
||||
function controlPlaneDrillDown(state: SentinelCicdState, pipelineRun: unknown, flush: unknown): Record<string, unknown> {
|
||||
const pipelineRunName = nonEmptyString(pipelineRun);
|
||||
const next = controlPlaneNext(state, "status");
|
||||
const pacBase = "bun scripts/cli.ts platform-infra pipelines-as-code";
|
||||
const flushRecord = record(flush);
|
||||
const flushNext = record(flushRecord.next);
|
||||
const flushJob = record(flushRecord.job);
|
||||
return {
|
||||
pipelineRun: pipelineRunName,
|
||||
pipelineRunStatus: pipelineRunName === null
|
||||
? null
|
||||
: `${pacBase} history --target ${state.spec.nodeId} --id ${pipelineRunName}`,
|
||||
pipelineRunFull: pipelineRunName === null
|
||||
? null
|
||||
: `${pacBase} history --target ${state.spec.nodeId} --id ${pipelineRunName} --full`,
|
||||
consumerStatus: `${pacBase} status --target ${state.spec.nodeId} --consumer sentinel-${state.spec.nodeId.toLowerCase()}-${state.spec.lane}`,
|
||||
imageStatus: `bun scripts/cli.ts web-probe sentinel image status --node ${state.spec.nodeId} --lane ${state.spec.lane}${sentinelCliSuffix(state)}`,
|
||||
gitMirrorStatus: next.gitMirrorStatus,
|
||||
gitMirrorFlush: next.gitMirrorFlush,
|
||||
asyncFlushJob: nonEmptyString(flushJob.id),
|
||||
asyncFlushStatus: nonEmptyString(flushNext.status),
|
||||
valuesRedacted: true,
|
||||
};
|
||||
}
|
||||
|
||||
function applySentinelRuntimeSecrets(state: SentinelCicdState, timeoutSeconds: number): Record<string, unknown> {
|
||||
const sourcesByPurpose = new Map<string, Record<string, unknown>>();
|
||||
for (const source of arrayAt(state.secrets, "sources").map(record)) {
|
||||
|
||||
@@ -27,6 +27,23 @@ duration_seconds() {
|
||||
echo null
|
||||
}
|
||||
|
||||
json_normalize() {
|
||||
UNIDESK_JSON_NORMALIZE_INPUT="${1:-}" node <<'NODE'
|
||||
const input = String(process.env.UNIDESK_JSON_NORMALIZE_INPUT || '').trim();
|
||||
if (!input) {
|
||||
process.stdout.write('{}');
|
||||
process.exit(0);
|
||||
}
|
||||
for (let end = input.length; end > 0; end -= 1) {
|
||||
try {
|
||||
process.stdout.write(JSON.stringify(JSON.parse(input.slice(0, end))));
|
||||
process.exit(0);
|
||||
} catch {}
|
||||
}
|
||||
process.stdout.write('{}');
|
||||
NODE
|
||||
}
|
||||
|
||||
api_url() {
|
||||
prepare_gitea_api_base
|
||||
printf '%s/api/v1/%s' "$UNIDESK_PAC_GITEA_API_BASE_URL" "$1"
|
||||
@@ -632,29 +649,35 @@ function envReuseOf(item) {
|
||||
const env = item.envReuse || {};
|
||||
return env.dependencyReuse || env.mode || item.envReuseStatus || null;
|
||||
}
|
||||
process.stdout.write(JSON.stringify(image ? {
|
||||
imageStatus: image.imageStatus || image.status || (image.digestRef ? 'built' : null),
|
||||
envIdentity: image.envIdentity || null,
|
||||
envReuse: envReuseOf(image),
|
||||
nodeDepsReuse: null,
|
||||
buildCache: null,
|
||||
digest: digestOf(image),
|
||||
gitopsCommit: image.gitopsCommit || null,
|
||||
sourceCommit: image.sourceCommit || null,
|
||||
valuesPrinted: false,
|
||||
} : humanEnv ? {
|
||||
imageStatus: 'published',
|
||||
envIdentity: null,
|
||||
envReuse: humanEnv.envReuse,
|
||||
nodeDepsReuse: humanEnv.nodeDeps,
|
||||
buildCache: humanEnv.cache,
|
||||
buildPackage: humanEnv.buildPackage,
|
||||
buildNetwork: humanEnv.buildNetwork,
|
||||
digest: null,
|
||||
gitopsCommit: null,
|
||||
sourceCommit: null,
|
||||
valuesPrinted: false,
|
||||
} : { valuesPrinted: false }));
|
||||
let out = { valuesPrinted: false };
|
||||
if (image) {
|
||||
out = {
|
||||
imageStatus: image.imageStatus || image.status || (image.digestRef ? 'built' : null),
|
||||
envIdentity: image.envIdentity || null,
|
||||
envReuse: envReuseOf(image),
|
||||
nodeDepsReuse: null,
|
||||
buildCache: null,
|
||||
digest: digestOf(image),
|
||||
gitopsCommit: image.gitopsCommit || null,
|
||||
sourceCommit: image.sourceCommit || null,
|
||||
valuesPrinted: false,
|
||||
};
|
||||
} else if (humanEnv) {
|
||||
out = {
|
||||
imageStatus: 'published',
|
||||
envIdentity: null,
|
||||
envReuse: humanEnv.envReuse,
|
||||
nodeDepsReuse: humanEnv.nodeDeps,
|
||||
buildCache: humanEnv.cache,
|
||||
buildPackage: humanEnv.buildPackage,
|
||||
buildNetwork: humanEnv.buildNetwork,
|
||||
digest: null,
|
||||
gitopsCommit: null,
|
||||
sourceCommit: null,
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
process.stdout.write(JSON.stringify(out));
|
||||
NODE
|
||||
rm -f "$log_file"
|
||||
}
|
||||
@@ -701,6 +724,149 @@ NODE
|
||||
rm -f "$deploy_file"
|
||||
}
|
||||
|
||||
cicd_diagnostics() {
|
||||
params_file=$(mktemp)
|
||||
pipelines_file=$(mktemp)
|
||||
artifact_file=$(mktemp)
|
||||
argo_file=$(mktemp)
|
||||
runtime_file=$(mktemp)
|
||||
printf '%s' "$UNIDESK_PAC_PARAMS_JSON" >"$params_file"
|
||||
printf '%s' "${1:-[]}" >"$pipelines_file"
|
||||
printf '%s' "${2:-{}}" >"$artifact_file"
|
||||
printf '%s' "${3:-{}}" >"$argo_file"
|
||||
printf '%s' "${4:-{}}" >"$runtime_file"
|
||||
probe_env=$(node - "$params_file" "$pipelines_file" <<'NODE'
|
||||
const fs = require('node:fs');
|
||||
const params = JSON.parse(fs.readFileSync(process.argv[2], 'utf8') || '{}');
|
||||
const pipelines = JSON.parse(fs.readFileSync(process.argv[3], 'utf8') || '[]');
|
||||
const latest = Array.isArray(pipelines) ? pipelines[0] || {} : {};
|
||||
const sourceCommit = latest.sourceCommit || null;
|
||||
const tag = sourceCommit ? String(sourceCommit).slice(0, 12) : '';
|
||||
const imageRepository = typeof params.image_repository === 'string' ? params.image_repository : '';
|
||||
const probeBase = typeof params.registry_probe_base === 'string' ? params.registry_probe_base.replace(/\/+$/u, '') : '';
|
||||
let registryUrl = '';
|
||||
if (imageRepository && tag) {
|
||||
const firstSlash = imageRepository.indexOf('/');
|
||||
const registry = firstSlash >= 0 ? imageRepository.slice(0, firstSlash) : imageRepository;
|
||||
const path = firstSlash >= 0 ? imageRepository.slice(firstSlash + 1) : '';
|
||||
const base = probeBase || (registry ? `http://${registry}` : '');
|
||||
registryUrl = base && path ? `${base}/v2/${path}/manifests/${tag}` : '';
|
||||
}
|
||||
function line(key, value) {
|
||||
process.stdout.write(`${key}=${Buffer.from(String(value || ''), 'utf8').toString('base64')}\n`);
|
||||
}
|
||||
line('imageRepository', imageRepository);
|
||||
line('registryProbeBase', probeBase);
|
||||
line('sourceCommit', sourceCommit || '');
|
||||
line('tag', tag);
|
||||
line('registryUrl', registryUrl);
|
||||
line('sentinelId', params.sentinel_id || '');
|
||||
line('gitopsBranch', params.gitops_branch || '');
|
||||
line('gitopsManifestPath', params.gitops_manifest_path || '');
|
||||
NODE
|
||||
)
|
||||
image_repository=$(printf '%s\n' "$probe_env" | sed -n 's/^imageRepository=//p' | base64 -d 2>/dev/null || true)
|
||||
registry_probe_base=$(printf '%s\n' "$probe_env" | sed -n 's/^registryProbeBase=//p' | base64 -d 2>/dev/null || true)
|
||||
source_commit=$(printf '%s\n' "$probe_env" | sed -n 's/^sourceCommit=//p' | base64 -d 2>/dev/null || true)
|
||||
image_tag=$(printf '%s\n' "$probe_env" | sed -n 's/^tag=//p' | base64 -d 2>/dev/null || true)
|
||||
registry_url=$(printf '%s\n' "$probe_env" | sed -n 's/^registryUrl=//p' | base64 -d 2>/dev/null || true)
|
||||
sentinel_id=$(printf '%s\n' "$probe_env" | sed -n 's/^sentinelId=//p' | base64 -d 2>/dev/null || true)
|
||||
gitops_branch=$(printf '%s\n' "$probe_env" | sed -n 's/^gitopsBranch=//p' | base64 -d 2>/dev/null || true)
|
||||
gitops_manifest_path=$(printf '%s\n' "$probe_env" | sed -n 's/^gitopsManifestPath=//p' | base64 -d 2>/dev/null || true)
|
||||
registry_present=false
|
||||
registry_digest=""
|
||||
if [ -n "$registry_url" ]; then
|
||||
header_file=$(mktemp)
|
||||
if curl -fsS -D "$header_file" -o /dev/null -H 'Accept: application/vnd.docker.distribution.manifest.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.docker.distribution.manifest.list.v2+json' "$registry_url" >/dev/null 2>&1; then
|
||||
registry_present=true
|
||||
fi
|
||||
headers=$(cat "$header_file" 2>/dev/null || true)
|
||||
rm -f "$header_file"
|
||||
if printf '%s' "$headers" | grep -qi '^HTTP/.* 200'; then registry_present=true; fi
|
||||
registry_digest=$(printf '%s\n' "$headers" | awk 'BEGIN{IGNORECASE=1} /^Docker-Content-Digest:/ { sub(/\r$/,"",$2); print $2; exit }')
|
||||
fi
|
||||
registry_present_json=false
|
||||
if [ "$registry_present" = true ]; then registry_present_json=true; fi
|
||||
export UNIDESK_PAC_DIAG_IMAGE_REPOSITORY="$image_repository"
|
||||
export UNIDESK_PAC_DIAG_REGISTRY_PROBE_BASE="$registry_probe_base"
|
||||
export UNIDESK_PAC_DIAG_SOURCE_COMMIT="$source_commit"
|
||||
export UNIDESK_PAC_DIAG_IMAGE_TAG="$image_tag"
|
||||
export UNIDESK_PAC_DIAG_REGISTRY_URL="$registry_url"
|
||||
export UNIDESK_PAC_DIAG_REGISTRY_PRESENT="$registry_present_json"
|
||||
export UNIDESK_PAC_DIAG_REGISTRY_DIGEST="$registry_digest"
|
||||
export UNIDESK_PAC_DIAG_SENTINEL_ID="$sentinel_id"
|
||||
export UNIDESK_PAC_DIAG_GITOPS_BRANCH="$gitops_branch"
|
||||
export UNIDESK_PAC_DIAG_GITOPS_MANIFEST_PATH="$gitops_manifest_path"
|
||||
node - "$params_file" "$pipelines_file" "$artifact_file" "$argo_file" "$runtime_file" <<'NODE'
|
||||
const fs = require('node:fs');
|
||||
function parseLoose(path, fallback) {
|
||||
const input = fs.readFileSync(path, 'utf8').trim();
|
||||
if (!input) return fallback;
|
||||
for (let end = input.length; end > 0; end -= 1) {
|
||||
try { return JSON.parse(input.slice(0, end)); } catch {}
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
const params = parseLoose(process.argv[2], {});
|
||||
const pipelines = parseLoose(process.argv[3], []);
|
||||
const artifact = parseLoose(process.argv[4], {});
|
||||
const argo = parseLoose(process.argv[5], {});
|
||||
const runtime = parseLoose(process.argv[6], {});
|
||||
const latest = Array.isArray(pipelines) ? pipelines[0] || {} : {};
|
||||
const registryPresent = process.env.UNIDESK_PAC_DIAG_REGISTRY_PRESENT === 'true';
|
||||
const sourceCommit = process.env.UNIDESK_PAC_DIAG_SOURCE_COMMIT || latest.sourceCommit || null;
|
||||
const registryDigest = process.env.UNIDESK_PAC_DIAG_REGISTRY_DIGEST || null;
|
||||
const expectedDigest = registryDigest || artifact.digest || null;
|
||||
const runtimeImage = runtime.image || null;
|
||||
const runtimeMatches = !expectedDigest || (typeof runtimeImage === 'string' && runtimeImage.includes(expectedDigest));
|
||||
const gitopsReady = Boolean(artifact.gitopsCommit || argo.revision);
|
||||
let code = 'pac-diagnostics-not-applicable';
|
||||
let phase = 'not-applicable';
|
||||
let ok = true;
|
||||
let hint = 'consumer has no sentinel image_repository diagnostic config';
|
||||
if (params.image_repository) {
|
||||
if (!sourceCommit) {
|
||||
ok = false; code = 'sentinel-pac-source-unknown'; phase = 'source-unknown'; hint = 'latest PaC PipelineRun did not expose a source commit';
|
||||
} else if (!registryPresent) {
|
||||
ok = false; code = 'sentinel-pac-registry-missing'; phase = 'source-ready-registry-missing'; hint = 'PaC source commit is known but the configured registry tag is missing';
|
||||
} else if (!gitopsReady) {
|
||||
ok = false; code = 'sentinel-pac-gitops-missing'; phase = 'registry-ready-gitops-missing'; hint = 'registry tag exists but GitOps revision is not visible in PaC artifact or Argo status';
|
||||
} else if (argo.sync !== 'Synced' || argo.health !== 'Healthy') {
|
||||
ok = false; code = 'sentinel-pac-argo-not-ready'; phase = 'gitops-ready-argo-pending'; hint = 'GitOps exists but Argo is not Synced/Healthy';
|
||||
} else if (!runtimeMatches) {
|
||||
ok = false; code = 'sentinel-pac-runtime-not-aligned'; phase = 'argo-ready-runtime-mismatch'; hint = 'runtime image does not match the registry digest observed for the source commit';
|
||||
} else {
|
||||
code = 'sentinel-pac-ready'; phase = 'ready'; hint = 'source, registry tag, GitOps, Argo and runtime are aligned';
|
||||
}
|
||||
}
|
||||
process.stdout.write(JSON.stringify({
|
||||
ok,
|
||||
code,
|
||||
phase,
|
||||
hint,
|
||||
sourceCommit,
|
||||
imageRepository: process.env.UNIDESK_PAC_DIAG_IMAGE_REPOSITORY || null,
|
||||
registryProbeBase: process.env.UNIDESK_PAC_DIAG_REGISTRY_PROBE_BASE || null,
|
||||
imageTag: process.env.UNIDESK_PAC_DIAG_IMAGE_TAG || null,
|
||||
registry: {
|
||||
present: registryPresent,
|
||||
digest: registryDigest,
|
||||
url: process.env.UNIDESK_PAC_DIAG_REGISTRY_URL || null,
|
||||
},
|
||||
gitops: {
|
||||
branch: process.env.UNIDESK_PAC_DIAG_GITOPS_BRANCH || null,
|
||||
manifestPath: process.env.UNIDESK_PAC_DIAG_GITOPS_MANIFEST_PATH || null,
|
||||
commit: artifact.gitopsCommit || argo.revision || null,
|
||||
},
|
||||
argo: { sync: argo.sync || null, health: argo.health || null, revision: argo.revision || null },
|
||||
runtime: { image: runtimeImage, digest: runtime.digest || null, readyReplicas: runtime.readyReplicas ?? null, replicas: runtime.replicas ?? null },
|
||||
pipelineRun: latest.name || null,
|
||||
valuesPrinted: false,
|
||||
}));
|
||||
NODE
|
||||
rm -f "$params_file" "$pipelines_file" "$artifact_file" "$argo_file" "$runtime_file"
|
||||
}
|
||||
|
||||
hook_summary() {
|
||||
hooks=$(gitea_api GET "repos/$UNIDESK_PAC_GITEA_OWNER/$UNIDESK_PAC_GITEA_REPO/hooks" 2>/dev/null || echo '[]')
|
||||
HOOKS_JSON="$hooks" node <<'NODE'
|
||||
@@ -719,16 +885,18 @@ status_action() {
|
||||
latest=$(printf '%s' "$pipelines" | node -e 'const fs=require("fs"); const a=JSON.parse(fs.readFileSync(0,"utf8")||"[]"); process.stdout.write(a[0]?.name||"")')
|
||||
export UNIDESK_PAC_TARGET_PIPELINERUN="$latest"
|
||||
tasks=$(task_rows)
|
||||
artifact=$(artifact_summary)
|
||||
artifact=$(json_normalize "$(artifact_summary)")
|
||||
hooks=$(hook_summary)
|
||||
argo=$(kubectl -n "$UNIDESK_PAC_ARGO_NAMESPACE" get application "$UNIDESK_PAC_ARGO_APPLICATION" -o json 2>/dev/null | node -e 'const fs=require("fs"); const s=fs.readFileSync(0,"utf8").trim(); if(!s){process.stdout.write("{}"); process.exit(0)} const a=JSON.parse(s); process.stdout.write(JSON.stringify({sync:a.status?.sync?.status||null, health:a.status?.health?.status||null, revision:a.status?.sync?.revision||null}))' || echo '{}')
|
||||
runtime=$(runtime_summary)
|
||||
printf '{"ok":%s,"crdPresent":%s,"controllerReady":"%s","repositoryCondition":"%s","webhooks":%s,"pipelineRuns":%s,"taskRuns":%s,"artifact":%s,"argo":%s,"runtime":%s,"valuesPrinted":false}\n' \
|
||||
argo=$(json_normalize "$argo")
|
||||
runtime=$(json_normalize "$(runtime_summary)")
|
||||
diagnostics=$(cicd_diagnostics "$pipelines" "$artifact" "$argo" "$runtime")
|
||||
printf '{"ok":%s,"crdPresent":%s,"controllerReady":"%s","repositoryCondition":"%s","webhooks":%s,"pipelineRuns":%s,"taskRuns":%s,"artifact":%s,"argo":%s,"runtime":%s,"diagnostics":%s,"valuesPrinted":false}\n' \
|
||||
"$( [ -n "$crd" ] && [ "$controller_ready" != "0/0" ] && echo true || echo false )" \
|
||||
"$( [ -n "$crd" ] && echo true || echo false )" \
|
||||
"$(json_string "$controller_ready")" \
|
||||
"$(json_string "$repository_condition")" \
|
||||
"$hooks" "$pipelines" "$tasks" "$artifact" "$argo" "$runtime"
|
||||
"$hooks" "$pipelines" "$tasks" "$artifact" "$argo" "$runtime" "$diagnostics"
|
||||
}
|
||||
|
||||
history_action() {
|
||||
|
||||
@@ -615,6 +615,7 @@ function statusSummary(payload: Record<string, unknown>): Record<string, unknown
|
||||
const taskRuns = arrayRecords(payload.taskRuns);
|
||||
const argo = record(payload.argo);
|
||||
const runtime = record(payload.runtime);
|
||||
const diagnostics = record(payload.diagnostics);
|
||||
const artifact = {
|
||||
...record(payload.artifact),
|
||||
imageStatus: record(payload.artifact).imageStatus ?? (runtime.image === undefined ? undefined : "runtime"),
|
||||
@@ -622,7 +623,7 @@ function statusSummary(payload: Record<string, unknown>): Record<string, unknown
|
||||
gitopsCommit: record(payload.artifact).gitopsCommit ?? argo.revision,
|
||||
};
|
||||
return {
|
||||
ready: payload.crdPresent === true && String(payload.controllerReady ?? "0/0") !== "0/0",
|
||||
ready: payload.crdPresent === true && String(payload.controllerReady ?? "0/0") !== "0/0" && diagnostics.ok !== false,
|
||||
crdPresent: payload.crdPresent === true,
|
||||
controllerReady: payload.controllerReady,
|
||||
repositoryCondition: payload.repositoryCondition,
|
||||
@@ -632,6 +633,7 @@ function statusSummary(payload: Record<string, unknown>): Record<string, unknown
|
||||
artifact,
|
||||
argo,
|
||||
runtime,
|
||||
diagnostics,
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
@@ -781,6 +783,7 @@ function renderStatus(result: Record<string, unknown>): RenderedCliResult {
|
||||
const taskRuns = arrayRecords(summary.taskRuns);
|
||||
const artifact = record(summary.artifact);
|
||||
const argo = record(summary.argo);
|
||||
const diagnostics = record(summary.diagnostics);
|
||||
const lines = [
|
||||
"PLATFORM-INFRA PIPELINES-AS-CODE STATUS",
|
||||
...(coverage.length === 0 ? [] : [
|
||||
@@ -802,6 +805,19 @@ function renderStatus(result: Record<string, unknown>): RenderedCliResult {
|
||||
"ARGO",
|
||||
...table(["SYNC", "HEALTH", "REVISION"], [[stringValue(argo.sync), stringValue(argo.health), short(stringValue(argo.revision))]]),
|
||||
"",
|
||||
"CICD DIAGNOSIS",
|
||||
...table(["OK", "CODE", "PHASE", "SOURCE", "REGISTRY", "GITOPS", "RUNTIME"], [[
|
||||
boolText(diagnostics.ok !== false),
|
||||
stringValue(diagnostics.code),
|
||||
stringValue(diagnostics.phase),
|
||||
short(stringValue(diagnostics.sourceCommit)),
|
||||
registryText(record(diagnostics.registry)),
|
||||
short(stringValue(record(diagnostics.gitops).commit)),
|
||||
runtimeText(record(diagnostics.runtime)),
|
||||
]]),
|
||||
` hint: ${compactLine(stringValue(diagnostics.hint))}`,
|
||||
` pipeline-run: ${latest.name === undefined ? "-" : `bun scripts/cli.ts platform-infra pipelines-as-code history --target ${stringValue(record(result.target).id)} --id ${stringValue(latest.name)}`}`,
|
||||
"",
|
||||
"NEXT",
|
||||
` full: ${stringValue(record(result.next).status)} --full`,
|
||||
` all-history: ${stringValue(record(result.next).history).replace(/ --consumer [^ ]+/u, "")} --limit 10`,
|
||||
@@ -1042,6 +1058,20 @@ function envReuseText(envReuse: Record<string, unknown>): string {
|
||||
return parts.length === 0 ? "-" : parts.join(",");
|
||||
}
|
||||
|
||||
function registryText(registry: Record<string, unknown>): string {
|
||||
if (Object.keys(registry).length === 0) return "-";
|
||||
const present = registry.present === true ? "present" : "missing";
|
||||
const digest = short(stringValue(registry.digest), 18);
|
||||
return digest === "-" ? present : `${present}:${digest}`;
|
||||
}
|
||||
|
||||
function runtimeText(runtime: Record<string, unknown>): string {
|
||||
if (Object.keys(runtime).length === 0) return "-";
|
||||
const ready = `${stringValue(runtime.readyReplicas)}/${stringValue(runtime.replicas)}`;
|
||||
const digest = short(stringValue(runtime.digest), 18);
|
||||
return digest === "-" ? ready : `${ready}:${digest}`;
|
||||
}
|
||||
|
||||
function renderHistoryDetail(row: Record<string, unknown>): string[] {
|
||||
const envReuse = record(row.envReuse);
|
||||
const taskRuns = record(row.taskRuns);
|
||||
|
||||
Reference in New Issue
Block a user