import { existsSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { resolve } from "node:path"; import { spawnSync } from "node:child_process"; import { resolveSelfMediaDeliveryTarget, resolveSelfMediaDeliveryTargetByConfigRef, selfMediaDeliveryConfigRef, selfMediaEffectiveDeployment, type SelfMediaDeliveryTarget, } from "./selfmedia-config"; import { stableJsonSha256 } from "./stable-json"; export interface SelfMediaRendererBinding { readonly consumer: { readonly id: string; readonly node: string; readonly lane: string; readonly namespace: string; readonly pipeline: string; readonly pipelineRunPrefix: string; readonly sourceArtifact: { readonly configRef: string; readonly mode: string; readonly renderer: string; }; }; readonly repository: { readonly id: string; readonly params: Readonly>; }; } export interface SelfMediaRenderedPipeline { readonly pipeline: Record; readonly configRef: string; readonly effectiveConfigSha256: string; readonly sourceWorktreeRemote: string; } export function renderSelfMediaDesiredPipeline(binding: SelfMediaRendererBinding, sourceWorktree: string): SelfMediaRenderedPipeline { const target = resolveSelfMediaDeliveryTargetByConfigRef(binding.consumer.sourceArtifact.configRef); const configRef = selfMediaDeliveryConfigRef(target.id); if (binding.consumer.sourceArtifact.renderer !== "selfmedia-runtime" || binding.consumer.sourceArtifact.mode !== "embedded-pipeline-spec") { throw new Error("selfmedia-runtime requires embedded-pipeline-spec"); } if (binding.consumer.sourceArtifact.configRef !== configRef) { throw new Error(`sourceArtifact.configRef must equal resolved owning selector ${configRef}; observed ${binding.consumer.sourceArtifact.configRef}`); } assertBinding(target, binding); assertServiceRepositoryContract(sourceWorktree); const effectiveDeployment = selfMediaEffectiveDeployment(target); validateRuntimeRender(sourceWorktree, effectiveDeployment); const effectiveDeploymentB64 = Buffer.from(`${Bun.YAML.stringify(effectiveDeployment).trim()}\n`, "utf8").toString("base64"); return { pipeline: pipeline(target, effectiveDeploymentB64), configRef, effectiveConfigSha256: stableJsonSha256(target), sourceWorktreeRemote: target.source.worktreeRemote, }; } export function selfMediaSourceWorktreeRemote(targetId: string): string { return resolveSelfMediaDeliveryTarget(targetId).source.worktreeRemote; } function pipeline(target: SelfMediaDeliveryTarget, effectiveDeploymentB64: string): Record { const params = [ "revision", "source-snapshot-prefix", "git-read-url", "gitops-read-url", "gitops-write-url", "gitops-username", "gitops-secret-name", ]; const taskParams = params.map((name) => ({ name, type: "string" })); const taskParamBindings = params.map((name) => ({ name, value: `$(params.${name})` })); return { apiVersion: "tekton.dev/v1", kind: "Pipeline", metadata: { name: target.ci.pipeline, namespace: target.ci.namespace, labels: { "app.kubernetes.io/name": target.ci.pipeline, "app.kubernetes.io/part-of": "selfmedia-factory", "app.kubernetes.io/managed-by": "unidesk", }, }, spec: { params: taskParams, tasks: [{ name: "build-and-publish", params: taskParamBindings, taskSpec: { params: taskParams, volumes: [ { name: "workspace", emptyDir: { sizeLimit: target.ci.workspaceSize } }, { name: "buildkit-state", hostPath: { path: target.build.envReuse.statePath, type: "DirectoryOrCreate" }, }, { name: "tmp", emptyDir: { sizeLimit: "4Gi" } }, { name: "gitops-token", secret: { secretName: "$(params.gitops-secret-name)", defaultMode: 288, items: [{ key: target.gitops.credentialTokenKey, path: "token" }], }, }, ], steps: [ sourceStep(target, effectiveDeploymentB64), prepareBuildkitStep(target), envReuseStep(target), imageBuildStep(target), gitOpsPublishStep(target), ], }, }], }, }; } function sourceStep(target: SelfMediaDeliveryTarget, effectiveDeploymentB64: string): Record { const proxy = target.build.proxy; return { name: "immutable-source", image: target.ci.toolImage, imagePullPolicy: "IfNotPresent", workingDir: "/workspace", env: [ { name: "HTTP_PROXY", value: requiredString(proxy.http, "build.proxy.http") }, { name: "HTTPS_PROXY", value: requiredString(proxy.https, "build.proxy.https") }, { name: "ALL_PROXY", value: requiredString(proxy.all, "build.proxy.all") }, { name: "NO_PROXY", value: requiredStringArray(proxy.noProxy, "build.proxy.noProxy").join(",") }, ], script: `#!/bin/sh set -eu stage_started_seconds=$(date +%s) source_commit="$(params.revision)" snapshot_prefix="$(params.source-snapshot-prefix)" rm -rf /workspace/source /workspace/release askpass=/tmp/selfmedia-source-git-askpass cat >"$askpass" <<'ASKPASS' #!/bin/sh case "$1" in *Username*) printf '%s' "$SELFMEDIA_GIT_USERNAME" ;; *Password*) cat /var/run/selfmedia-gitops/token ;; *) exit 1 ;; esac ASKPASS chmod 700 "$askpass" export SELFMEDIA_GIT_USERNAME="$(params.gitops-username)" export GIT_ASKPASS="$askpass" export GIT_TERMINAL_PROMPT=0 git clone --filter=blob:none --no-checkout "$(params.git-read-url)" /workspace/source cd /workspace/source git fetch --depth=1 --filter=blob:none origin "+$snapshot_prefix/$source_commit:refs/remotes/origin/selfmedia-source-snapshot" git checkout --detach "$source_commit" test "$(git rev-parse HEAD)" = "$source_commit" rm -f "$askpass" printf '%s' '${effectiveDeploymentB64}' | base64 -d > /workspace/effective-deployment.yaml bun install --frozen-lockfile bun deploy/scripts/prepare-build.ts \\ --config /workspace/effective-deployment.yaml \\ --source-root /workspace/source \\ --source-commit "$source_commit" \\ --source-snapshot-prefix "$snapshot_prefix" \\ --output-dir /workspace/release stage_finished_seconds=$(date +%s) printf '{"ok":true,"phase":"source-prepare","stageTimings":{"sourcePrepareSeconds":%s},"valuesPrinted":false}\n' "$((stage_finished_seconds-stage_started_seconds))" `, securityContext: nonRootSecurity(), volumeMounts: [ { name: "workspace", mountPath: "/workspace" }, { name: "tmp", mountPath: "/tmp" }, { name: "gitops-token", mountPath: "/var/run/selfmedia-gitops", readOnly: true }, ], }; } function prepareBuildkitStep(target: SelfMediaDeliveryTarget): Record { return { name: "prepare-buildkit-state", image: target.ci.toolImage, imagePullPolicy: "IfNotPresent", script: "#!/bin/sh\nset -eu\nmkdir -p /home/user/.local/share/buildkit/.unidesk\nchown 1000:1000 /home/user/.local/share/buildkit /home/user/.local/share/buildkit/.unidesk\n", securityContext: { runAsUser: 0, runAsGroup: 0 }, volumeMounts: [{ name: "buildkit-state", mountPath: "/home/user/.local/share/buildkit" }], }; } function envReuseStep(target: SelfMediaDeliveryTarget): Record { const identityFiles = target.build.envReuse.identityFiles.map((path) => `/workspace/source/${path}`); const identityInputs = [...identityFiles, "/workspace/release/codex-version"] .map(shellSingleQuote) .join(" "); return { name: "env-reuse", image: target.ci.toolImage, imagePullPolicy: "IfNotPresent", script: `#!/bin/sh set -eu stage_started_seconds=$(date +%s) identity_file=/home/user/.local/share/buildkit/.unidesk/selfmedia-env-identity for input in ${identityInputs}; do test -f "$input"; done env_identity="sha256:$(sha256sum ${identityInputs} | sha256sum | awk '{print $1}')" previous_identity="" if [ -f "$identity_file" ]; then previous_identity=$(cat "$identity_file"); fi if [ "$previous_identity" = "$env_identity" ]; then reuse_status=hit previous_present=true else reuse_status=miss if [ -n "$previous_identity" ]; then previous_present=true; else previous_present=false; fi fi printf '%s\n' "$env_identity" > /workspace/env-identity stage_finished_seconds=$(date +%s) printf '{"ok":true,"phase":"env-reuse","envIdentity":"%s","envReuseStatus":"%s","envReuse":{"status":"%s","mode":"${target.build.envReuse.mode}","identitySource":"owning-yaml-source-artifact","previousIdentityPresent":%s},"stageTimings":{"envIdentitySeconds":%s},"valuesPrinted":false}\n' "$env_identity" "$reuse_status" "$reuse_status" "$previous_present" "$((stage_finished_seconds-stage_started_seconds))" `, securityContext: nonRootSecurity(), volumeMounts: [ { name: "workspace", mountPath: "/workspace" }, { name: "buildkit-state", mountPath: "/home/user/.local/share/buildkit" }, ], }; } function imageBuildStep(target: SelfMediaDeliveryTarget): Record { return { name: "image-build", image: target.ci.buildkitImage, imagePullPolicy: "IfNotPresent", workingDir: "/workspace", env: [ { name: "SOURCE_COMMIT", value: "$(params.revision)" }, { name: "BUILDKITD_FLAGS", value: "--oci-worker-no-process-sandbox --oci-worker-net=host --allow-insecure-entitlement network.host" }, ], script: `#!/bin/sh set -eu stage_started_seconds=$(date +%s) /bin/sh /workspace/source/deploy/scripts/build-image.sh cp /workspace/env-identity /home/user/.local/share/buildkit/.unidesk/selfmedia-env-identity stage_finished_seconds=$(date +%s) printf '{"ok":true,"phase":"image-build-complete","envIdentity":"%s","stageTimings":{"imageBuildSeconds":%s},"valuesPrinted":false}\n' "$(cat /workspace/env-identity)" "$((stage_finished_seconds-stage_started_seconds))" `, securityContext: { privileged: true, runAsUser: 1000, runAsGroup: 1000 }, volumeMounts: [ { name: "workspace", mountPath: "/workspace" }, { name: "buildkit-state", mountPath: "/home/user/.local/share/buildkit" }, { name: "tmp", mountPath: "/tmp" }, ], }; } function gitOpsPublishStep(target: SelfMediaDeliveryTarget): Record { return { name: "gitops-publish", image: target.ci.toolImage, imagePullPolicy: "IfNotPresent", workingDir: "/workspace", env: [{ name: "SOURCE_COMMIT", value: "$(params.revision)" }], script: `#!/bin/sh set -eu stage_started_seconds=$(date +%s) askpass=/tmp/selfmedia-git-askpass cat >"$askpass" <<'ASKPASS' #!/bin/sh case "$1" in *Username*) printf '%s' "$SELFMEDIA_GIT_USERNAME" ;; *Password*) cat /var/run/selfmedia-gitops/token ;; *) exit 1 ;; esac ASKPASS chmod 700 "$askpass" export SELFMEDIA_GIT_USERNAME="$(params.gitops-username)" export GIT_ASKPASS="$askpass" export GIT_TERMINAL_PROMPT=0 bun /workspace/source/deploy/scripts/publish-gitops.ts \\ --config /workspace/effective-deployment.yaml \\ --source-root /workspace/source \\ --metadata /workspace/build-metadata.json \\ --source-commit "$SOURCE_COMMIT" \\ --worktree /workspace/selfmedia-gitops rm -f "$askpass" stage_finished_seconds=$(date +%s) printf '{"ok":true,"phase":"gitops-stage","stageTimings":{"gitopsPublishSeconds":%s},"valuesPrinted":false}\n' "$((stage_finished_seconds-stage_started_seconds))" `, securityContext: nonRootSecurity(), volumeMounts: [ { name: "workspace", mountPath: "/workspace" }, { name: "tmp", mountPath: "/tmp" }, { name: "gitops-token", mountPath: "/var/run/selfmedia-gitops", readOnly: true }, ], }; } function assertBinding(target: SelfMediaDeliveryTarget, binding: SelfMediaRendererBinding): void { const expected: Record = { node: target.node, lane: target.lane, namespace: target.ci.namespace, pipeline: target.ci.pipeline, pipelineRunPrefix: target.ci.pipelineRunPrefix, }; const observed: Record = { node: binding.consumer.node, lane: binding.consumer.lane, namespace: binding.consumer.namespace, pipeline: binding.consumer.pipeline, pipelineRunPrefix: binding.consumer.pipelineRunPrefix, }; for (const [key, value] of Object.entries(expected)) { if (observed[key] !== value) throw new Error(`PaC ${binding.consumer.id} ${key}=${observed[key]} does not match selfmedia owner ${value}`); } const requiredParams: Readonly> = { node: target.node, source_branch: target.source.branch, source_snapshot_prefix: target.source.snapshotPrefix, git_read_url: target.source.readUrl, gitops_read_url: target.gitops.readUrl, gitops_write_url: target.gitops.writeUrl, gitops_branch: target.gitops.branch, gitops_username: target.gitops.credentialUsername, gitops_secret_name: target.gitops.credentialSecretName, pipeline_name: target.ci.pipeline, pipeline_run_prefix: target.ci.pipelineRunPrefix, service_account: target.ci.serviceAccountName, image_repository: target.build.imageRepository, pipeline_timeout: target.ci.pipelineTimeout, }; for (const [key, value] of Object.entries(requiredParams)) { if (binding.repository.params[key] !== value) throw new Error(`PaC ${binding.consumer.id} repository params.${key} must match selfmedia owner`); } } function assertServiceRepositoryContract(sourceWorktree: string): void { const required = [ "deploy/Dockerfile", "deploy/scripts/prepare-build.ts", "deploy/scripts/build-image.sh", "deploy/scripts/publish-gitops.ts", "deploy/scripts/render-runtime.ts", ]; for (const file of required) { if (!existsSync(resolve(sourceWorktree, file))) throw new Error(`selfmedia source repository is missing renderer contract file ${file}`); } } function validateRuntimeRender(sourceWorktree: string, effectiveDeployment: Record): void { const temporary = mkdtempSync(resolve(tmpdir(), "unidesk-selfmedia-render-")); const configPath = resolve(temporary, "effective-deployment.yaml"); try { writeFileSync(configPath, `${Bun.YAML.stringify(effectiveDeployment).trim()}\n`, "utf8"); const result = spawnSync("bun", [ "deploy/scripts/render-runtime.ts", "--config", configPath, "--image", `127.0.0.1:5000/selfmedia/newsroom@sha256:${"a".repeat(64)}`, "--source-commit", "b".repeat(40), ], { cwd: sourceWorktree, encoding: "utf8", maxBuffer: 16 * 1024 * 1024, timeout: 60_000, }); if (result.error !== undefined) throw result.error; if (result.status !== 0) { const evidence = `${result.stderr || result.stdout || "render-runtime failed"}`.trim().slice(-2000); throw new Error(`selfmedia effective deployment failed service renderer validation: ${evidence}`); } const output = result.stdout; const publicExposure = requiredRecord(effectiveDeployment.publicExposure, "deployment.publicExposure"); const required = ["kind: Deployment", "kind: Service", "kind: PersistentVolumeClaim", "kind: NetworkPolicy", `hostPort: ${requiredInteger(publicExposure.hostPort, "deployment.publicExposure.hostPort")}`, "automountServiceAccountToken: false"]; for (const marker of required) { if (!output.includes(marker)) throw new Error(`selfmedia service renderer output is missing ${marker}`); } const forbidden = ["kind: Secret", "kind: Role\n", "kind: RoleBinding", "kind: ClusterRole", "kind: ClusterRoleBinding"]; for (const marker of forbidden) { if (output.includes(marker)) throw new Error(`selfmedia service renderer output contains forbidden ${marker.trim()}`); } if ((output.match(/kind: PersistentVolumeClaim/gu) ?? []).length !== 2) throw new Error("selfmedia service renderer must produce exactly two PVCs"); if ((output.match(/kind: NetworkPolicy/gu) ?? []).length < 3) throw new Error("selfmedia service renderer must produce at least three NetworkPolicies"); } finally { rmSync(temporary, { recursive: true, force: true }); } } function nonRootSecurity(): Record { return { runAsUser: 1000, runAsGroup: 1000, runAsNonRoot: true, allowPrivilegeEscalation: false, capabilities: { drop: ["ALL"] }, }; } function requiredString(value: unknown, path: string): string { if (typeof value !== "string" || value.length === 0) throw new Error(`${path} must be a non-empty string`); return value; } function requiredStringArray(value: unknown, path: string): string[] { if (!Array.isArray(value) || value.some((item) => typeof item !== "string" || item.length === 0)) throw new Error(`${path} must be an array of non-empty strings`); return value as string[]; } function requiredRecord(value: unknown, path: string): Record { if (value === null || typeof value !== "object" || Array.isArray(value)) throw new Error(`${path} must be an object`); return value as Record; } function requiredInteger(value: unknown, path: string): number { if (!Number.isInteger(value)) throw new Error(`${path} must be an integer`); return value as number; } function shellSingleQuote(value: string): string { return `'${value.replaceAll("'", `'"'"'`)}'`; }