fix: 收敛HWLAB L2发布TaskRun

合并严格串行的计划与发布阶段,并在无构建计划时跳过build TaskRun。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
root
2026-07-22 08:25:06 +02:00
parent 0e12cfae5a
commit 08a0090e40
5 changed files with 184 additions and 412 deletions
+90 -87
View File
@@ -322,7 +322,7 @@ function planArtifactsTask({ serviceIds = defaultServiceIds } = {}) {
results: serviceIds.flatMap((serviceId) => [
{ name: affectedResultName(serviceId), description: `${serviceId} rollout affected according to ci-plan` },
{ name: buildResultName(serviceId), description: `${serviceId} image build required according to ci-plan` }
]),
]).concat([{ name: "build-required", description: "true when at least one planned service image must be built" }]),
workspaces: [{ name: "source" }],
steps: [{
name: "plan",
@@ -402,12 +402,46 @@ function perServiceBuildTask(serviceId, { runAfter = ["plan-artifacts"] } = {})
function perServiceBuildMatrixTask(serviceIds, { runAfter = ["plan-artifacts"] } = {}) {
const task = perServiceBuildTask(serviceIds[0], { runAfter });
task.name = "build-services";
delete task.when;
task.when = [{ input: "$(tasks.plan-artifacts.results.build-required)", operator: "in", values: ["true"] }];
task.params = task.params.filter((param) => param.name !== "service-id");
task.matrix = { params: [{ name: "service-id", value: serviceIds }] };
return task;
}
function mergeNamedEntries(...groups) {
const entries = new Map();
for (const group of groups) {
for (const entry of group || []) {
if (!entries.has(entry.name)) entries.set(entry.name, entry);
}
}
return [...entries.values()];
}
function releasePlanTask({ serviceIds = defaultServiceIds } = {}) {
const plan = planArtifactsTask({ serviceIds });
const prepareParams = [
{ name: "git-url" }, { name: "git-read-url" }, { name: "gitops-read-url" }, { name: "source-branch" },
{ name: "gitops-branch" }, { name: "lane" }, { name: "catalog-path" }, { name: "image-tag-mode" },
{ name: "registry-prefix" }, { name: "services" }, { name: "revision" }
];
const prepareBindings = prepareParams.map(({ name }) => ({ name, value: `$(params.${name})` }));
return {
name: "plan-artifacts",
workspaces: [{ name: "source", workspace: "source" }],
taskSpec: {
params: mergeNamedEntries(prepareParams, plan.taskSpec.params),
results: plan.taskSpec.results,
workspaces: [{ name: "source" }],
steps: [
{ name: "prepare-source", image: ciToolsRunnerImage, env: proxyEnv(), script: prepareSourceScript() },
plan.taskSpec.steps[0]
]
},
params: mergeNamedEntries(prepareBindings, plan.params)
};
}
function collectArtifactsTask({ serviceIds = defaultServiceIds } = {}) {
return {
name: "collect-artifacts",
@@ -438,6 +472,52 @@ function collectArtifactsTask({ serviceIds = defaultServiceIds } = {}) {
};
}
function gitopsPromoteTask() {
return {
name: "gitops-promote",
workspaces: [
{ name: "source", workspace: "source" },
{ name: "gitea-auth", workspace: "gitea-auth" }
],
taskSpec: {
params: [
{ name: "git-url" }, { name: "git-read-url" }, { name: "gitops-read-url" }, { name: "git-write-url" }, { name: "gitops-username" },
{ name: "source-branch" }, { name: "gitops-branch" }, { name: "lane" }, { name: "catalog-path" },
{ name: "image-tag-mode" }, { name: "runtime-path" }, { name: "revision" }, { name: "registry-prefix" }
],
results: [{ name: "runtime-ready-required", description: "true when GitOps promotion changed runtime desired state and runtime readiness must be observed" }],
workspaces: [{ name: "source" }, { name: "gitea-auth" }],
steps: [{ name: "promote", image: ciToolsRunnerImage, env: proxyEnv(), script: gitopsPromoteScript() }]
},
params: [
{ name: "git-url", value: "$(params.git-url)" }, { name: "git-read-url", value: "$(params.git-read-url)" },
{ name: "gitops-read-url", value: "$(params.gitops-read-url)" }, { name: "git-write-url", value: "$(params.git-write-url)" },
{ name: "gitops-username", value: "$(params.gitops-username)" }, { name: "source-branch", value: "$(params.source-branch)" },
{ name: "gitops-branch", value: "$(params.gitops-branch)" }, { name: "lane", value: "$(params.lane)" },
{ name: "catalog-path", value: "$(params.catalog-path)" }, { name: "image-tag-mode", value: "$(params.image-tag-mode)" },
{ name: "runtime-path", value: "$(params.runtime-path)" }, { name: "revision", value: "$(params.revision)" },
{ name: "registry-prefix", value: "$(params.registry-prefix)" }
]
};
}
function releaseArtifactsTask({ serviceIds = defaultServiceIds } = {}) {
const collect = collectArtifactsTask({ serviceIds });
const promote = gitopsPromoteTask();
return {
name: "release-artifacts",
runAfter: ["build-services"],
workspaces: promote.workspaces,
taskSpec: {
params: mergeNamedEntries(collect.taskSpec.params, promote.taskSpec.params),
results: promote.taskSpec.results,
workspaces: promote.taskSpec.workspaces,
steps: [collect.taskSpec.steps[0], promote.taskSpec.steps[0]]
},
params: mergeNamedEntries(collect.params, promote.params)
};
}
function runtimeReadyScript() {
return `#!/bin/sh
set -eu
@@ -549,12 +629,13 @@ function tektonTasks(args = { lane: "node" }, deploy = null) {
function tektonPipeline(args = { lane: "node" }, deploy = null) {
const settings = ciLaneSettings(args, deploy);
const serviceIds = serviceIdsForLane(args.lane, deploy);
const runtimePath = gitopsPathForProfile(args, settings.profile, deploy);
const preFlushRuntimeReadyTasks = isRuntimeLane(settings.lane)
? []
: [{
...runtimeReadyTask({ profile: settings.profile, deploy }),
when: [{ input: "$(tasks.gitops-promote.results.runtime-ready-required)", operator: "in", values: ["true"] }]
when: [{ input: "$(tasks.release-artifacts.results.runtime-ready-required)", operator: "in", values: ["true"] }]
}];
return {
apiVersion: "tekton.dev/v1",
@@ -567,9 +648,9 @@ function tektonPipeline(args = { lane: "node" }, deploy = null) {
"hwlab.pikastech.local/gitops-target": settings.gitopsTarget
},
annotations: {
"hwlab.pikastech.local/source-config": "scripts/gitops-render.mjs#tekton-native-primitive-ci",
"hwlab.pikastech.local/ci-contract": "tekton-native-primitive-tasks",
"hwlab.pikastech.local/policy": "native-per-service-taskrun-image-publish"
"hwlab.pikastech.local/source-config": "scripts/gitops-render.mjs#bounded-release-taskruns",
"hwlab.pikastech.local/ci-contract": "plan-build-release-taskruns",
"hwlab.pikastech.local/policy": "reviewed-plan-exact-scope"
}
},
spec: {
@@ -601,87 +682,9 @@ function tektonPipeline(args = { lane: "node" }, deploy = null) {
{ name: "gitea-auth" }
],
tasks: [
{
name: "prepare-source",
workspaces: [
{ name: "source", workspace: "source" }
],
taskSpec: {
params: [
{ name: "git-url" },
{ name: "git-read-url" },
{ name: "gitops-read-url" },
{ name: "source-branch" },
{ name: "gitops-branch" },
{ name: "lane" },
{ name: "catalog-path" },
{ name: "image-tag-mode" },
{ name: "registry-prefix" },
{ name: "services" },
{ name: "revision" }
],
workspaces: [{ name: "source" }],
steps: [{ name: "prepare-source", image: ciToolsRunnerImage, env: proxyEnv(), script: prepareSourceScript() }]
},
params: [
{ name: "git-url", value: "$(params.git-url)" },
{ name: "git-read-url", value: "$(params.git-read-url)" },
{ name: "gitops-read-url", value: "$(params.gitops-read-url)" },
{ name: "source-branch", value: "$(params.source-branch)" },
{ name: "gitops-branch", value: "$(params.gitops-branch)" },
{ name: "lane", value: "$(params.lane)" },
{ name: "catalog-path", value: "$(params.catalog-path)" },
{ name: "image-tag-mode", value: "$(params.image-tag-mode)" },
{ name: "registry-prefix", value: "$(params.registry-prefix)" },
{ name: "services", value: "$(params.services)" },
{ name: "revision", value: "$(params.revision)" }
]
},
...primitiveValidationTasks.map(primitiveValidationTask),
...imagePublishTaskSet(args, deploy),
{
name: "gitops-promote",
runAfter: ["collect-artifacts"],
workspaces: [
{ name: "source", workspace: "source" },
{ name: "gitea-auth", workspace: "gitea-auth" }
],
taskSpec: {
params: [
{ name: "git-url" },
{ name: "git-read-url" },
{ name: "gitops-read-url" },
{ name: "git-write-url" },
{ name: "gitops-username" },
{ name: "source-branch" },
{ name: "gitops-branch" },
{ name: "lane" },
{ name: "catalog-path" },
{ name: "image-tag-mode" },
{ name: "runtime-path" },
{ name: "revision" },
{ name: "registry-prefix" }
],
results: [{ name: "runtime-ready-required", description: "true when GitOps promotion changed runtime desired state and runtime readiness must be observed" }],
workspaces: [{ name: "source" }, { name: "gitea-auth" }],
steps: [{ name: "promote", image: ciToolsRunnerImage, env: proxyEnv(), script: gitopsPromoteScript() }]
},
params: [
{ name: "git-url", value: "$(params.git-url)" },
{ name: "git-read-url", value: "$(params.git-read-url)" },
{ name: "gitops-read-url", value: "$(params.gitops-read-url)" },
{ name: "git-write-url", value: "$(params.git-write-url)" },
{ name: "gitops-username", value: "$(params.gitops-username)" },
{ name: "source-branch", value: "$(params.source-branch)" },
{ name: "gitops-branch", value: "$(params.gitops-branch)" },
{ name: "lane", value: "$(params.lane)" },
{ name: "catalog-path", value: "$(params.catalog-path)" },
{ name: "image-tag-mode", value: "$(params.image-tag-mode)" },
{ name: "runtime-path", value: "$(params.runtime-path)" },
{ name: "revision", value: "$(params.revision)" },
{ name: "registry-prefix", value: "$(params.registry-prefix)" }
]
},
releasePlanTask({ serviceIds }),
perServiceBuildMatrixTask(serviceIds, { runAfter: ["plan-artifacts"] }),
releaseArtifactsTask({ serviceIds }),
...preFlushRuntimeReadyTasks
]
}
@@ -38,6 +38,7 @@ const entries = allServices.map((serviceId) => {
codeChanged: service.codeChanged ?? null
};
});
fs.writeFileSync("/tekton/results/build-required", entries.some((entry) => entry.buildRequired) ? "true" : "false");
for (const entry of entries) {
fs.writeFileSync("/tekton/results/affected-" + entry.serviceId, entry.affected ? "true" : "false");
fs.writeFileSync("/tekton/results/build-" + entry.serviceId, entry.buildRequired ? "true" : "false");