fix: use manual v02 ci trigger
This commit is contained in:
@@ -130,9 +130,9 @@ DeepSeek proxy manifest 是 GitOps desired state 的一部分,DEV/PROD 分别
|
||||
|
||||
G14 不要求 GitHub webhook 或 GitHub Actions 配置。`hwlab-ci/hwlab-g14-branch-poller` CronJob 每 1 分钟通过 Git SSH 拉取 `G14` HEAD,并用 source commit 的前 12 位生成 PipelineRun 名称 `hwlab-g14-ci-poll-<short12>`。
|
||||
|
||||
`v0.2` 接入时应新增独立 poller/reconciler 或显式参数化同一控制面,监控 `v0.2` HEAD,并使用可区分的 PipelineRun 名称前缀,例如 `hwlab-v02-ci-poll-<short12>`。该 lane 的 GitOps promotion 只允许写入 `v0.2` 专属 catalog、runtime path 和 GitOps branch;不得向 `G14` 或现有 DEV/PROD runtime path 写入生成物。
|
||||
`v0.2` 接入不再新增 HWLAB 发布触发 CronJob;标准入口是 UniDesk CLI `bun scripts/cli.ts hwlab g14 control-plane trigger-current --lane v02 --confirm`,由该入口解析当前 `origin/v0.2` HEAD 并直接创建可区分的 commit-pinned PipelineRun,例如 `hwlab-v02-ci-poll-<short12>`。该 lane 的 GitOps promotion 只允许写入 `v0.2` 专属 catalog、runtime path 和 GitOps branch;不得向 `G14` 或现有 DEV/PROD runtime path 写入生成物。
|
||||
|
||||
如果同名 PipelineRun 已存在,poller 直接跳过;如果不存在,则创建新的 PipelineRun。这样可以用 Kubernetes 原生 CronJob、ServiceAccount、RBAC 和 Tekton API 实现无 GitHub webhook 的分支监控,同时避免同一个 commit 被反复派单。历史上的 `chore: promote G14 GitOps source ...` source 分支生成提交已经废弃;poller 只应该看到人写 source commit。
|
||||
G14 lane 中如果同名 PipelineRun 已存在,poller 直接跳过;如果不存在,则创建新的 PipelineRun。这样可以用 Kubernetes 原生 CronJob、ServiceAccount、RBAC 和 Tekton API 实现无 GitHub webhook 的分支监控,同时避免同一个 commit 被反复派单。历史上的 `chore: promote G14 GitOps source ...` source 分支生成提交已经废弃;G14 poller 只应该看到人写 source commit。
|
||||
|
||||
Pipeline 的标准路径是:`G14` source commit -> Tekton 原语校验 task -> commit-tagged image push 到 G14 registry -> refresh artifact catalog -> render `deploy/gitops/g14/**` -> push catalog 与 GitOps desired state 到 `G14-gitops` -> Argo CD 同步 runtime。CD 只消费已经构建好的镜像和 Git desired state,不在 Argo CD 内构建镜像。
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
## 内部架构
|
||||
|
||||
CI/CD 内部由 branch poller、PipelineRun、component planner、BuildKit publish、GitOps promotion、Argo sync 和公网验收构成。source branch 只保存源码和人写配置;`v0.2-gitops` branch 保存 catalog 和 rendered runtime desired state;live runtime 是最终通过证据。
|
||||
CI/CD 内部由 UniDesk 手动触发入口、PipelineRun、component planner、BuildKit publish、GitOps promotion、Argo sync 和公网验收构成。source branch 只保存源码和人写配置;`v0.2-gitops` branch 保存 catalog 和 rendered runtime desired state;live runtime 是最终通过证据。
|
||||
|
||||
## API 接口说明
|
||||
|
||||
@@ -27,7 +27,7 @@ CI/CD 内部由 branch poller、PipelineRun、component planner、BuildKit publi
|
||||
- `v0.2` 固定作为 G14 上的新增 CI/CD lane,不改写现有 `G14` DEV/PROD lane。
|
||||
- `v0.2` source branch 只保存源码、人写配置、模板、脚本和文档;CI/CD 生成物只进入 `v0.2-gitops`。
|
||||
- `hwlab-v02` 是唯一 runtime namespace;`74.48.78.17:19666/19667` 是唯一公网验收入口。
|
||||
- 共享 G14 k3s、Tekton controller、Argo CD controller、本地 registry、工具镜像和脚本库;隔离分支、catalog、runtime path、Application、Pipeline、CronJob、ServiceAccount、SecretRef、PVC 和 FRP 入口。
|
||||
- 共享 G14 k3s、Tekton controller、Argo CD controller、本地 registry、工具镜像和脚本库;隔离分支、catalog、runtime path、Application、Pipeline、ServiceAccount、SecretRef、PVC 和 FRP 入口。
|
||||
- 旧 DEV/D601/main 门禁不得进入 `v0.2` 发布调用链;新增检查只覆盖固定 branch、namespace、catalog、runtime path、GitOps branch、Argo destination 和公网入口这些硬边界。
|
||||
|
||||
## 固定命名
|
||||
@@ -41,8 +41,7 @@ CI/CD 内部由 branch poller、PipelineRun、component planner、BuildKit publi
|
||||
| Runtime path | `v0.2-gitops:deploy/gitops/g14/runtime-v02` |
|
||||
| Runtime namespace | `hwlab-v02` |
|
||||
| Tekton Pipeline | `hwlab-ci/hwlab-v02-ci-image-publish` |
|
||||
| Branch poller | `hwlab-ci/hwlab-v02-branch-poller` |
|
||||
| Control-plane reconciler | `hwlab-ci/hwlab-v02-control-plane-reconciler` |
|
||||
| Manual trigger | UniDesk CLI `bun scripts/cli.ts hwlab g14 control-plane trigger-current --lane v02 --confirm` |
|
||||
| Tekton ServiceAccount | `hwlab-ci/hwlab-v02-tekton-runner` |
|
||||
| PipelineRun prefix | `hwlab-v02-ci-poll-<short12>` |
|
||||
| Argo CD AppProject | `argocd/hwlab-v02` |
|
||||
@@ -62,7 +61,7 @@ CI/CD 内部由 branch poller、PipelineRun、component planner、BuildKit publi
|
||||
1. live runtime:`hwlab-v02` namespace 中 Deployment/StatefulSet template、Pod ready、事件、日志和 `19666/19667` 公网 health。
|
||||
2. Argo desired state:`argocd/hwlab-g14-v02` 的 revision、sync、health、source branch 和 runtime path。
|
||||
3. GitOps branch:`v0.2-gitops` 中的 `deploy/artifact-catalog.v02.json` 与 `deploy/gitops/g14/runtime-v02/**`。
|
||||
4. Tekton 执行证据:`hwlab-v02-branch-poller`、PipelineRun、TaskRun result、`gitops-promote` 终态。
|
||||
4. Tekton 执行证据:UniDesk `trigger-current` 返回的 PipelineRun、TaskRun result、`gitops-promote` 终态。
|
||||
5. 干净 source workspace:`origin/v0.2`、`deploy/deploy.json`、模板、render 脚本和 `--no-write` 输出。
|
||||
|
||||
旧 commit 记忆、`G14`/`G14-gitops` DEV/PROD 产物、D601 legacy 路径、source branch 中历史 generated 文件和临时 worktree 只能作为线索,不能作为 `v0.2` 发布通过证据。
|
||||
@@ -94,7 +93,7 @@ CI/CD 内部由 branch poller、PipelineRun、component planner、BuildKit publi
|
||||
|
||||
标准链路如下:
|
||||
|
||||
1. `hwlab-v02-branch-poller` 轮询 `origin/v0.2`,按 source commit 创建 `hwlab-v02-ci-poll-<short12>` PipelineRun。
|
||||
1. UniDesk CLI `hwlab g14 control-plane trigger-current --lane v02 --confirm` 解析当前 `origin/v0.2` 完整 source commit SHA,直接创建 commit-pinned `hwlab-v02-ci-poll-<short12>` PipelineRun;默认 `--dry-run` 只返回将要创建的 manifest。
|
||||
2. `prepare-source` checkout `v0.2` source,并从 `v0.2-gitops` 读取上一版 `deploy/artifact-catalog.v02.json`。
|
||||
3. 原语校验 task 只覆盖 repo 报告护栏、GitOps render 合同和必要的代码语法/单元检查;旧 DEV/D601/main gate 不进入 lane。
|
||||
4. planner 根据 component input 判断 affected/reused services。
|
||||
@@ -114,7 +113,7 @@ code boot metadata 固定映射到三个启动环境变量:
|
||||
| 变量 | 自动推导来源 | 约束 |
|
||||
| --- | --- | --- |
|
||||
| `HWLAB_BOOT_REPO` | `v0.2` lane 的 canonical GitHub source repo 配置 | 必须是 canonical GitHub URL;用于身份记录,运行时读取由 resolver 自动分流到 mirror/cache。 |
|
||||
| `HWLAB_BOOT_COMMIT` | `hwlab-v02-branch-poller` 观察到的 `origin/v0.2` 完整 source commit SHA,也就是 PipelineRun revision | 必须是完整 40 位 commit SHA;禁止 branch、tag、`latest` 或人工覆盖。 |
|
||||
| `HWLAB_BOOT_COMMIT` | UniDesk `trigger-current` 解析到的 `origin/v0.2` 完整 source commit SHA,也就是 PipelineRun revision | 必须是完整 40 位 commit SHA;禁止 branch、tag、`latest` 或人工覆盖。 |
|
||||
| `HWLAB_BOOT_SH` | service model 或 `deploy/deploy.json` 中 serviceId 到 boot script 的映射,默认形态为 `deploy/runtime/boot/<serviceId>.sh` | 必须是 repo 内相对路径;禁止绝对路径、`..` 越界和从 env image 中隐式寻找旧脚本。 |
|
||||
|
||||
CI/CD 必须把三变量同时写入 `deploy/artifact-catalog.v02.json`、rendered workload Pod template env/annotation 和 runtime health identity。三变量是由 lane 自动推导的发布事实,不是人工 OPS 参数;如果自动推导缺失或无法证明 `HWLAB_BOOT_COMMIT` 属于 `v0.2` 允许 ancestry,本轮 promotion 必须失败。
|
||||
@@ -169,20 +168,20 @@ registry 与 git mirror/cache 分属不同基础设施边界。registry 保持
|
||||
|
||||
接入 `v0.2` 不得改变以下对象:
|
||||
|
||||
- `G14` source branch 的 poller 语义。
|
||||
- `G14` source branch 的 poller/reconciler 语义。
|
||||
- `G14-gitops` DEV/PROD catalog 与 runtime desired state。
|
||||
- `hwlab-dev` 与 `hwlab-prod` namespace。
|
||||
- `hwlab-g14-dev` 与 `hwlab-g14-prod` Argo Application。
|
||||
- DEV `17666/17667` 与 PROD `18666/18667` FRP 入口。
|
||||
- D601 legacy 回溯路径和旧运行面边界。
|
||||
|
||||
如果 `v0.2` 接入失败,回滚或暂停只能作用于 `hwlab-v02` lane:停止 `hwlab-v02-branch-poller`、暂停或删除 `hwlab-g14-v02`、回滚 `v0.2-gitops` runtime path、关闭 `hwlab-v02-frpc` 或清理 `hwlab-v02` namespace 资源;不得重启、删除或回滚 DEV/PROD 运行面。
|
||||
如果 `v0.2` 接入失败,回滚或暂停只能作用于 `hwlab-v02` lane:停止触发新的 v02 PipelineRun、暂停或删除 `hwlab-g14-v02`、回滚 `v0.2-gitops` runtime path、关闭 `hwlab-v02-frpc` 或清理 `hwlab-v02` namespace 资源;不得重启、删除或回滚 DEV/PROD 运行面。
|
||||
|
||||
## 验收标准
|
||||
|
||||
`v0.2` CI/CD 通过必须同时满足:
|
||||
|
||||
- `hwlab-ci` 中存在 `hwlab-v02-ci-image-publish`、`hwlab-v02-branch-poller`、`hwlab-v02-control-plane-reconciler` 和 `hwlab-v02-tekton-runner`。
|
||||
- `hwlab-ci` 中存在 `hwlab-v02-ci-image-publish` 和 `hwlab-v02-tekton-runner`;`hwlab-v02-branch-poller` 与 `hwlab-v02-control-plane-reconciler` 不再作为 v02 标准对象,若历史残留应由 UniDesk control-plane apply 清理。
|
||||
- 最新 `v0.2` source commit 对应的 PipelineRun 完成,且 promotion 写入 `v0.2-gitops`。
|
||||
- `v0.2-gitops` 中存在 `deploy/artifact-catalog.v02.json` 与 `deploy/gitops/g14/runtime-v02/**`。
|
||||
- `argocd/hwlab-g14-v02` 指向 `v0.2-gitops:deploy/gitops/g14/runtime-v02`,sync revision 与目标 GitOps revision 对齐。
|
||||
@@ -228,7 +227,7 @@ GitOps branch 已更新、source branch render 通过、PipelineRun 名称存在
|
||||
| 规格项 | 状态 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| v02 独立 source/GitOps/runtime lane | 已实现 | `v0.2`、`v0.2-gitops`、`hwlab-v02` 和 `runtime-v02` 已固定。 |
|
||||
| Tekton poller/pipeline/promotion | 已实现 | 通过 `hwlab-v02-*` 对象和 GitOps promotion 管理。 |
|
||||
| 手动 CLI trigger/pipeline/promotion | 已实现 | 通过 UniDesk `trigger-current` 创建 commit-pinned PipelineRun,并由 `hwlab-v02-ci-image-publish` 与 GitOps promotion 管理。 |
|
||||
| v02 runtime readiness fail-closed | 已实现 | `gitops-promote` 推送后触发 Argo hard refresh;`runtime-ready` 读取 workload Pod template source commit,timeout、observer RBAC 不足或未就绪会让 PipelineRun 失败。 |
|
||||
| v02 裁撤服务不进入发布面 | 已实现 | v02 Tekton build service set、artifact catalog 和 runtime render 只包含保留服务;v02 Argo 开启 prune 清理旧 live 对象;裁撤服务仍可保留在 DEV legacy 源码中。 |
|
||||
| Argo v02 Application | 已实现 | `hwlab-g14-v02` 指向 v02 GitOps path 和 namespace。 |
|
||||
|
||||
@@ -1612,8 +1612,10 @@ const eventName = requiredEnv("RECONCILE_EVENT");
|
||||
const manifestFiles = [
|
||||
"deploy/gitops/g14/" + tektonDir + "/rbac.yaml",
|
||||
"deploy/gitops/g14/" + tektonDir + "/pipeline.yaml",
|
||||
"deploy/gitops/g14/" + tektonDir + "/poller.yaml",
|
||||
"deploy/gitops/g14/" + tektonDir + "/control-plane-reconciler.yaml",
|
||||
...(process.env.LANE === "v02" ? [] : [
|
||||
"deploy/gitops/g14/" + tektonDir + "/poller.yaml",
|
||||
"deploy/gitops/g14/" + tektonDir + "/control-plane-reconciler.yaml"
|
||||
]),
|
||||
...argoFiles
|
||||
];
|
||||
const plurals = new Map([
|
||||
@@ -2593,6 +2595,7 @@ function tektonPipelineRunTemplate({ source, args }) {
|
||||
|
||||
function tektonPollerCronJob(args) {
|
||||
const settings = ciLaneSettings(args);
|
||||
if (settings.lane === "v02") throw new Error("v02 CI/CD is manually triggered by UniDesk CLI and must not render a branch poller CronJob");
|
||||
return {
|
||||
apiVersion: "batch/v1",
|
||||
kind: "CronJob",
|
||||
@@ -2666,6 +2669,7 @@ function tektonPollerCronJob(args) {
|
||||
|
||||
function tektonControlPlaneReconcilerCronJob(args) {
|
||||
const settings = ciLaneSettings(args);
|
||||
if (settings.lane === "v02") throw new Error("v02 CI/CD is manually triggered by UniDesk CLI and must not render a control-plane reconciler CronJob");
|
||||
return {
|
||||
apiVersion: "batch/v1",
|
||||
kind: "CronJob",
|
||||
@@ -3660,10 +3664,12 @@ This directory is generated by \`scripts/g14-gitops-render.mjs --lane v02\` from
|
||||
- CI: Tekton Pipeline \`hwlab-ci/hwlab-v02-ci-image-publish\`.
|
||||
- Source reads: v0.2 CI source clone uses devops-infra read-only mirror \`${args.gitReadUrl}\`; canonical repo \`${args.sourceRepo}\` remains the identity and GitOps write path.
|
||||
- Artifact contract: runtime images come from \`${args.catalogPath}\`; source branch \`v0.2\` does not track this generated catalog.
|
||||
- Manual trigger: run \`bun scripts/cli.ts hwlab g14 control-plane trigger-current --lane v02 --confirm\` from UniDesk to create commit-pinned PipelineRun \`hwlab-v02-ci-poll-<short12>\` for the current \`origin/v0.2\` commit.
|
||||
- Branch split: source branch \`${args.sourceBranch}\` remains human-authored source; generated desired state is promoted to \`${args.gitopsBranch}\`.
|
||||
- CD: Argo CD Application \`argocd/hwlab-g14-v02\` consumes \`${args.gitopsBranch}:deploy/gitops/g14/runtime-v02\` and deploys only to namespace \`hwlab-v02\`.
|
||||
- Public preview: FRP exposes v0.2 web \`${args.webEndpoint}\` / edge \`${args.runtimeEndpoint}\` through \`hwlab-v02/hwlab-v02-frpc\`.
|
||||
- G14 DEV/PROD boundary: this lane does not modify \`G14\`, \`G14-gitops\`, \`hwlab-dev\`, \`hwlab-prod\`, 17666/17667 or 18666/18667.
|
||||
- CronJob policy: v0.2 does not generate \`hwlab-v02-branch-poller\` or \`hwlab-v02-control-plane-reconciler\`; obsolete live CronJobs should be deleted by the UniDesk control-plane apply command.
|
||||
- Gate policy: old DEV/D601/main gates do not enter this lane; new checks stay limited to the fixed branch, namespace, catalog, runtime path and Argo boundaries.
|
||||
`;
|
||||
}
|
||||
@@ -3743,8 +3749,10 @@ async function plannedFiles(args) {
|
||||
if (args.lane === "v02") putJson("devops-infra/git-mirror.yaml", devopsInfraGitMirrorManifest());
|
||||
putJson(`${settings.tektonDir}/rbac.yaml`, tektonRbac(args));
|
||||
putJson(`${settings.tektonDir}/pipeline.yaml`, tektonPipeline(args));
|
||||
putJson(`${settings.tektonDir}/poller.yaml`, tektonPollerCronJob(args));
|
||||
putJson(`${settings.tektonDir}/control-plane-reconciler.yaml`, tektonControlPlaneReconcilerCronJob(args));
|
||||
if (args.lane !== "v02") {
|
||||
putJson(`${settings.tektonDir}/poller.yaml`, tektonPollerCronJob(args));
|
||||
putJson(`${settings.tektonDir}/control-plane-reconciler.yaml`, tektonControlPlaneReconcilerCronJob(args));
|
||||
}
|
||||
putJson(`${settings.tektonDir}/pipelinerun.sample.yaml`, tektonPipelineRunTemplate({ source, args }));
|
||||
putJson("argocd/project.yaml", argoProject(args));
|
||||
for (const profile of profiles) putJson(`argocd/application-${profile}.yaml`, argoApplication(args, profile));
|
||||
|
||||
@@ -169,15 +169,13 @@ test("v02 render follows TypeScript runtime checks and does not self-patch boots
|
||||
assert.match(content, /"hwlab\.pikastech\.local\/source-commit"/u);
|
||||
}
|
||||
|
||||
const reconciler = await readFile(path.join(outDir, "tekton-v02", "control-plane-reconciler.yaml"), "utf8");
|
||||
assert.match(reconciler, /cross-namespace-rbac-bootstrap-managed/u);
|
||||
assert.match(reconciler, /HWLAB_RECONCILE_CROSS_NAMESPACE_RBAC/u);
|
||||
assert.match(reconciler, /"name": "GIT_READ_URL"[\s\S]{0,140}git-mirror-http\.devops-infra\.svc\.cluster\.local/u);
|
||||
assert.match(reconciler, /git_timed control-plane-clone 180 git clone --depth 1 --branch \\"\$SOURCE_BRANCH\\" \\"\$GIT_READ_URL\\"/u);
|
||||
await assert.rejects(() => readFile(path.join(outDir, "tekton-v02", "control-plane-reconciler.yaml"), "utf8"), { code: "ENOENT" });
|
||||
await assert.rejects(() => readFile(path.join(outDir, "tekton-v02", "poller.yaml"), "utf8"), { code: "ENOENT" });
|
||||
|
||||
const poller = await readFile(path.join(outDir, "tekton-v02", "poller.yaml"), "utf8");
|
||||
assert.match(poller, /"name": "GIT_READ_URL"[\s\S]{0,140}git-mirror-http\.devops-infra\.svc\.cluster\.local/u);
|
||||
assert.match(poller, /git_timed poller-clone 180 git clone --depth 1 --branch \\"\$SOURCE_BRANCH\\" \\"\$GIT_READ_URL\\"/u);
|
||||
const pipelineRunSample = await readFile(path.join(outDir, "tekton-v02", "pipelinerun.sample.yaml"), "utf8");
|
||||
assert.match(pipelineRunSample, /"name": "git-read-url"[\s\S]{0,140}git-mirror-http\.devops-infra\.svc\.cluster\.local/u);
|
||||
assert.match(pipelineRunSample, /"name": "revision"[\s\S]{0,80}"value": "[0-9a-f]{40}"/u);
|
||||
assert.match(pipelineRunSample, /"name": "source-branch"[\s\S]{0,80}"value": "v0\.2"/u);
|
||||
} finally {
|
||||
await rm(outDir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user