Files
pikasTech-unidesk/scripts/src/sentinel-pac-execution-context.ts
T
2026-07-11 11:42:16 +02:00

94 lines
3.9 KiB
TypeScript

// Responsibility: expose the YAML-owned sentinel internal publish capability without claiming provenance.
import { rootPath } from "./config";
import type { CicdDeliveryAuthority } from "./cicd-delivery-authority";
import type { WebProbeSentinelOptions } from "./hwlab-node-web-sentinel-cicd-shared";
import { readYamlRecord } from "./platform-infra-ops-library";
import { materializeYamlComposition } from "./yaml-composition";
const configPath = "config/platform-infra/pipelines-as-code.yaml";
const configRef = `${configPath}#capabilities.sentinelInternalPublish`;
export interface SentinelPacInternalPublishCapability {
readonly enabled: boolean;
readonly admissionProvenance: "unavailable";
readonly trackingIssue: string;
readonly configRef: string;
readonly valuesPrinted: false;
}
export type SentinelPacInternalPublishAuthorization = {
readonly allowed: false;
readonly mode: "sentinel-pac-internal-publish-blocked";
readonly reason:
| "operator-entrypoint"
| "delivery-authority-not-pac"
| "capability-disabled"
| "admission-provenance-unavailable";
readonly capability: SentinelPacInternalPublishCapability;
readonly valuesPrinted: false;
};
export function readSentinelPacInternalPublishCapability(): SentinelPacInternalPublishCapability {
const raw = readYamlRecord<Record<string, unknown>>(rootPath(...configPath.split("/")), "platform-infra-pipelines-as-code");
const composed = materializeYamlComposition(raw, { label: configPath }).value;
const capabilities = record(composed.capabilities, `${configPath}#capabilities`);
const capability = record(capabilities.sentinelInternalPublish, configRef);
const enabled = booleanField(capability.enabled, `${configRef}.enabled`);
const admissionProvenance = stringField(capability.admissionProvenance, `${configRef}.admissionProvenance`);
if (admissionProvenance !== "unavailable") {
throw new Error(`${configRef}.admissionProvenance must remain unavailable until the admission contract is implemented`);
}
const trackingIssue = stringField(capability.trackingIssue, `${configRef}.trackingIssue`);
if (!/^https:\/\/github\.com\/pikasTech\/unidesk\/issues\/\d+$/u.test(trackingIssue)) {
throw new Error(`${configRef}.trackingIssue must reference the owning GitHub issue`);
}
return {
enabled,
admissionProvenance,
trackingIssue,
configRef,
valuesPrinted: false,
};
}
export function authorizeSentinelPacInternalPublish(
options: WebProbeSentinelOptions,
authority: CicdDeliveryAuthority,
capability = readSentinelPacInternalPublishCapability(),
): SentinelPacInternalPublishAuthorization {
if (options.kind !== "publish" || options.internalExecution !== "pac-controller") {
return denied("operator-entrypoint", capability);
}
if (authority.kind !== "pac-pr-merge") return denied("delivery-authority-not-pac", capability);
if (!capability.enabled) return denied("capability-disabled", capability);
return denied("admission-provenance-unavailable", capability);
}
function denied(
reason: SentinelPacInternalPublishAuthorization["reason"],
capability: SentinelPacInternalPublishCapability,
): SentinelPacInternalPublishAuthorization {
return {
allowed: false,
mode: "sentinel-pac-internal-publish-blocked",
reason,
capability,
valuesPrinted: false,
};
}
function record(value: unknown, path: string): Record<string, unknown> {
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`${path} must be an object`);
return value as Record<string, unknown>;
}
function booleanField(value: unknown, path: string): boolean {
if (typeof value !== "boolean") throw new Error(`${path} must be a boolean`);
return value;
}
function stringField(value: unknown, path: string): string {
if (typeof value !== "string" || value.trim().length === 0) throw new Error(`${path} must be a non-empty string`);
return value.trim();
}