Files
pikasTech-unidesk/scripts/src/platform-infra-sub2rank-pipeline.ts
T
2026-07-13 08:49:18 +02:00

226 lines
9.8 KiB
TypeScript

import { stableJsonSha256 } from "./stable-json";
import {
readSub2RankConfig,
resolveSub2RankTarget,
sub2RankConfigLabel,
type Sub2RankConfig,
type Sub2RankTarget,
} from "./platform-infra-sub2rank-config";
import {
renderSub2RankManifest,
sub2RankConfigBase64Placeholder,
sub2RankConfigSha256Placeholder,
sub2RankImagePlaceholder,
sub2RankSourceCommitPlaceholder,
} from "./platform-infra-sub2rank";
import type { PacSourceArtifactBinding, PacSourceArtifactMode, PacSourceArtifactRenderer } from "./platform-infra-pipelines-as-code-source-artifact";
export interface Sub2RankPipelineProvenance {
readonly configRef: string;
readonly effectiveConfigSha256: string;
readonly renderer: PacSourceArtifactRenderer;
readonly mode: PacSourceArtifactMode;
}
export function sub2RankOwningSourceRemote(): string {
return readSub2RankConfig().application.remote;
}
export function renderSub2RankDesiredPipeline(binding: PacSourceArtifactBinding): {
pipeline: Record<string, unknown>;
provenance: Sub2RankPipelineProvenance;
} {
const config = readSub2RankConfig();
const target = resolveSub2RankTarget(config, binding.consumer.node);
const expectedConfigRef = `${sub2RankConfigLabel}#delivery`;
if (binding.consumer.sourceArtifact.configRef !== expectedConfigRef) throw new Error(`Sub2Rank sourceArtifact.configRef must equal ${expectedConfigRef}`);
const manifestTemplate = renderSub2RankManifest(config, target, {
imageRef: sub2RankImagePlaceholder,
appConfigBase64: sub2RankConfigBase64Placeholder,
appConfigSha256: sub2RankConfigSha256Placeholder,
sourceCommit: sub2RankSourceCommitPlaceholder,
});
return {
pipeline: renderPipeline(config, target, binding, manifestTemplate),
provenance: {
configRef: expectedConfigRef,
effectiveConfigSha256: stableJsonSha256(pipelineOwner(config, target)),
renderer: "sub2rank-platform-service",
mode: "embedded-pipeline-spec",
},
};
}
function renderPipeline(
config: Sub2RankConfig,
target: Sub2RankTarget,
binding: PacSourceArtifactBinding,
manifestTemplate: string,
): Record<string, unknown> {
const delivery = config.delivery;
const params = [
{ name: "git-read-url", type: "string", default: binding.repository.cloneUrl },
{ name: "source-branch", type: "string", default: config.application.branch },
{ name: "revision", type: "string" },
{ name: "source-stage-ref", type: "string" },
{ name: "config-path", type: "string", default: config.application.sourceConfigPath },
{ name: "dockerfile", type: "string", default: config.application.dockerfile },
{ name: "image-repository", type: "string", default: config.image.repository },
{ name: "tools-image", type: "string", default: delivery.pipeline.toolsImage },
{ name: "buildkit-image", type: "string", default: delivery.pipeline.buildkitImage },
{ name: "build-network", type: "string", default: delivery.build.networkMode },
{ name: "build-http-proxy", type: "string", default: delivery.build.proxy.http },
{ name: "build-https-proxy", type: "string", default: delivery.build.proxy.https },
{ name: "build-all-proxy", type: "string", default: delivery.build.proxy.all },
{ name: "build-no-proxy", type: "string", default: delivery.build.proxy.noProxy.join(",") },
{ name: "gitops-read-url", type: "string", default: delivery.gitops.readUrl },
{ name: "gitops-write-url", type: "string", default: delivery.gitops.writeUrl },
{ name: "gitops-branch", type: "string", default: delivery.gitops.branch },
{ name: "gitops-manifest-path", type: "string", default: delivery.gitops.manifestPath },
{ name: "gitops-release-state-path", type: "string", default: delivery.gitops.releaseStatePath },
{ name: "gitops-max-push-attempts", type: "string", default: String(delivery.gitops.maxPushAttempts) },
{ name: "gitops-author-name", type: "string", default: delivery.gitops.author.name },
{ name: "gitops-author-email", type: "string", default: delivery.gitops.author.email },
{ name: "manifest-template-b64", type: "string", default: Buffer.from(manifestTemplate, "utf8").toString("base64") },
];
const task = (name: string, steps: readonly Record<string, unknown>[], runAfter: readonly string[] = []): Record<string, unknown> => ({
name,
...(runAfter.length === 0 ? {} : { runAfter }),
workspaces: [{ name: "source", workspace: "source" }],
taskSpec: { params: params.map(({ name }) => ({ name })), workspaces: [{ name: "source" }], steps },
params: params.map(({ name: paramName }) => ({ name: paramName, value: `$(params.${paramName})` })),
});
return {
apiVersion: "tekton.dev/v1",
kind: "Pipeline",
metadata: {
name: binding.consumer.pipeline,
namespace: binding.consumer.namespace,
labels: {
"app.kubernetes.io/name": "sub2rank",
"app.kubernetes.io/part-of": "platform-infra",
"app.kubernetes.io/managed-by": "unidesk",
"unidesk.ai/node": target.id,
},
},
spec: {
params,
workspaces: [{ name: "source" }],
tasks: [
task("source-validate", [{
name: "checkout-and-cli-validate",
image: "$(params.tools-image)",
imagePullPolicy: "IfNotPresent",
env: proxyEnv(),
script: sourceValidateScript(),
}]),
task("image-build", [{
name: "build-and-push",
image: "$(params.buildkit-image)",
imagePullPolicy: "IfNotPresent",
env: [
...proxyEnv(),
{ name: "BUILDKITD_FLAGS", value: "--oci-worker-no-process-sandbox --oci-worker-net=host --allow-insecure-entitlement network.host" },
{ name: "SOURCE_ROOT", value: "$(workspaces.source.path)/repo" },
{ name: "SOURCE_COMMIT", value: "$(params.revision)" },
{ name: "IMAGE_REPOSITORY", value: "$(params.image-repository)" },
{ name: "DOCKERFILE", value: "$(params.dockerfile)" },
{ name: "BUILD_NETWORK", value: "$(params.build-network)" },
{ name: "BUILD_METADATA_FILE", value: "$(workspaces.source.path)/build-metadata.json" },
{ name: "BUILD_RESULT_FILE", value: "$(workspaces.source.path)/build-result.json" },
],
securityContext: { privileged: true, runAsUser: 1000, runAsGroup: 1000 },
script: buildScript(),
}], ["source-validate"]),
task("gitops-publish", [{
name: "publish-digest-manifest",
image: "$(params.tools-image)",
imagePullPolicy: "IfNotPresent",
env: proxyEnv(),
script: gitopsPublishScript(),
}], ["image-build"]),
],
},
};
}
function sourceValidateScript(): string {
return [
"#!/bin/sh",
"set -eu",
"root=\"$(workspaces.source.path)\"",
"rm -rf \"$root/repo\"",
"git clone --filter=blob:none --no-checkout \"$(params.git-read-url)\" \"$root/repo\"",
"cd \"$root/repo\"",
"git fetch --depth=1 --filter=blob:none origin \"+$(params.source-stage-ref):refs/remotes/origin/sub2rank-source-snapshot\"",
"git checkout --detach \"$(params.revision)\"",
"test \"$(git rev-parse HEAD)\" = \"$(params.revision)\"",
"bun install --frozen-lockfile",
"bun scripts/sub2rank-cli.ts --config \"$(params.config-path)\" config validate",
"chmod -R a+rX,g+rwX \"$root/repo\"",
"printf '{\"ok\":true,\"phase\":\"source-validate\",\"sourceCommit\":\"%s\",\"configPath\":\"%s\",\"valuesPrinted\":false}\\n' \"$(params.revision)\" \"$(params.config-path)\"",
].join("\n");
}
function buildScript(): string {
return [
"#!/bin/sh",
"set -eu",
"exec \"$(workspaces.source.path)/repo/scripts/ci/build-image.sh\"",
].join("\n");
}
function gitopsPublishScript(): string {
return [
"#!/bin/sh",
"set -eu",
"root=\"$(workspaces.source.path)\"",
"exec bun \"$root/repo/scripts/ci/publish-gitops.mjs\" \\",
" --source-root \"$root/repo\" \\",
" --source-commit \"$(params.revision)\" \\",
" --config-path \"$(params.config-path)\" \\",
" --metadata \"$root/build-metadata.json\" \\",
" --manifest-template-b64 \"$(params.manifest-template-b64)\" \\",
" --image-repository \"$(params.image-repository)\" \\",
" --gitops-read-url \"$(params.gitops-read-url)\" \\",
" --gitops-write-url \"$(params.gitops-write-url)\" \\",
" --gitops-branch \"$(params.gitops-branch)\" \\",
" --manifest-path \"$(params.gitops-manifest-path)\" \\",
" --release-state-path \"$(params.gitops-release-state-path)\" \\",
" --max-push-attempts \"$(params.gitops-max-push-attempts)\" \\",
" --author-name \"$(params.gitops-author-name)\" \\",
" --author-email \"$(params.gitops-author-email)\" \\",
" --worktree \"$root/gitops\"",
].join("\n");
}
function proxyEnv(): Record<string, unknown>[] {
return [
{ name: "HTTP_PROXY", value: "$(params.build-http-proxy)" },
{ name: "http_proxy", value: "$(params.build-http-proxy)" },
{ name: "HTTPS_PROXY", value: "$(params.build-https-proxy)" },
{ name: "https_proxy", value: "$(params.build-https-proxy)" },
{ name: "ALL_PROXY", value: "$(params.build-all-proxy)" },
{ name: "all_proxy", value: "$(params.build-all-proxy)" },
{ name: "NO_PROXY", value: "$(params.build-no-proxy)" },
{ name: "no_proxy", value: "$(params.build-no-proxy)" },
];
}
function pipelineOwner(config: Sub2RankConfig, target: Sub2RankTarget): Record<string, unknown> {
return {
application: {
repository: config.application.repository,
remote: config.application.remote,
branch: config.application.branch,
sourceConfigPath: config.application.sourceConfigPath,
dockerfile: config.application.dockerfile,
runtimeTarget: config.application.runtimeTarget,
},
image: config.image,
delivery: config.delivery,
target,
runtime: config.runtime,
};
}