fix: reuse agent worker env image in v02 ci
This commit is contained in:
@@ -136,10 +136,12 @@
|
||||
"imageTagMode": "full",
|
||||
"envReuseServices": [
|
||||
"hwlab-cloud-web",
|
||||
"hwlab-agent-worker",
|
||||
"hwlab-device-pod"
|
||||
],
|
||||
"bootScripts": {
|
||||
"hwlab-cloud-web": "deploy/runtime/boot/hwlab-cloud-web.sh",
|
||||
"hwlab-agent-worker": "deploy/runtime/boot/hwlab-agent-worker.sh",
|
||||
"hwlab-device-pod": "deploy/runtime/boot/hwlab-device-pod.sh"
|
||||
},
|
||||
"services": [
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
export HWLAB_SERVICE_ID="hwlab-agent-worker"
|
||||
export HWLAB_RUNTIME_MODE="${HWLAB_RUNTIME_MODE:-env-reuse-git-mirror-checkout}"
|
||||
export HWLAB_COMMIT_ID="${HWLAB_BOOT_COMMIT:?HWLAB_BOOT_COMMIT is required}"
|
||||
export HWLAB_REVISION="${HWLAB_BOOT_COMMIT}"
|
||||
export HWLAB_IMAGE="${HWLAB_ENVIRONMENT_IMAGE:-${HWLAB_IMAGE:-}}"
|
||||
export HWLAB_IMAGE_DIGEST="${HWLAB_ENVIRONMENT_DIGEST:-${HWLAB_IMAGE_DIGEST:-unknown}}"
|
||||
|
||||
bun_bin="${HWLAB_BUN_COMMAND:-}"
|
||||
if [ -z "$bun_bin" ]; then
|
||||
if command -v bun >/dev/null 2>&1; then
|
||||
bun_bin="$(command -v bun)"
|
||||
elif [ -x /usr/local/bin/bun ]; then
|
||||
bun_bin="/usr/local/bin/bun"
|
||||
elif [ -x ./node_modules/.bin/bun ]; then
|
||||
bun_bin="./node_modules/.bin/bun"
|
||||
else
|
||||
echo "hwlab-agent-worker boot failed: bun not found" >&2
|
||||
exit 127
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$#" -eq 0 ]; then
|
||||
set -- health
|
||||
fi
|
||||
|
||||
exec "$bun_bin" cmd/hwlab-agent-worker/main.ts "$@"
|
||||
@@ -115,7 +115,7 @@ devops-infra git mirror 仍是 PipelineRun 和 Argo CD 的集群内读写源。`
|
||||
5. affected service 通过 BuildKit 发布到 G14 本地 registry;reused service 复用 catalog digest。
|
||||
所有 selected service 的 build TaskRun 都只依赖 `plan-artifacts`,不按 service 串行排队,也不设置 8 并发或其他 Pipeline 级限流。
|
||||
实际并发由 Tekton controller、G14 节点资源、PVC I/O、BuildKit sidecar 和本地 registry 承载能力决定。
|
||||
`hwlab-cloud-web` 和 `hwlab-device-pod` 必须使用 `env-reuse-git-mirror-checkout`。
|
||||
`hwlab-cloud-web`、`hwlab-agent-worker` 和 `hwlab-device-pod` 必须使用 `env-reuse-git-mirror-checkout`。
|
||||
只有 package/runtime/env 输入变化时才构建 `<service>-env` 镜像。
|
||||
纯前端源码、Device Pod 源码或 boot code 变化只更新三变量和 GitOps desired state,
|
||||
不再重新构建业务镜像。
|
||||
@@ -200,7 +200,7 @@ Git 读写都走 `devops-infra` 本地 mirror/relay。读路径用 HTTP mirror c
|
||||
|
||||
任何仍需要访问 GitHub canonical remote 的 SSH 操作都必须显式走 G14 proxy。Host SSH config 可以使用 OpenBSD `nc -X connect -x 127.0.0.1:10808 %h %p`;Tekton `GIT_SSH_COMMAND`、git-mirror `sync`/`flush` 和其他容器内 `git@github.com` 或 `ssh://git@ssh.github.com:443` 路径必须使用 repo-owned Node HTTP CONNECT ProxyCommand,并保留短 `ConnectTimeout` 与 `ServerAlive*`。只改 `HTTP_PROXY`/`HTTPS_PROXY` 对 OpenSSH 无效;BusyBox `nc` 不支持 `-X connect`,不得再把 `ssh.github.com:443` 当作等价 proxy 或让 GitHub SSH 直连反复 60s 超时。
|
||||
|
||||
env image 复用把系统依赖和业务代码身份分离。`environmentDigest` 表示可复用运行环境,`HWLAB_BOOT_REPO`、`HWLAB_BOOT_COMMIT` 和 `HWLAB_BOOT_SH` 表示本次代码启动身份。code-only 变更只更新 boot metadata 和 runtime identity,不发布新 env image;只有真实 env 输入变化或缺少可复用 env digest 时才进入 env rebuild。`package.json` 只按 dependency/runtime 字段参与 env/runtime hash,`scripts` 只属于开发入口,不得因为清理旧门禁脚本而重建所有 service 或重建 env image。
|
||||
env image 复用把系统依赖和业务代码身份分离。`environmentDigest` 表示可复用运行环境,`HWLAB_BOOT_REPO`、`HWLAB_BOOT_COMMIT` 和 `HWLAB_BOOT_SH` 表示本次代码启动身份。code-only 变更只更新 boot metadata 和 runtime identity,不发布新 env image;只有真实 env 输入变化或缺少可复用 env digest 时才进入 env rebuild。`hwlab-agent-worker` 是 suspended Job template,也必须复用 env image:worker 代码变化只能更新 boot commit 和 Job template identity,不能重新构建 `hwlab-agent-worker` service image。`package.json` 只按 dependency/runtime 字段参与 env/runtime hash,`scripts` 只属于开发入口,不得因为清理旧门禁脚本而重建所有 service 或重建 env image。
|
||||
|
||||
BuildKit publish 采用 service 级全并行 fan-out。
|
||||
每个 service build task 拥有独立 BuildKit sidecar、独立 service workdir 和独立 report 文件,均从同一 `plan-artifacts` 结果判断是否需要执行。
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
- `cmd/hwlab-agent-worker/main.ts` 提供 CLI 命令 `health` 和 `dry-run/smoke`。
|
||||
- `internal/agent/runtime.mjs` 提供 `runLocalWorkerDryRun`,读取 manager 创建的本地 session 状态并写入 evidence。
|
||||
- Job template 通过 env 注入 `HWLAB_AGENT_SESSION_ID`、`HWLAB_SKILL_COMMIT_ID` 和 `HWLAB_SKILL_VERSION`。
|
||||
- v0.2 CI/CD 中 worker 使用 `env-reuse-git-mirror-checkout`:环境镜像只承载 runtime、依赖、launcher 和 git client,实际 worker 代码由 `HWLAB_BOOT_REPO`、`HWLAB_BOOT_COMMIT`、`HWLAB_BOOT_SH=deploy/runtime/boot/hwlab-agent-worker.sh` 从 `devops-infra` git mirror checkout 后启动。worker code-only 变更不得触发 `hwlab-agent-worker` service image BuildKit 重建,只允许更新 boot commit 和 Job template identity。
|
||||
|
||||
## API 接口说明
|
||||
|
||||
@@ -38,6 +39,7 @@
|
||||
| --- | --- | --- |
|
||||
| CLI health/dry-run | 已实现 | worker 以 JSON 输出。 |
|
||||
| suspended Job template | 已实现 | runtime-v02 中存在 `hwlab-agent-worker-template`。 |
|
||||
| v0.2 env-reuse 启动 | 已实现 | `deploy/runtime/boot/hwlab-agent-worker.sh` 通过 git mirror checkout 后执行 worker CLI;code-only 变更复用 env image。 |
|
||||
| session-scoped evidence 写入 | 已实现 | 本地 dry-run 覆盖 trace/evidence。 |
|
||||
| 长驻 HTTP API | 不适用 | worker 不是长驻服务。 |
|
||||
| 生产调度与真实执行 | 未完全实现 | 当前仍是 skeleton/dry-run 模式。 |
|
||||
|
||||
@@ -354,6 +354,98 @@ 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 marks agent-worker code-only change as env reuse rollout", async () => {
|
||||
const repo = await createFixtureRepo({ includeDevicePod: true, includeAgentWorker: 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-agent-worker"]
|
||||
});
|
||||
const initialWorker = initialPlan.services.find((service) => service.serviceId === "hwlab-agent-worker");
|
||||
await writeFile(path.join(repo, "deploy/artifact-catalog.v02.json"), JSON.stringify(createCatalogFixture("ready", {
|
||||
agentWorkerEnvironmentInputHash: initialWorker.environmentInputHash,
|
||||
agentWorkerCodeInputHash: initialWorker.codeInputHash,
|
||||
agentWorkerBootCommit: initialPlan.sourceCommitId
|
||||
}), null, 2));
|
||||
await git(repo, ["add", "deploy/artifact-catalog.v02.json"]);
|
||||
await git(repo, ["commit", "-m", "seed v02 worker catalog"]);
|
||||
|
||||
await writeFile(path.join(repo, "cmd/hwlab-agent-worker/main.ts"), "console.log('worker v2');\n");
|
||||
await git(repo, ["add", "."]);
|
||||
await git(repo, ["commit", "-m", "change agent worker code"]);
|
||||
|
||||
const plan = await createG14CiPlan({
|
||||
repoRoot: repo,
|
||||
lane: "v02",
|
||||
baseRef: "HEAD~1",
|
||||
targetRef: "HEAD",
|
||||
artifactCatalogPath: "deploy/artifact-catalog.v02.json",
|
||||
services: ["hwlab-agent-worker"]
|
||||
});
|
||||
const worker = plan.services.find((service) => service.serviceId === "hwlab-agent-worker");
|
||||
assert.equal(worker.runtimeMode, "env-reuse-git-mirror-checkout");
|
||||
assert.equal(worker.envChanged, false);
|
||||
assert.equal(worker.codeChanged, true);
|
||||
assert.equal(worker.buildRequired, false);
|
||||
assert.equal(worker.bootSh, "deploy/runtime/boot/hwlab-agent-worker.sh");
|
||||
assert.deepEqual(plan.affectedServices, ["hwlab-agent-worker"]);
|
||||
assert.deepEqual(plan.rolloutServices, ["hwlab-agent-worker"]);
|
||||
assert.deepEqual(plan.buildServices, []);
|
||||
assert.equal(plan.imageBuildRequired, false);
|
||||
assert.equal(plan.ciCdPlan.noImageBuildReason, "env-reuse-code-only-rollout");
|
||||
});
|
||||
|
||||
test("v02 planner rebuilds agent-worker env image only for runtime dependency changes", async () => {
|
||||
const repo = await createFixtureRepo({ includeDevicePod: true, includeAgentWorker: 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-agent-worker"]
|
||||
});
|
||||
const initialWorker = initialPlan.services.find((service) => service.serviceId === "hwlab-agent-worker");
|
||||
await writeFile(path.join(repo, "deploy/artifact-catalog.v02.json"), JSON.stringify(createCatalogFixture("ready", {
|
||||
agentWorkerEnvironmentInputHash: initialWorker.environmentInputHash,
|
||||
agentWorkerCodeInputHash: initialWorker.codeInputHash,
|
||||
agentWorkerBootCommit: initialPlan.sourceCommitId
|
||||
}), null, 2));
|
||||
await git(repo, ["add", "deploy/artifact-catalog.v02.json"]);
|
||||
await git(repo, ["commit", "-m", "seed v02 worker catalog"]);
|
||||
|
||||
await writeFile(path.join(repo, "package.json"), JSON.stringify({
|
||||
type: "module",
|
||||
dependencies: {
|
||||
"@openai/codex": "^0.129.0"
|
||||
}
|
||||
}, null, 2));
|
||||
await git(repo, ["add", "package.json"]);
|
||||
await git(repo, ["commit", "-m", "change worker env dependency"]);
|
||||
|
||||
const plan = await createG14CiPlan({
|
||||
repoRoot: repo,
|
||||
lane: "v02",
|
||||
baseRef: "HEAD~1",
|
||||
targetRef: "HEAD",
|
||||
artifactCatalogPath: "deploy/artifact-catalog.v02.json",
|
||||
services: ["hwlab-agent-worker"]
|
||||
});
|
||||
const worker = plan.services.find((service) => service.serviceId === "hwlab-agent-worker");
|
||||
assert.equal(worker.runtimeMode, "env-reuse-git-mirror-checkout");
|
||||
assert.equal(worker.envChanged, true);
|
||||
assert.equal(worker.buildRequired, true);
|
||||
assert.deepEqual(plan.affectedServices, ["hwlab-agent-worker"]);
|
||||
assert.deepEqual(plan.buildServices, ["hwlab-agent-worker"]);
|
||||
assert.ok(worker.reason.includes("environment-input-changed"));
|
||||
assert.ok(worker.reason.includes("runtime-deps-changed"));
|
||||
});
|
||||
|
||||
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();
|
||||
@@ -458,9 +550,11 @@ async function createFixtureRepo(options = {}) {
|
||||
const deployServices = options.deployServices !== false;
|
||||
const k3sServiceMappings = options.k3sServiceMappings === true;
|
||||
const includeDevicePod = options.includeDevicePod === true;
|
||||
const includeAgentWorker = options.includeAgentWorker === true;
|
||||
const repo = await mkdtemp(path.join(os.tmpdir(), "hwlab-g14-ci-plan-"));
|
||||
await mkdir(path.join(repo, "cmd/hwlab-cloud-api"), { recursive: true });
|
||||
await mkdir(path.join(repo, "cmd/hwlab-deepseek-responses-bridge"), { recursive: true });
|
||||
await mkdir(path.join(repo, "cmd/hwlab-agent-worker"), { recursive: true });
|
||||
await mkdir(path.join(repo, "web/hwlab-cloud-web"), { recursive: true });
|
||||
await mkdir(path.join(repo, "internal/dev-entrypoint"), { recursive: true });
|
||||
await mkdir(path.join(repo, "internal/device-pod"), { recursive: true });
|
||||
@@ -479,6 +573,7 @@ async function createFixtureRepo(options = {}) {
|
||||
await writeFile(path.join(repo, "cmd/hwlab-cloud-api/main.ts"), "console.log('api');\n");
|
||||
await writeFile(path.join(repo, "cmd/hwlab-deepseek-responses-bridge/main.ts"), "console.log('bridge');\n");
|
||||
await writeFile(path.join(repo, "cmd/hwlab-deepseek-responses-bridge/main.test.ts"), "console.log('test');\n");
|
||||
await writeFile(path.join(repo, "cmd/hwlab-agent-worker/main.ts"), "console.log('worker');\n");
|
||||
await writeFile(path.join(repo, "web/hwlab-cloud-web/index.html"), "<html></html>\n");
|
||||
await writeFile(path.join(repo, "internal/dev-entrypoint/artifact-runtime.mjs"), "export const artifactRuntime = 1;\n");
|
||||
await writeFile(path.join(repo, "internal/dev-entrypoint/cloud-web-runtime.mjs"), "export const runtime = 1;\n");
|
||||
@@ -493,6 +588,7 @@ async function createFixtureRepo(options = {}) {
|
||||
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-agent-worker.sh"), "#!/bin/sh\nexec bun cmd/hwlab-agent-worker/main.ts \"$@\"\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");
|
||||
await writeFile(path.join(repo, "skills/hwlab-agent-runtime/SKILL.md"), "agent runtime skill\n");
|
||||
@@ -506,7 +602,7 @@ async function createFixtureRepo(options = {}) {
|
||||
baseRef: "HEAD",
|
||||
targetRef: "HEAD",
|
||||
artifactCatalogPath: "deploy/nonexistent-artifact-catalog.json",
|
||||
services: ["hwlab-cloud-api", "hwlab-cloud-web", ...(includeDevicePod ? ["hwlab-device-pod"] : [])]
|
||||
services: ["hwlab-cloud-api", "hwlab-cloud-web", ...(includeAgentWorker ? ["hwlab-agent-worker"] : []), ...(includeDevicePod ? ["hwlab-device-pod"] : [])]
|
||||
});
|
||||
const componentInputHashes = Object.fromEntries(initialPlan.services.map((service) => [service.serviceId, service.componentInputHash]));
|
||||
const catalog = createCatalogFixture(catalogMode, { sourceCommitId: initialSha, componentInputHashes });
|
||||
@@ -541,9 +637,10 @@ function createDeployFixture({ deployServices, k3sServiceMappings, includeDevice
|
||||
lanes: {
|
||||
v02: {
|
||||
sourceRepo: "git@github.com:pikasTech/HWLAB.git",
|
||||
envReuseServices: ["hwlab-cloud-web", "hwlab-device-pod"],
|
||||
envReuseServices: ["hwlab-cloud-web", "hwlab-agent-worker", "hwlab-device-pod"],
|
||||
bootScripts: {
|
||||
"hwlab-cloud-web": "deploy/runtime/boot/hwlab-cloud-web.sh",
|
||||
"hwlab-agent-worker": "deploy/runtime/boot/hwlab-agent-worker.sh",
|
||||
"hwlab-device-pod": "deploy/runtime/boot/hwlab-device-pod.sh"
|
||||
}
|
||||
}
|
||||
@@ -571,7 +668,7 @@ function createDeployFixture({ deployServices, k3sServiceMappings, includeDevice
|
||||
function createCatalogFixture(mode, options = {}) {
|
||||
const digest = mode === "ready" ? `sha256:${"1".repeat(64)}` : "not_published";
|
||||
return {
|
||||
services: ["hwlab-cloud-api", "hwlab-cloud-web", "hwlab-device-pod"].map((serviceId) => ({
|
||||
services: ["hwlab-cloud-api", "hwlab-cloud-web", "hwlab-agent-worker", "hwlab-device-pod"].map((serviceId) => ({
|
||||
serviceId,
|
||||
commitId: "abc1234",
|
||||
sourceCommitId: options.sourceCommitId ?? "abc1234",
|
||||
@@ -581,20 +678,26 @@ function createCatalogFixture(mode, options = {}) {
|
||||
...((options.componentInputHashes?.[serviceId] || (serviceId === "hwlab-cloud-api" && options.cloudApiComponentInputHash)) ? {
|
||||
componentInputHash: options.cloudApiComponentInputHash ?? options.componentInputHashes?.[serviceId]
|
||||
} : {}),
|
||||
...((serviceId === "hwlab-cloud-web" || serviceId === "hwlab-device-pod") ? {
|
||||
...((serviceId === "hwlab-cloud-web" || serviceId === "hwlab-agent-worker" || serviceId === "hwlab-device-pod") ? {
|
||||
runtimeMode: "env-reuse-git-mirror-checkout",
|
||||
envReuse: true,
|
||||
environmentImage: `127.0.0.1:5000/hwlab/${serviceId}-env:env-abc1234`,
|
||||
environmentDigest: digest,
|
||||
environmentInputHash: serviceId === "hwlab-cloud-web"
|
||||
? options.cloudWebEnvironmentInputHash ?? "e".repeat(64)
|
||||
: serviceId === "hwlab-agent-worker"
|
||||
? options.agentWorkerEnvironmentInputHash ?? "e".repeat(64)
|
||||
: options.devicePodEnvironmentInputHash ?? "e".repeat(64),
|
||||
codeInputHash: serviceId === "hwlab-cloud-web"
|
||||
? options.cloudWebCodeInputHash ?? "c".repeat(64)
|
||||
: serviceId === "hwlab-agent-worker"
|
||||
? options.agentWorkerCodeInputHash ?? "c".repeat(64)
|
||||
: options.devicePodCodeInputHash ?? "c".repeat(64),
|
||||
bootRepo: "git@github.com:pikasTech/HWLAB.git",
|
||||
bootCommit: serviceId === "hwlab-cloud-web"
|
||||
? options.cloudWebBootCommit ?? "abc1234"
|
||||
: serviceId === "hwlab-agent-worker"
|
||||
? options.agentWorkerBootCommit ?? "abc1234"
|
||||
: options.devicePodBootCommit ?? "abc1234",
|
||||
bootSh: `deploy/runtime/boot/${serviceId}.sh`
|
||||
} : {})
|
||||
|
||||
@@ -416,7 +416,8 @@ function catalogServiceById(catalog, serviceId) {
|
||||
return (catalog?.services ?? []).find((service) => service?.serviceId === serviceId) ?? null;
|
||||
}
|
||||
|
||||
function v02EnvReuseEnabled(catalog, serviceId) {
|
||||
function v02EnvReuseEnabled(catalog, serviceId, envReuseServiceIds = null) {
|
||||
if (envReuseServiceIds?.has(serviceId)) return true;
|
||||
const service = catalogServiceById(catalog, serviceId);
|
||||
return service?.runtimeMode === v02EnvReuseRuntimeMode || service?.envReuse === true;
|
||||
}
|
||||
@@ -458,17 +459,17 @@ function bootMetadataForService({ args, catalog, deployService, serviceId, sourc
|
||||
};
|
||||
}
|
||||
|
||||
function runtimeArtifactForService({ catalog, serviceId, source, registryPrefix, useDeployImages = false, digestPin = false }) {
|
||||
function runtimeArtifactForService({ catalog, deploy, serviceId, source, registryPrefix, useDeployImages = false, digestPin = false, envReuseServiceIds = null }) {
|
||||
const catalogService = catalogServiceById(catalog, serviceId);
|
||||
if (catalogService?.runtimeMode === v02EnvReuseRuntimeMode || catalogService?.envReuse === true) {
|
||||
const environmentImage = catalogService.environmentImage ?? imageFor(registryPrefix, `${serviceId}-env`, `env-${String(catalogService.environmentInputHash ?? source.full).slice(0, 12)}`);
|
||||
const runtimeImage = digestPin && useDeployImages ? digestPinnedImage(environmentImage, catalogService.environmentDigest ?? catalogService.digest) : environmentImage;
|
||||
if (v02EnvReuseEnabled(catalog, serviceId, envReuseServiceIds)) {
|
||||
const environmentImage = catalogService?.environmentImage ?? imageFor(registryPrefix, `${serviceId}-env`, `env-${String(catalogService?.environmentInputHash ?? source.full).slice(0, 12)}`);
|
||||
const runtimeImage = digestPin && useDeployImages ? digestPinnedImage(environmentImage, catalogService?.environmentDigest ?? catalogService?.digest) : environmentImage;
|
||||
return {
|
||||
image: runtimeImage,
|
||||
imageTag: imageTagFromReference(environmentImage) ?? source.imageTag ?? source.short,
|
||||
commit: catalogService.bootCommit ?? source.full,
|
||||
commit: catalogService?.bootCommit ?? source.full,
|
||||
runtimeMode: v02EnvReuseRuntimeMode,
|
||||
environmentDigest: catalogService.environmentDigest ?? catalogService.digest ?? null
|
||||
environmentDigest: catalogService?.environmentDigest ?? catalogService?.digest ?? null
|
||||
};
|
||||
}
|
||||
const fallbackImageTag = source.imageTag ?? source.short;
|
||||
@@ -753,6 +754,7 @@ function transformWorkloads({ workloads, deploy, catalog, source, sourceBranch =
|
||||
const profileLabel = runtimeLabelForProfile(profile);
|
||||
const gitopsTarget = gitopsTargetForProfile(profile);
|
||||
const digestPin = profile === "v02";
|
||||
const envReuseServiceIds = envReuseServiceIdsForLane(deploy, profile);
|
||||
const stableRuntimeLabels = {
|
||||
"app.kubernetes.io/part-of": "hwlab",
|
||||
"hwlab.pikastech.local/environment": profileLabel,
|
||||
@@ -784,7 +786,7 @@ function transformWorkloads({ workloads, deploy, catalog, source, sourceBranch =
|
||||
const podTemplate = item?.spec?.template;
|
||||
if (!podTemplate?.spec?.containers) continue;
|
||||
const templateServiceId = serviceIdForWorkload(item, podTemplate.spec.containers[0]);
|
||||
const templateArtifact = runtimeArtifactForService({ catalog, serviceId: templateServiceId, source, registryPrefix, useDeployImages, digestPin });
|
||||
const templateArtifact = runtimeArtifactForService({ catalog, deploy, serviceId: templateServiceId, source, registryPrefix, useDeployImages, digestPin, envReuseServiceIds });
|
||||
const templateSourceCommit = profile === "v02" ? (templateArtifact.commit ?? source.full) : source.full;
|
||||
const templateArtifactCommit = templateArtifact.commit;
|
||||
label(podTemplate.metadata ??= {}, {
|
||||
@@ -808,8 +810,8 @@ function transformWorkloads({ workloads, deploy, catalog, source, sourceBranch =
|
||||
const serviceId = serviceIdForWorkload(item, container);
|
||||
const deployService = deployServices.get(serviceId);
|
||||
if (!deployService) continue;
|
||||
const artifact = runtimeArtifactForService({ catalog, serviceId, source, registryPrefix, useDeployImages, digestPin });
|
||||
const envReuse = profile === "v02" && v02EnvReuseEnabled(catalog, serviceId);
|
||||
const artifact = runtimeArtifactForService({ catalog, deploy, serviceId, source, registryPrefix, useDeployImages, digestPin, envReuseServiceIds });
|
||||
const envReuse = profile === "v02" && v02EnvReuseEnabled(catalog, serviceId, envReuseServiceIds);
|
||||
const bootMetadata = envReuse ? bootMetadataForService({ args: { sourceRepo: deploy?.lanes?.v02?.sourceRepo || defaultSourceRepo, registryPrefix }, catalog, deployService, serviceId, source }) : null;
|
||||
const image = artifact.image;
|
||||
const runtimeCommit = artifact.commit;
|
||||
|
||||
@@ -229,9 +229,15 @@ test("v02 render follows TypeScript runtime checks and does not self-patch boots
|
||||
assert.ok(cloudApi, "expected v02 hwlab-cloud-api workload");
|
||||
const cloudApiEnv = cloudApi.spec.template.spec.containers[0].env;
|
||||
assert.ok(cloudApiEnv.some((entry) => entry.name === "HWLAB_CODE_AGENT_AGENTRUN_SOURCE_COMMIT" && entry.value === sourceRevision));
|
||||
const agentWorker = workloadsJson.items.find((item) => item.metadata?.name === "hwlab-agent-worker");
|
||||
const agentWorker = workloadsJson.items.find((item) => item.metadata?.labels?.["hwlab.pikastech.local/service-id"] === "hwlab-agent-worker");
|
||||
assert.ok(agentWorker, "expected v02 hwlab-agent-worker suspended Job template");
|
||||
const agentWorkerEnv = agentWorker?.spec?.template?.spec?.containers?.[0]?.env ?? [];
|
||||
assert.equal(agentWorkerEnv.some((entry) => entry.name === "HWLAB_BOOT_COMMIT"), false, "agent-worker must not be switched to env reuse by device-pod work");
|
||||
assert.ok(agentWorkerEnv.some((entry) => entry.name === "HWLAB_RUNTIME_MODE" && entry.value === "env-reuse-git-mirror-checkout"));
|
||||
assert.ok(agentWorkerEnv.some((entry) => entry.name === "HWLAB_BOOT_REPO" && entry.value === "git@github.com:pikasTech/HWLAB.git"));
|
||||
assert.ok(agentWorkerEnv.some((entry) => entry.name === "HWLAB_BOOT_COMMIT" && entry.value === sourceRevision));
|
||||
assert.ok(agentWorkerEnv.some((entry) => entry.name === "HWLAB_BOOT_SH" && entry.value === "deploy/runtime/boot/hwlab-agent-worker.sh"));
|
||||
assert.equal(agentWorker?.spec?.template?.metadata?.annotations?.["hwlab.pikastech.local/runtime-mode"], "env-reuse-git-mirror-checkout");
|
||||
assert.equal(agentWorker?.spec?.template?.metadata?.annotations?.["hwlab.pikastech.local/boot-commit"], sourceRevision);
|
||||
const workloadTemplates = workloadsJson.items.filter((item) => item.spec?.template?.metadata?.labels?.["hwlab.pikastech.local/source-commit"]);
|
||||
assert.ok(workloadTemplates.length > 0, "expected rendered pod-template source labels");
|
||||
assert.equal(devicePod.spec.template.metadata.labels["hwlab.pikastech.local/source-commit"], sourceRevision);
|
||||
|
||||
@@ -83,8 +83,6 @@ const serviceSpecificPaths = Object.freeze({
|
||||
"hwlab-agent-worker": [
|
||||
"cmd/hwlab-agent-worker/",
|
||||
"internal/agent/",
|
||||
"internal/db/",
|
||||
"internal/audit/",
|
||||
"skills/hwlab-agent-runtime/"
|
||||
],
|
||||
"hwlab-device-pod": ["cmd/hwlab-device-pod/", "internal/device-pod/"],
|
||||
|
||||
Reference in New Issue
Block a user