fix: 将 hwpod 纳入 v02 runtime component
This commit is contained in:
@@ -78,6 +78,7 @@ HWLAB 是 monorepo,G14 CI/CD 加速必须按组件输入判断构建和滚动
|
||||
- `scripts/g14-ci-plan.mjs` 是只读 planner,默认读取当前 workspace 的 `deploy/deploy.json`、`deploy/artifact-catalog.dev.json` 和 `scripts/src/g14-ci-plan-lib.mjs` 内建 component model,输出 `affectedServices`、`reusedServices`、`componentCommitId`、`componentInputHash`、`dockerfileHash`、`baseImageDigest`、`buildArgsHash` 和原因;它不得修改 deploy、catalog 或 GitOps 文件。Tekton `prepare-source` 会先从 `G14-gitops` 注入上一轮发布态 catalog,source 分支里的 catalog 只作为 seed contract。
|
||||
- 服务清单兼容顺序固定为:显式 `--services`、`deploy.services[]`、`deploy.k3s.serviceMappings[]`、`internal/protocol.SERVICE_IDS`。因此旧 `deploy/deploy.json` 形态和当前完整 `deploy.services[]` 形态都必须能被 planner 识别。
|
||||
- 组件边界固定由 `scripts/src/g14-ci-plan-lib.mjs` 的内建 service-path model 定义;如需新增或调整 `componentPaths`、`sharedPaths`、`runtimeDeps` 或 `buildSystemPaths`,直接修改 planner 库和对应测试,不再额外维护 repo-local commands/forbidden skeleton。
|
||||
- `hwpod`/`device-pod-cli` 是 runner 和 runtime 镜像内的稳定工具入口,不是纯本地 `hwlab-cli` 源码工具;`tools/device-pod-cli.mjs`、`tools/device-pod-cli.ts`、`tools/src/device-pod-cli-lib.ts` 和 `skills/device-pod-cli/` 必须作为 shared runtime input 进入 component model。修改这些路径时,G14/v0.2 CI 必须至少触发携带 `/usr/local/bin/hwpod` 的 runtime 服务重新构建或 env-reuse rollout,不能全量复用旧 artifact 后只报告 PipelineRun 成功。
|
||||
- `scripts/g14-artifact-publish.mjs` 默认启用组件级 lazy build:先运行 planner,再只构建/推送 `affectedServices`,`reusedServices` 从 `deploy/artifact-catalog.dev.json` 复用已有 sha256 digest;如果 catalog 没有可验证 digest,planner 直接把该服务列为 affected 并重新发布,不能用旧 guard 阻塞,也不能退回 Docker 或 legacy full-build 路线。
|
||||
- `scripts/g14-artifact-publish.mjs` 的 publish report 必须携带 planner 的 per-service 元数据;`scripts/refresh-artifact-catalog.mjs` 默认只预览,只有 G14 Tekton promotion 显式传 `--write` 时,才把生成的 `commitId`、`image`、`imageTag`、`digest`、`publishState` 和 component provenance 字段写进当前 workspace 的 `deploy/artifact-catalog.dev.json`,随后只提交到 `G14-gitops`。`deploy/deploy.json` 是人写的 runtime config 真相源,不得被 promotion、refresh 脚本或人工发布流程回写镜像身份字段。
|
||||
- `scripts/g14-gitops-render.mjs` 只支持混合 desired state:workload 的 container image、`HWLAB_IMAGE`、`HWLAB_IMAGE_TAG` 和 pod template `source-commit` 来自 `deploy/artifact-catalog.dev.json` 的 per-service artifact identity;普通 env、replica、healthPath、profile 等配置来自 `deploy/deploy.json`。全局 GitOps metadata 仍记录本次 source commit。`--legacy-source-images` 与 `HWLAB_G14_USE_DEPLOY_IMAGES=0` 已废弃,不得把所有 workload image 回退渲染为同一个 source commit tag。
|
||||
|
||||
@@ -254,7 +254,7 @@ test("v02 planner marks cloud-web code-only change as env reuse rollout", async
|
||||
assert.equal(plan.ciCdPlan.noImageBuildReason, "env-reuse-code-only-rollout");
|
||||
});
|
||||
|
||||
test("v02 planner keeps CLI and host skill changes out of unrelated service builds", async () => {
|
||||
test("v02 planner keeps hwlab-cli-only changes out of runtime service builds", async () => {
|
||||
const repo = await createFixtureRepo({ includeDevicePod: true });
|
||||
const initialSha = (await git(repo, ["rev-parse", "HEAD"])).stdout.trim();
|
||||
const initialPlan = await createG14CiPlan({
|
||||
@@ -278,9 +278,7 @@ test("v02 planner keeps CLI and host skill changes out of unrelated service buil
|
||||
await git(repo, ["add", "deploy/artifact-catalog.v02.json"]);
|
||||
await git(repo, ["commit", "-m", "seed v02 catalog"]);
|
||||
|
||||
await writeFile(path.join(repo, "tools/hwlab-cli/client.test.ts"), "console.log('cli test');\n");
|
||||
await writeFile(path.join(repo, "tools/src/hwlab-cli-lib.ts"), "export const harness = true;\n");
|
||||
await writeFile(path.join(repo, "skills/device-pod-cli/assets/device-host-cli.mjs"), "console.log('host cli');\n");
|
||||
await writeFile(path.join(repo, "package.json"), JSON.stringify({
|
||||
type: "module",
|
||||
scripts: {
|
||||
@@ -306,6 +304,50 @@ test("v02 planner keeps CLI and host skill changes out of unrelated service buil
|
||||
assert.equal(plan.services.find((service) => service.serviceId === "hwlab-device-pod").affected, false);
|
||||
});
|
||||
|
||||
test("v02 planner treats hwpod runner CLI changes as runtime inputs", async () => {
|
||||
const repo = await createFixtureRepo({ includeDevicePod: true });
|
||||
const initialSha = (await git(repo, ["rev-parse", "HEAD"])).stdout.trim();
|
||||
const initialPlan = await createG14CiPlan({
|
||||
repoRoot: repo,
|
||||
lane: "v02",
|
||||
baseRef: "HEAD",
|
||||
targetRef: initialSha,
|
||||
artifactCatalogPath: "deploy/artifact-catalog.v02.json",
|
||||
services: ["hwlab-cloud-api", "hwlab-cloud-web", "hwlab-device-pod", "hwlab-agent-skills"]
|
||||
});
|
||||
const initialCloudWeb = initialPlan.services.find((service) => service.serviceId === "hwlab-cloud-web");
|
||||
const initialDevicePod = initialPlan.services.find((service) => service.serviceId === "hwlab-device-pod");
|
||||
await writeFile(path.join(repo, "deploy/artifact-catalog.v02.json"), JSON.stringify(createCatalogFixture("ready", {
|
||||
cloudWebEnvironmentInputHash: initialCloudWeb.environmentInputHash,
|
||||
cloudWebCodeInputHash: initialCloudWeb.codeInputHash,
|
||||
cloudWebBootCommit: initialPlan.sourceCommitId,
|
||||
devicePodEnvironmentInputHash: initialDevicePod.environmentInputHash,
|
||||
devicePodCodeInputHash: initialDevicePod.codeInputHash,
|
||||
devicePodBootCommit: initialPlan.sourceCommitId
|
||||
}), null, 2));
|
||||
await git(repo, ["add", "deploy/artifact-catalog.v02.json"]);
|
||||
await git(repo, ["commit", "-m", "seed v02 catalog"]);
|
||||
|
||||
await writeFile(path.join(repo, "tools/src/device-pod-cli-lib.ts"), "export const hwpod = true;\n");
|
||||
await writeFile(path.join(repo, "skills/device-pod-cli/SKILL.md"), "hwpod skill v2\n");
|
||||
await git(repo, ["add", "."]);
|
||||
await git(repo, ["commit", "-m", "change hwpod runtime cli"]);
|
||||
|
||||
const plan = await createG14CiPlan({
|
||||
repoRoot: repo,
|
||||
lane: "v02",
|
||||
baseRef: "HEAD~1",
|
||||
targetRef: "HEAD",
|
||||
artifactCatalogPath: "deploy/artifact-catalog.v02.json",
|
||||
services: ["hwlab-cloud-api", "hwlab-cloud-web", "hwlab-device-pod", "hwlab-agent-skills"]
|
||||
});
|
||||
assert.deepEqual(plan.affectedServices, ["hwlab-agent-skills", "hwlab-cloud-api", "hwlab-cloud-web", "hwlab-device-pod"]);
|
||||
assert.deepEqual(plan.buildServices, ["hwlab-agent-skills", "hwlab-cloud-api"]);
|
||||
assert.equal(plan.services.find((service) => service.serviceId === "hwlab-cloud-web").codeChanged, true);
|
||||
assert.equal(plan.services.find((service) => service.serviceId === "hwlab-device-pod").codeChanged, true);
|
||||
assert.deepEqual(plan.services.find((service) => service.serviceId === "hwlab-cloud-api").reason, ["shared-runtime-changed"]);
|
||||
});
|
||||
|
||||
async function createFixtureRepo(options = {}) {
|
||||
const deployServices = options.deployServices !== false;
|
||||
const k3sServiceMappings = options.k3sServiceMappings === true;
|
||||
@@ -319,6 +361,7 @@ async function createFixtureRepo(options = {}) {
|
||||
await mkdir(path.join(repo, "tools/hwlab-cli"), { recursive: true });
|
||||
await mkdir(path.join(repo, "tools/src"), { recursive: true });
|
||||
await mkdir(path.join(repo, "skills/device-pod-cli/assets"), { recursive: true });
|
||||
await mkdir(path.join(repo, "skills/device-pod-cli/scripts"), { recursive: true });
|
||||
await mkdir(path.join(repo, "skills/hwlab-agent-runtime"), { recursive: true });
|
||||
await mkdir(path.join(repo, "deploy/runtime/boot"), { recursive: true });
|
||||
await mkdir(path.join(repo, "deploy/runtime/launcher"), { recursive: true });
|
||||
@@ -341,6 +384,12 @@ async function createFixtureRepo(options = {}) {
|
||||
await writeFile(path.join(repo, "internal/dev-entrypoint/cloud-web-routes.mjs"), "export const routes = 1;\n");
|
||||
await writeFile(path.join(repo, "internal/dev-entrypoint/http.mjs"), "export const http = 1;\n");
|
||||
await writeFile(path.join(repo, "internal/device-pod/executor.ts"), "export const version = 1;\n");
|
||||
await writeFile(path.join(repo, "tools/device-pod-cli.mjs"), "console.log('hwpod wrapper');\n");
|
||||
await writeFile(path.join(repo, "tools/device-pod-cli.ts"), "console.log('hwpod ts');\n");
|
||||
await writeFile(path.join(repo, "tools/src/device-pod-cli-lib.ts"), "export const hwpod = false;\n");
|
||||
await writeFile(path.join(repo, "skills/device-pod-cli/SKILL.md"), "hwpod skill\n");
|
||||
await writeFile(path.join(repo, "skills/device-pod-cli/scripts/device-pod-cli.mjs"), "console.log('skill wrapper');\n");
|
||||
await writeFile(path.join(repo, "skills/device-pod-cli/assets/device-host-cli.mjs"), "console.log('host cli');\n");
|
||||
await writeFile(path.join(repo, "deploy/runtime/boot/hwlab-cloud-web.sh"), "#!/bin/sh\nbun run --cwd web/hwlab-cloud-web build\nexec bun .hwlab-cloud-web-runtime.mjs\n");
|
||||
await writeFile(path.join(repo, "deploy/runtime/boot/hwlab-device-pod.sh"), "#!/bin/sh\nexec bun cmd/hwlab-device-pod/main.ts\n");
|
||||
await writeFile(path.join(repo, "deploy/runtime/launcher/hwlab-env-reuse-launcher.ts"), "console.log('launcher');\n");
|
||||
|
||||
@@ -44,7 +44,11 @@ export const DEFAULT_RUNTIME_PACKAGE_FIELDS = Object.freeze([
|
||||
|
||||
export const DEFAULT_SHARED_RUNTIME_PATHS = Object.freeze([
|
||||
"internal/protocol/",
|
||||
"internal/build-metadata.mjs"
|
||||
"internal/build-metadata.mjs",
|
||||
"tools/device-pod-cli.mjs",
|
||||
"tools/device-pod-cli.ts",
|
||||
"tools/src/device-pod-cli-lib.ts",
|
||||
"skills/device-pod-cli/"
|
||||
]);
|
||||
|
||||
export const DEFAULT_DOCS_ONLY_PATHS = Object.freeze([
|
||||
|
||||
Reference in New Issue
Block a user