Merge pull request #2755 from pikasTech/fix/2721-release-plan-catalog-authority
fix: 从 GitOps authority 生成手动发布计划
This commit is contained in:
@@ -71,6 +71,7 @@ bun scripts/cli.ts hwlab nodes control-plane legacy-cicd --help
|
||||
- 当前 GitOps catalog 落后 source 时,plan 必须把累计发布欠账与本次 base 到 source 的请求范围分开显示;
|
||||
- 有效范围大于请求范围时标记 `expanded`,`release trigger` 必须在发送 webhook 前拒绝;
|
||||
- 计划范围扩大时先修复 source range、源码、YAML 或项目 planner,再重新 plan;
|
||||
- artifact catalog 必须从 owning YAML 声明、且与 Argo/promotion 相同的 GitOps read URL 与 branch 恢复;禁止用 source mirror 中的同名 branch 代替 GitOps authority;
|
||||
- 审阅通过后运行 `release trigger --confirm`;该命令只向 PaC 发送 webhook,由 PaC 创建 PipelineRun;
|
||||
- 禁止裸创建 PipelineRun、由 CLI 直建 PipelineRun,或恢复 Gitea 自动 hook;
|
||||
- L3 继续要求对应 L2 已通过和用户对当次生产发布的明确授权。
|
||||
|
||||
@@ -1001,6 +1001,7 @@ templates:
|
||||
params:
|
||||
git_read_url: http://gitea-http.devops-infra.svc.cluster.local:3000/mirrors/pikasTech-HWLAB.git
|
||||
git_write_url: http://git-mirror-write.devops-infra.svc.cluster.local:8080/pikasTech/HWLAB.git
|
||||
gitops_read_url: http://git-mirror-http.devops-infra.svc.cluster.local:8080/pikasTech/HWLAB.git
|
||||
source_branch: v0.3
|
||||
gitops_branch: v0.3-gitops
|
||||
source_snapshot_prefix: refs/unidesk/snapshots/gitea-actions/hwlab-node-runtime/v0.3
|
||||
|
||||
@@ -146,6 +146,13 @@ L2 与 L3 发布必须使用 `plan -> 人工审阅 -> trigger` 的受控手动
|
||||
|
||||
`plan` 必须是只读操作,并针对精确 target、lane 和 source commit 输出:env identity 及 reuse/build 判定、待构建镜像数量与服务列表、待 rollout 服务、全部受影响服务、范围基线和相对基线新增项。`plan` 不得同步 mirror、创建 snapshot、修改 branch、创建 PipelineRun 或写入运行面。
|
||||
|
||||
`plan` 恢复 artifact catalog 时必须满足:
|
||||
|
||||
- 读取 owning YAML 声明的 GitOps read URL 与 branch;
|
||||
- 与 Argo 和 promotion 使用同一 GitOps authority;
|
||||
- Source mirror 只用于读取 source commit,不得代替 GitOps authority;
|
||||
- catalog 无法读取、来源不一致或落后于已部署 revision 时,停止触发并返回重新 plan 的只读引导。
|
||||
|
||||
范围相对 owning YAML 声明的发布意图扩大时,`plan` 必须明确列出新增项。操作者应先修正源码、配置或 planner,使范围回到预期后重新生成计划;不得通过确认参数、白名单、忽略 warning 或扩大资源预算继续触发。
|
||||
|
||||
唯一 mutation 是受控手动 `trigger`。它必须显式选择 target、lane、精确 source commit 和 pipeline intent,并向 PaC 发送 webhook;PaC 从同一 source authority 创建 commit-pinned PipelineRun。触发前必须先审阅当前输入的 plan;计划输入或范围已变化时必须重新 plan。禁止 CLI 直建或裸创建 PipelineRun、由 PR 合并回调 trigger,或为兼容旧自动链保留第二入口。
|
||||
|
||||
@@ -316,6 +316,12 @@ test("L2/L3 release exposes plan first and trigger only sends the PaC webhook",
|
||||
expect(planAction).toContain("expandedServices");
|
||||
expect(planAction).toContain("buildServices");
|
||||
expect(planAction).toContain("rolloutServices");
|
||||
expect(planAction).toContain('gitops_read_url=$(cluster_service_url "$UNIDESK_PAC_MANUAL_GITOPS_READ_URL")');
|
||||
expect(planAction).toContain('HWLAB_GIT_READ_URL="$gitops_read_url"');
|
||||
expect(planAction).not.toContain('HWLAB_GIT_READ_URL="$UNIDESK_PAC_REPOSITORY_URL"');
|
||||
const pac = readPacConfig({ consumerId: "hwlab-nc01-v03", selectDefault: true });
|
||||
expect(pac.repositories.find((repository) => repository.id === "hwlab-nc01-v03")?.params.gitops_read_url)
|
||||
.toBe("http://git-mirror-http.devops-infra.svc.cluster.local:8080/pikasTech/HWLAB.git");
|
||||
expect(triggerAction).toContain("release-plan-scope-expanded");
|
||||
expect(triggerAction).not.toContain("kubectl create");
|
||||
expect(triggerAction).not.toContain("PipelineRun");
|
||||
|
||||
@@ -115,6 +115,22 @@ prepare_gitea_api_base() {
|
||||
export UNIDESK_PAC_GITEA_API_BASE_URL
|
||||
}
|
||||
|
||||
cluster_service_url() {
|
||||
value=$1
|
||||
hostport=$(printf '%s' "$value" | sed -n 's#^https\?://\([^/]*\).*$#\1#p')
|
||||
host=${hostport%%:*}
|
||||
case "$host" in
|
||||
*.svc.cluster.local)
|
||||
service=${host%%.*}
|
||||
rest=${host#*.}
|
||||
namespace=${rest%%.*}
|
||||
cluster_ip=$(kubectl -n "$namespace" get service "$service" -o jsonpath='{.spec.clusterIP}')
|
||||
printf '%s' "$value" | sed "s#$host#$cluster_ip#"
|
||||
;;
|
||||
*) printf '%s' "$value" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
gitea_api() {
|
||||
method="$1"
|
||||
path="$2"
|
||||
@@ -242,11 +258,12 @@ manual_release_plan_json() {
|
||||
return 1
|
||||
fi
|
||||
ln -s "$UNIDESK_PAC_MANUAL_NODE_MODULES" "$tmp/source/node_modules"
|
||||
gitops_read_url=$(cluster_service_url "$UNIDESK_PAC_MANUAL_GITOPS_READ_URL")
|
||||
(
|
||||
cd "$tmp/source"
|
||||
HWLAB_CATALOG_PATH="$UNIDESK_PAC_MANUAL_ARTIFACT_CATALOG_PATH" \
|
||||
HWLAB_GITOPS_BRANCH="$UNIDESK_PAC_MANUAL_GITOPS_BRANCH" \
|
||||
HWLAB_GIT_READ_URL="$UNIDESK_PAC_REPOSITORY_URL" \
|
||||
HWLAB_GIT_READ_URL="$gitops_read_url" \
|
||||
HWLAB_SERVICES="$UNIDESK_PAC_MANUAL_SERVICE_IDS" \
|
||||
node scripts/ci/restore-artifact-catalog.mjs
|
||||
)
|
||||
|
||||
@@ -1630,6 +1630,7 @@ async function manualRelease(config: UniDeskConfig, action: "plan" | "trigger",
|
||||
runtimeService: params.runtime_deployment ?? consumer.id,
|
||||
planningNodeModules,
|
||||
artifactCatalogPath: hwlabLane?.catalogPath ?? "",
|
||||
gitopsReadUrl: params.gitops_read_url ?? params.git_read_url ?? repository.cloneUrl,
|
||||
gitopsBranch: params.gitops_branch ?? hwlabLane?.gitopsBranch ?? "",
|
||||
serviceIds: hwlabLane?.serviceIds ?? [],
|
||||
registryPrefix: hwlabLane?.registryPrefix ?? "",
|
||||
@@ -2423,7 +2424,7 @@ export class PacReleaseManifestRenderError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
function remoteScript(action: "apply" | "status" | "history" | "diagnose-regression" | "debug-step" | "disable-automatic-webhooks" | "manual-release-plan" | "manual-release-trigger", pac: PacConfig, target: PacTarget, repository: PacRepository, consumer: PacConsumer, options: ApplyOptions | HistoryOptions, secrets: SecretMaterial, releaseManifest: string, historyConsumers: PacConsumer[] = [consumer], collectArtifactLogs = false, webhookRepositories: PacRepository[] = [repository], manualRelease?: { sourceCommit: string; baseCommit: string | null; sourceBranch: string; imageRepositories: string[]; runtimeService: string; planningNodeModules: string; artifactCatalogPath: string; gitopsBranch: string; serviceIds: readonly string[]; registryPrefix: string; baseImage: string }): string {
|
||||
function remoteScript(action: "apply" | "status" | "history" | "diagnose-regression" | "debug-step" | "disable-automatic-webhooks" | "manual-release-plan" | "manual-release-trigger", pac: PacConfig, target: PacTarget, repository: PacRepository, consumer: PacConsumer, options: ApplyOptions | HistoryOptions, secrets: SecretMaterial, releaseManifest: string, historyConsumers: PacConsumer[] = [consumer], collectArtifactLogs = false, webhookRepositories: PacRepository[] = [repository], manualRelease?: { sourceCommit: string; baseCommit: string | null; sourceBranch: string; imageRepositories: string[]; runtimeService: string; planningNodeModules: string; artifactCatalogPath: string; gitopsReadUrl: string; gitopsBranch: string; serviceIds: readonly string[]; registryPrefix: string; baseImage: string }): string {
|
||||
const webhookUrl = `${pac.gitea.internalBaseUrl.replace(/\/+$/u, "").replace(/gitea-http\.[^.]+\.svc\.cluster\.local:3000/u, `${pac.release.controllerServiceName}.${pac.release.namespace}.svc.cluster.local:${pac.release.controllerServicePort}`)}`;
|
||||
const admissionIdentity = pacAdmissionDesiredIdentity(target.id);
|
||||
const rbacIdentity = pacConsumerRbacDesiredIdentity(target.id);
|
||||
@@ -2460,6 +2461,7 @@ function remoteScript(action: "apply" | "status" | "history" | "diagnose-regress
|
||||
UNIDESK_PAC_MANUAL_RENDERER: consumer.sourceArtifact?.renderer ?? "generic",
|
||||
UNIDESK_PAC_MANUAL_NODE_MODULES: manualRelease?.planningNodeModules ?? "",
|
||||
UNIDESK_PAC_MANUAL_ARTIFACT_CATALOG_PATH: manualRelease?.artifactCatalogPath ?? "",
|
||||
UNIDESK_PAC_MANUAL_GITOPS_READ_URL: manualRelease?.gitopsReadUrl ?? "",
|
||||
UNIDESK_PAC_MANUAL_GITOPS_BRANCH: manualRelease?.gitopsBranch ?? "",
|
||||
UNIDESK_PAC_MANUAL_SERVICE_IDS: (manualRelease?.serviceIds ?? []).join(","),
|
||||
UNIDESK_PAC_MANUAL_REGISTRY_PREFIX: manualRelease?.registryPrefix ?? "",
|
||||
|
||||
Reference in New Issue
Block a user