Merge pull request #2699 from pikasTech/fix/pac-delivery-observation-mode
修复 PaC 配置型 consumer 的发布观测误阻塞
This commit is contained in:
@@ -113,7 +113,11 @@ bun scripts/cli.ts hwlab nodes control-plane legacy-cicd --help
|
||||
- 禁止在入口内部调用完整 `status`、同步读取 TaskRun/log、执行 Git fetch 或拼接第二个 history 状态源;
|
||||
- 任一批量读取失败时必须保留 typed `readErrors` 和最后成功阶段,不得提高 YAML timeout 或把读取失败投影为对象缺失;
|
||||
- 固定 `remoteCaptureCount=1`、`mutation=false`,不得再手工串联 `status`、`history`、`delivery-timing` 或裸 Kubernetes 对象查询;
|
||||
- `complete` 表示 PipelineRun、exact-source runtime 与 Argo 全部就绪;`partial` 保留已成立的 exact-source 证据并明确 Argo 或 runtime 首断点;`blocked` 表示目标读取失败、source 不匹配、PipelineRun 未成功或当前 runtime 未观察到该 source;
|
||||
- runtime 证据是否适用只读 owning YAML 的 `consumers[].deliveryObservation.runtimeEvidence`:
|
||||
- `required` 要求 exact-source workload 存在且 ready;
|
||||
- `not-applicable` 用于只有配置/控制面交付、没有独立 workload 的 consumer,缺少 workload 不得阻塞;
|
||||
- 字段缺失时按非核心配置回退到 `required`,并输出配置路径、回退值和 `blocking=false` warning;
|
||||
- `complete` 表示 PipelineRun、适用的 runtime 证据与 Argo 全部就绪;`partial` 保留已成立的证据并明确 Argo 或适用 runtime 首断点;`blocked` 表示目标读取失败、source 不匹配、PipelineRun 未成功或必需 runtime 未观察到该 source;
|
||||
- TaskRun artifact 日志默认延后时只披露 `artifactEvidence.deferred=true`,不放宽既有 status evaluator,也不伪造 provenance。
|
||||
|
||||
- 新增 PaC consumer 的首次引导:
|
||||
|
||||
@@ -14,6 +14,7 @@ metadata:
|
||||
- 1769
|
||||
- 1802
|
||||
- 1811
|
||||
- 2165
|
||||
- 2274
|
||||
- 2628
|
||||
defaults:
|
||||
@@ -441,6 +442,8 @@ consumers:
|
||||
pipelineRunPrefix: unidesk-host-
|
||||
argoNamespace: argocd
|
||||
argoApplication: unidesk-host
|
||||
deliveryObservation:
|
||||
runtimeEvidence: required
|
||||
closeoutGitOpsMirrorFlush: true
|
||||
closeoutGitOpsMirrorLane: v03
|
||||
argoBootstrap:
|
||||
@@ -459,6 +462,8 @@ consumers:
|
||||
pipelineRunPrefix: platform-infra-gitea-nc01-
|
||||
argoNamespace: argocd
|
||||
argoApplication: platform-infra-gitea-nc01
|
||||
deliveryObservation:
|
||||
runtimeEvidence: not-applicable
|
||||
closeoutGitOpsMirrorFlush: false
|
||||
- id: platform-infra-sub2rank-nc01
|
||||
repositoryRef: sub2rank-nc01
|
||||
@@ -469,6 +474,8 @@ consumers:
|
||||
pipelineRunPrefix: sub2rank-nc01
|
||||
argoNamespace: argocd
|
||||
argoApplication: sub2rank-nc01
|
||||
deliveryObservation:
|
||||
runtimeEvidence: required
|
||||
closeoutGitOpsMirrorFlush: true
|
||||
closeoutGitOpsMirrorLane: v03
|
||||
argoBootstrap:
|
||||
@@ -508,6 +515,8 @@ consumers:
|
||||
pipelineRunPrefix: sub2rank-development-nc01
|
||||
argoNamespace: argocd
|
||||
argoApplication: sub2rank-development-nc01
|
||||
deliveryObservation:
|
||||
runtimeEvidence: required
|
||||
closeoutGitOpsMirrorFlush: false
|
||||
argoBootstrap:
|
||||
project: default
|
||||
@@ -546,6 +555,8 @@ consumers:
|
||||
pipelineRunPrefix: hwpod-home-nc01
|
||||
argoNamespace: argocd
|
||||
argoApplication: hwpod-home-nc01
|
||||
deliveryObservation:
|
||||
runtimeEvidence: required
|
||||
closeoutGitOpsMirrorFlush: true
|
||||
closeoutGitOpsMirrorLane: v03
|
||||
argoBootstrap:
|
||||
@@ -634,6 +645,8 @@ consumers:
|
||||
pipelineRunPrefix: selfmedia-nc01
|
||||
argoNamespace: argocd
|
||||
argoApplication: selfmedia-nc01
|
||||
deliveryObservation:
|
||||
runtimeEvidence: required
|
||||
closeoutGitOpsMirrorFlush: false
|
||||
argoBootstrap:
|
||||
project: default
|
||||
@@ -675,6 +688,8 @@ consumers:
|
||||
pipelineRunPrefix: selfmedia-production-nc01
|
||||
argoNamespace: argocd
|
||||
argoApplication: selfmedia-production-nc01
|
||||
deliveryObservation:
|
||||
runtimeEvidence: required
|
||||
closeoutGitOpsMirrorFlush: false
|
||||
params:
|
||||
source_branch: release
|
||||
@@ -729,6 +744,8 @@ consumers:
|
||||
pipelineRunPrefix: pikaoa-dev-nc01
|
||||
argoNamespace: argocd
|
||||
argoApplication: pikaoa-dev-nc01
|
||||
deliveryObservation:
|
||||
runtimeEvidence: required
|
||||
closeoutGitOpsMirrorFlush: false
|
||||
argoBootstrap:
|
||||
project: default
|
||||
@@ -770,6 +787,8 @@ consumers:
|
||||
pipelineRunPrefix: pikaoa-nc01
|
||||
argoNamespace: argocd
|
||||
argoApplication: pikaoa-nc01
|
||||
deliveryObservation:
|
||||
runtimeEvidence: required
|
||||
closeoutGitOpsMirrorFlush: false
|
||||
params:
|
||||
source_branch: release
|
||||
@@ -911,6 +930,8 @@ templates:
|
||||
pipelineRunPrefix: "agentrun-${nodeLower}-v02-ci"
|
||||
argoNamespace: argocd
|
||||
argoApplication: "agentrun-${nodeLower}-v02"
|
||||
deliveryObservation:
|
||||
runtimeEvidence: required
|
||||
closeoutGitOpsMirrorFlush: false
|
||||
agentrunRelease:
|
||||
id: "agentrun-${nodeLower}-release"
|
||||
@@ -922,6 +943,8 @@ templates:
|
||||
pipelineRunPrefix: "agentrun-${nodeLower}-release-ci"
|
||||
argoNamespace: argocd
|
||||
argoApplication: "agentrun-${nodeLower}-release"
|
||||
deliveryObservation:
|
||||
runtimeEvidence: required
|
||||
closeoutGitOpsMirrorFlush: false
|
||||
params:
|
||||
git_read_url: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-agentrun.git
|
||||
@@ -944,6 +967,8 @@ templates:
|
||||
pipelineRunPrefix: "hwlab-web-probe-sentinel-${nodeLower}"
|
||||
argoNamespace: argocd
|
||||
argoApplication: "hwlab-web-probe-sentinel-${nodeLower}"
|
||||
deliveryObservation:
|
||||
runtimeEvidence: required
|
||||
closeoutGitOpsMirrorFlush: true
|
||||
closeoutGitOpsMirrorLane: v03
|
||||
hwlabV03:
|
||||
@@ -956,4 +981,6 @@ templates:
|
||||
pipelineRunPrefix: "hwlab-${nodeLower}-v03-ci-poll"
|
||||
argoNamespace: argocd
|
||||
argoApplication: hwlab-node-v03
|
||||
deliveryObservation:
|
||||
runtimeEvidence: required
|
||||
closeoutGitOpsMirrorFlush: true
|
||||
|
||||
+15
@@ -275,6 +275,21 @@ preflight、CI gate 或人工确认门禁。
|
||||
- 禁止在 PipelineRun、Pod 或 Job 启动后临时安装 `kubectl`;
|
||||
- 该默认能力只属于 CI tools image,不得隐式扩大为产品业务镜像或长期 controller 的运行时依赖。
|
||||
|
||||
### 6.10 OPS-TARGET-REQ-010 发布观测证据适用性
|
||||
|
||||
| 编号 | 短名 | 主责模块 | 关联模块 |
|
||||
| --- | --- | --- | --- |
|
||||
| OPS-TARGET-REQ-010 | 观测证据适用性 | PJ2026-01060308 YAML目标治理 | [发布流水](PJ2026-010601-controlled-release.md)、[运维监控](PJ2026-010605-observability-monitoring.md) |
|
||||
|
||||
PaC consumer 必须通过 owning YAML 的
|
||||
`deliveryObservation.runtimeEvidence` 声明发布观测是否需要 workload 证据。
|
||||
|
||||
- `required` consumer 必须观察到与 source commit 精确匹配且 ready 的 workload;
|
||||
- `not-applicable` 只适用于配置或控制面交付,没有独立业务 workload 的 consumer;
|
||||
- `not-applicable` consumer 仍必须满足 source commit 匹配、PipelineRun 成功和 Argo `Synced/Healthy`;
|
||||
- 非核心字段缺失时可以回退到 `required`,但必须输出配置路径、回退值、原因和 `blocking=false` warning;
|
||||
- 观测模式不得改变 PipelineRun、GitOps、Argo 或 runtime authority,也不得新增第二状态源。
|
||||
|
||||
## 7. 过程控制
|
||||
|
||||
本规格的执行 issue 为 [#911](https://github.com/pikasTech/unidesk/issues/911)。源码文件头部应标注 `SPEC: PJ2026-01060308 cicd-yaml-targets draft-2026-06-25-cicd-yaml-targets`;自动生成文件、纯配置、锁文件和无法承载注释头的二进制产物可例外。
|
||||
|
||||
@@ -12,7 +12,15 @@ const commit = "b".repeat(40);
|
||||
function fixture(overrides: Record<string, unknown> = {}): Record<string, unknown> {
|
||||
return {
|
||||
target: { id: "NC01" },
|
||||
consumer: { id: "fixture" },
|
||||
consumer: {
|
||||
id: "fixture",
|
||||
deliveryObservation: {
|
||||
runtimeEvidence: "required",
|
||||
configPath: "config/platform-infra/pipelines-as-code.yaml#consumers.fixture.deliveryObservation.runtimeEvidence",
|
||||
defaulted: false,
|
||||
fallbackReason: null,
|
||||
},
|
||||
},
|
||||
observation: { ok: true },
|
||||
summary: {
|
||||
latestPipelineRun: { name: "fixture-run", status: "True", reason: "Succeeded", sourceCommit: commit },
|
||||
@@ -140,3 +148,38 @@ test("delivery observe preserves a typed snapshot read failure", () => {
|
||||
});
|
||||
assert.equal((result.readErrors as unknown[]).length, 1);
|
||||
});
|
||||
|
||||
test("delivery observe completes a configuration-only consumer without runtime workloads", () => {
|
||||
const status = fixture({ runtime: { workloads: [] } });
|
||||
status.consumer = {
|
||||
id: "fixture",
|
||||
deliveryObservation: {
|
||||
runtimeEvidence: "not-applicable",
|
||||
configPath: "config/platform-infra/pipelines-as-code.yaml#consumers.fixture.deliveryObservation.runtimeEvidence",
|
||||
defaulted: false,
|
||||
fallbackReason: null,
|
||||
},
|
||||
};
|
||||
const result = projectPacDeliveryObserve({ status, sourceCommit: commit });
|
||||
assert.equal(result.status, "complete");
|
||||
assert.equal((result.runtime as Record<string, unknown>).applicable, false);
|
||||
assert.equal((result.runtime as Record<string, unknown>).ready, null);
|
||||
assert.deepEqual(result.firstBreak, null);
|
||||
assert.deepEqual((result.warnings as Record<string, unknown>[])[0], {
|
||||
code: "runtime-evidence-not-applicable",
|
||||
phase: "runtime",
|
||||
blocking: false,
|
||||
mode: "not-applicable",
|
||||
configPath: "config/platform-infra/pipelines-as-code.yaml#consumers.fixture.deliveryObservation.runtimeEvidence",
|
||||
});
|
||||
});
|
||||
|
||||
test("delivery observe defaults missing non-core runtime evidence mode without blocking", () => {
|
||||
const status = fixture();
|
||||
status.consumer = { id: "fixture" };
|
||||
const result = projectPacDeliveryObserve({ status, sourceCommit: commit });
|
||||
assert.equal(result.status, "complete");
|
||||
assert.equal((result.runtime as Record<string, unknown>).evidenceMode, "required");
|
||||
assert.equal((result.warnings as Record<string, unknown>[])[0]?.code, "runtime-evidence-mode-defaulted");
|
||||
assert.equal((result.warnings as Record<string, unknown>[])[0]?.blocking, false);
|
||||
});
|
||||
|
||||
@@ -48,6 +48,12 @@ export function projectPacDeliveryObserve(input: PacDeliveryObserveInput): Recor
|
||||
const runtime = record(summary.runtime);
|
||||
const workloads = arrayRecords(runtime.workloads);
|
||||
const matchingWorkloads = workloads.filter((item) => item.sourceCommit === input.sourceCommit);
|
||||
const deliveryObservation = record(record(input.status.consumer).deliveryObservation);
|
||||
const configuredRuntimeEvidence = stringOrNull(deliveryObservation.runtimeEvidence);
|
||||
const runtimeEvidence = configuredRuntimeEvidence === "not-applicable" ? "not-applicable" : "required";
|
||||
const runtimeEvidenceRequired = runtimeEvidence === "required";
|
||||
const observationConfigPath = stringOrNull(deliveryObservation.configPath);
|
||||
const observationModeDefaulted = deliveryObservation.defaulted === true || configuredRuntimeEvidence === null;
|
||||
const pipelineSourceCommit = stringOrNull(pipeline.sourceCommit);
|
||||
const sourceMatched = pipelineSourceCommit === input.sourceCommit;
|
||||
const pipelineSucceeded = pipeline.status === "True";
|
||||
@@ -55,15 +61,16 @@ export function projectPacDeliveryObserve(input: PacDeliveryObserveInput): Recor
|
||||
const argoReady = argo.sync === "Synced" && argo.health === "Healthy";
|
||||
const observationReady = record(input.status.observation).ok === true;
|
||||
const readErrors = arrayRecords(input.status.readErrors);
|
||||
const state = !observationReady || !sourceMatched || !pipelineSucceeded || matchingWorkloads.length === 0
|
||||
const state = !observationReady || !sourceMatched || !pipelineSucceeded || (runtimeEvidenceRequired && matchingWorkloads.length === 0)
|
||||
? "blocked"
|
||||
: runtimeReady && argoReady
|
||||
: (!runtimeEvidenceRequired || runtimeReady) && argoReady
|
||||
? "complete"
|
||||
: "partial";
|
||||
const firstBreak = firstBreakFor({
|
||||
observationReady,
|
||||
sourceMatched,
|
||||
pipelineSucceeded,
|
||||
runtimeEvidenceRequired,
|
||||
matchingWorkloads,
|
||||
runtimeReady,
|
||||
argoReady,
|
||||
@@ -72,6 +79,24 @@ export function projectPacDeliveryObserve(input: PacDeliveryObserveInput): Recor
|
||||
});
|
||||
|
||||
const artifactMode = record(record(summary.artifact).collector).mode ?? null;
|
||||
const warnings = runtimeEvidence === "not-applicable"
|
||||
? [{
|
||||
code: "runtime-evidence-not-applicable",
|
||||
phase: "runtime",
|
||||
blocking: false,
|
||||
mode: runtimeEvidence,
|
||||
configPath: observationConfigPath,
|
||||
}]
|
||||
: !observationModeDefaulted
|
||||
? []
|
||||
: [{
|
||||
code: "runtime-evidence-mode-defaulted",
|
||||
phase: "runtime",
|
||||
blocking: false,
|
||||
mode: runtimeEvidence,
|
||||
configPath: observationConfigPath,
|
||||
reason: stringOrNull(deliveryObservation.fallbackReason),
|
||||
}];
|
||||
return {
|
||||
ok: state !== "blocked",
|
||||
status: state,
|
||||
@@ -101,9 +126,11 @@ export function projectPacDeliveryObserve(input: PacDeliveryObserveInput): Recor
|
||||
firstCondition: argo.firstCondition ?? null,
|
||||
},
|
||||
runtime: {
|
||||
evidenceMode: runtimeEvidence,
|
||||
applicable: runtimeEvidenceRequired,
|
||||
workloadCount: workloads.length,
|
||||
matchingWorkloadCount: matchingWorkloads.length,
|
||||
ready: runtimeReady,
|
||||
ready: runtimeEvidenceRequired ? runtimeReady : null,
|
||||
workloads: matchingWorkloads.map((item) => ({
|
||||
deployment: item.deployment ?? null,
|
||||
namespace: item.namespace ?? null,
|
||||
@@ -118,6 +145,7 @@ export function projectPacDeliveryObserve(input: PacDeliveryObserveInput): Recor
|
||||
deferred: artifactMode === "taskrun-status-only" || artifactMode === "deferred-single-snapshot",
|
||||
},
|
||||
firstBreak,
|
||||
warnings,
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
@@ -136,7 +164,7 @@ export function renderPacDeliveryObserve(result: Record<string, unknown>): strin
|
||||
`SOURCE expected=${short(text(result.sourceCommit))} observed=${short(text(pipeline.sourceCommit))} matched=${String(pipeline.sourceMatched === true)}`,
|
||||
`PIPELINE id=${text(pipeline.id)} status=${text(pipeline.status)} reason=${text(pipeline.reason)}`,
|
||||
`ARGO sync=${text(argo.sync)} health=${text(argo.health)} revision=${short(text(argo.revision))}`,
|
||||
`RUNTIME matching=${text(runtime.matchingWorkloadCount)}/${text(runtime.workloadCount)} ready=${String(runtime.ready === true)}`,
|
||||
`RUNTIME mode=${text(runtime.evidenceMode)} matching=${text(runtime.matchingWorkloadCount)}/${text(runtime.workloadCount)} ready=${runtime.applicable === true ? String(runtime.ready === true) : "n/a"}`,
|
||||
...workloads.map((item) => ` ${text(item.namespace)}/${text(item.deployment)} ${text(item.readyReplicas)}/${text(item.replicas)} digest=${short(text(item.digest), 24)}`),
|
||||
`FIRST_BREAK ${Object.keys(firstBreak).length === 0 ? "-" : `${text(firstBreak.code)} phase=${text(firstBreak.phase)}`}`,
|
||||
"REMOTE_CAPTURE_COUNT 1",
|
||||
@@ -147,6 +175,7 @@ function firstBreakFor(input: {
|
||||
observationReady: boolean;
|
||||
sourceMatched: boolean;
|
||||
pipelineSucceeded: boolean;
|
||||
runtimeEvidenceRequired: boolean;
|
||||
matchingWorkloads: readonly Record<string, unknown>[];
|
||||
runtimeReady: boolean;
|
||||
argoReady: boolean;
|
||||
@@ -165,8 +194,8 @@ function firstBreakFor(input: {
|
||||
}
|
||||
if (!input.sourceMatched) return { code: "pipeline-source-commit-mismatch", phase: "pipeline" };
|
||||
if (!input.pipelineSucceeded) return { code: "pipeline-not-succeeded", phase: "pipeline" };
|
||||
if (input.matchingWorkloads.length === 0) return { code: "runtime-source-commit-not-observed", phase: "runtime" };
|
||||
if (!input.runtimeReady) return { code: "runtime-workload-not-ready", phase: "runtime" };
|
||||
if (input.runtimeEvidenceRequired && input.matchingWorkloads.length === 0) return { code: "runtime-source-commit-not-observed", phase: "runtime" };
|
||||
if (input.runtimeEvidenceRequired && !input.runtimeReady) return { code: "runtime-workload-not-ready", phase: "runtime" };
|
||||
if (!input.argoReady) {
|
||||
return {
|
||||
code: "argo-not-ready",
|
||||
|
||||
@@ -186,12 +186,14 @@ export interface PacConfigValidationWarning {
|
||||
| "pac-unselected-consumer-parse-invalid"
|
||||
| "pac-unselected-consumer-invalid"
|
||||
| "pac-unselected-repository-parse-invalid"
|
||||
| "pac-unselected-repository-invalid";
|
||||
| "pac-unselected-repository-invalid"
|
||||
| "pac-consumer-delivery-observation-defaulted";
|
||||
readonly object: { readonly kind: "consumer" | "repository"; readonly id: string };
|
||||
readonly consumer: string | null;
|
||||
readonly configPath: string;
|
||||
readonly blocking: false;
|
||||
readonly evidence: string;
|
||||
readonly fallback?: { readonly value: string; readonly reason: string };
|
||||
}
|
||||
|
||||
interface PacRepository {
|
||||
@@ -219,6 +221,12 @@ interface PacConsumer {
|
||||
pipelineRunPrefix: string;
|
||||
argoNamespace: string;
|
||||
argoApplication: string;
|
||||
deliveryObservation: {
|
||||
runtimeEvidence: "required" | "not-applicable";
|
||||
configPath: string;
|
||||
defaulted: boolean;
|
||||
fallbackReason: string | null;
|
||||
};
|
||||
argoBootstrap: {
|
||||
project: string;
|
||||
repoUrl: string;
|
||||
@@ -819,7 +827,22 @@ function parsePacConsumers(
|
||||
const id = rawPacObjectId(entry.value, entry.path, "consumer");
|
||||
try {
|
||||
if (!isRecord(entry.value)) throw new Error(`${configLabel}.${entry.path} must be an object`);
|
||||
values.push(parseConsumer(entry.value, entry.path, defaultRepositoryRef));
|
||||
const consumer = parseConsumer(entry.value, entry.path, defaultRepositoryRef);
|
||||
values.push(consumer);
|
||||
if (consumer.deliveryObservation.defaulted) {
|
||||
warnings.push({
|
||||
code: "pac-consumer-delivery-observation-defaulted",
|
||||
object: { kind: "consumer", id: consumer.id },
|
||||
consumer: consumer.id,
|
||||
configPath: consumer.deliveryObservation.configPath,
|
||||
blocking: false,
|
||||
evidence: pacValueEvidence(consumer.deliveryObservation.fallbackReason ?? "deliveryObservation.runtimeEvidence defaulted"),
|
||||
fallback: {
|
||||
value: consumer.deliveryObservation.runtimeEvidence,
|
||||
reason: consumer.deliveryObservation.fallbackReason ?? "non-core delivery observation mode is not configured",
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
if (selectedConsumerIds.has(id.toLowerCase())) throw error;
|
||||
warnings.push(pacConfigWarning("pac-unselected-consumer-parse-invalid", "consumer", id, entry.path, error));
|
||||
@@ -869,6 +892,7 @@ function parseConsumer(consumer: Record<string, unknown>, path: string, defaultR
|
||||
const argoBootstrap = consumer.argoBootstrap === undefined ? null : y.objectField(consumer, "argoBootstrap", path);
|
||||
const deliveryProvenance = consumer.deliveryProvenance === undefined ? null : parseConsumerDeliveryProvenance(y.objectField(consumer, "deliveryProvenance", path), `${path}.deliveryProvenance`);
|
||||
const runnerServiceAccount = consumer.runnerServiceAccount === undefined ? null : y.objectField(consumer, "runnerServiceAccount", path);
|
||||
const deliveryObservation = parseConsumerDeliveryObservation(consumer.deliveryObservation, path);
|
||||
return {
|
||||
id,
|
||||
node: y.stringField(consumer, "node", path),
|
||||
@@ -878,6 +902,7 @@ function parseConsumer(consumer: Record<string, unknown>, path: string, defaultR
|
||||
pipelineRunPrefix: y.stringField(consumer, "pipelineRunPrefix", path),
|
||||
argoNamespace: y.kubernetesNameField(consumer, "argoNamespace", path),
|
||||
argoApplication: y.kubernetesNameField(consumer, "argoApplication", path),
|
||||
deliveryObservation,
|
||||
argoBootstrap: argoBootstrap === null ? null : {
|
||||
project: y.kubernetesNameField(argoBootstrap, "project", `${path}.argoBootstrap`),
|
||||
repoUrl: urlField(argoBootstrap, "repoUrl", `${path}.argoBootstrap`),
|
||||
@@ -907,6 +932,34 @@ function parseConsumer(consumer: Record<string, unknown>, path: string, defaultR
|
||||
};
|
||||
}
|
||||
|
||||
function parseConsumerDeliveryObservation(value: unknown, path: string): PacConsumer["deliveryObservation"] {
|
||||
const configPath = `${configLabel}#${path}.deliveryObservation.runtimeEvidence`;
|
||||
if (!isRecord(value)) {
|
||||
return {
|
||||
runtimeEvidence: "required",
|
||||
configPath,
|
||||
defaulted: true,
|
||||
fallbackReason: value === undefined
|
||||
? "deliveryObservation.runtimeEvidence is missing"
|
||||
: "deliveryObservation must be an object",
|
||||
};
|
||||
}
|
||||
if (value.runtimeEvidence !== "required" && value.runtimeEvidence !== "not-applicable") {
|
||||
return {
|
||||
runtimeEvidence: "required",
|
||||
configPath,
|
||||
defaulted: true,
|
||||
fallbackReason: "deliveryObservation.runtimeEvidence must be required or not-applicable",
|
||||
};
|
||||
}
|
||||
return {
|
||||
runtimeEvidence: value.runtimeEvidence,
|
||||
configPath,
|
||||
defaulted: false,
|
||||
fallbackReason: null,
|
||||
};
|
||||
}
|
||||
|
||||
function parseFalse(record: Record<string, unknown>, key: string, path: string): false {
|
||||
if (y.booleanField(record, key, path) !== false) throw new Error(`${path}.${key} must be false`);
|
||||
return false;
|
||||
@@ -2594,6 +2647,7 @@ function consumerObservationSummary(consumer: PacConsumer): Record<string, unkno
|
||||
pipelineRunPrefix: consumer.pipelineRunPrefix,
|
||||
argoNamespace: consumer.argoNamespace,
|
||||
argoApplication: consumer.argoApplication,
|
||||
deliveryObservation: consumer.deliveryObservation,
|
||||
repositoryRef: consumer.repositoryRef,
|
||||
params: Object.keys(consumer.params).sort(),
|
||||
runnerServiceAccount: consumer.runnerServiceAccount,
|
||||
@@ -2652,6 +2706,7 @@ function compactPlanJson(result: Record<string, unknown>): Record<string, unknow
|
||||
pipelineRunPrefix: consumer.pipelineRunPrefix,
|
||||
argoNamespace: consumer.argoNamespace,
|
||||
argoApplication: consumer.argoApplication,
|
||||
deliveryObservation: consumer.deliveryObservation,
|
||||
repositoryRef: consumer.repositoryRef,
|
||||
params: consumer.params,
|
||||
runnerServiceAccount: consumer.runnerServiceAccount,
|
||||
@@ -2667,6 +2722,7 @@ function compactPlanJson(result: Record<string, unknown>): Record<string, unknow
|
||||
|
||||
function compactStatusJson(result: Record<string, unknown>): Record<string, unknown> {
|
||||
const consumer = record(result.consumer);
|
||||
const deliveryObservation = record(consumer.deliveryObservation);
|
||||
return {
|
||||
ok: result.ok === true,
|
||||
action: result.action,
|
||||
@@ -2679,6 +2735,7 @@ function compactStatusJson(result: Record<string, unknown>): Record<string, unkn
|
||||
namespace: consumer.namespace,
|
||||
pipeline: consumer.pipeline,
|
||||
argoApplication: consumer.argoApplication,
|
||||
deliveryObservation,
|
||||
},
|
||||
deliveryAuthority: result.deliveryAuthority,
|
||||
warnings: result.warnings,
|
||||
@@ -2919,6 +2976,7 @@ function renderPlan(result: Record<string, unknown>): RenderedCliResult {
|
||||
const config = record(result.config);
|
||||
const repository = record(result.repository);
|
||||
const consumer = record(result.consumer);
|
||||
const deliveryObservation = record(consumer.deliveryObservation);
|
||||
const internalPublish = record(record(config.capabilities).sentinelInternalPublish);
|
||||
const secrets = arrayRecords(result.secrets);
|
||||
const policy = arrayRecords(result.policy);
|
||||
@@ -2933,6 +2991,7 @@ function renderPlan(result: Record<string, unknown>): RenderedCliResult {
|
||||
["trigger", "Pipelines-as-Code", "Gitea push webhook"],
|
||||
["ci", "Tekton", stringValue(consumer.pipeline)],
|
||||
["cd", "Argo", stringValue(consumer.argoApplication)],
|
||||
["runtime evidence", "owning YAML", `${stringValue(deliveryObservation.runtimeEvidence)} (${stringValue(deliveryObservation.configPath)})`],
|
||||
]),
|
||||
"",
|
||||
"SECRETS",
|
||||
|
||||
Reference in New Issue
Block a user