import assert from "node:assert/strict"; import { execFile } from "node:child_process"; import { mkdtemp, writeFile } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import test from "node:test"; import { promisify } from "node:util"; import { SERVICE_IDS } from "../internal/protocol/index.mjs"; const execFileAsync = promisify(execFile); const repoRoot = path.resolve(path.dirname(new URL(import.meta.url).pathname), ".."); async function git(args) { const result = await execFileAsync("git", args, { cwd: repoRoot, timeout: 5000, maxBuffer: 1024 * 1024 }); return result.stdout.trim(); } function digestFor(index) { return `sha256:${String(index + 1).padStart(64, "0")}`; } test("refresh accepts an absolute publish report path", async () => { const commitId = await git(["rev-parse", "HEAD"]); const shortCommitId = await git(["rev-parse", "--short=7", "HEAD"]); const tempDir = await mkdtemp(path.join(os.tmpdir(), "hwlab-refresh-")); const reportPath = path.join(tempDir, "dev-artifacts.json"); await writeFile(reportPath, `${JSON.stringify({ reportVersion: "v1", taskId: "g14-artifact-publish", commitId: shortCommitId, artifactPublish: { status: "published", mode: "publish", sourceCommitId: commitId, registryPrefix: "127.0.0.1:5000/hwlab", serviceCount: SERVICE_IDS.length, requiredServiceCount: SERVICE_IDS.length, disabledServiceCount: 0, publishedCount: SERVICE_IDS.length, publishPlan: { version: "v2", services: SERVICE_IDS.map((serviceId, index) => ({ serviceId, required: true, digest: digestFor(index), image: `127.0.0.1:5000/hwlab/${serviceId}:${shortCommitId}`, imageTag: shortCommitId })) }, services: SERVICE_IDS.map((serviceId, index) => ({ serviceId, status: "published", artifactRequired: true, image: `127.0.0.1:5000/hwlab/${serviceId}:${shortCommitId}`, imageTag: shortCommitId, digest: digestFor(index), repositoryDigest: `127.0.0.1:5000/hwlab/${serviceId}@${digestFor(index)}`, buildCreatedAt: "2026-05-24T00:00:00.000Z", buildSource: `pikasTech/HWLAB@${commitId}`, componentCommitId: commitId, componentInputHash: `${String(index + 1).padStart(64, "a")}`, dockerfileHash: `${String(index + 1).padStart(64, "b")}`, buildArgsHash: `${String(index + 1).padStart(64, "c")}`, ciAffected: index === 0, ciReason: index === 0 ? ["component-path-changed"] : ["component-inputs-unchanged"] })) } }, null, 2)}\n`); const result = await execFileAsync(process.execPath, [ "scripts/refresh-artifact-catalog.mjs", "--target-ref", "HEAD", "--publish-report", reportPath, "--no-write" ], { cwd: repoRoot, timeout: 15000, maxBuffer: 10 * 1024 * 1024 }); const payload = JSON.parse(result.stdout); assert.equal(payload.status, "published"); assert.equal(payload.publishedCount, SERVICE_IDS.length); assert.deepEqual(payload.wrote, []); assert.equal(payload.services[0].componentCommitId, commitId); assert.equal(payload.services[0].ciAffected, true); assert.equal(payload.services[0].componentInputHash, `${String(1).padStart(64, "a")}`); }); test("refresh keeps reused service image tags from the publish report", async () => { const commitId = await git(["rev-parse", "HEAD"]); const shortCommitId = await git(["rev-parse", "--short=7", "HEAD"]); const reusedTag = "d42c77d"; const tempDir = await mkdtemp(path.join(os.tmpdir(), "hwlab-refresh-reused-")); const reportPath = path.join(tempDir, "dev-artifacts.json"); await writeFile(reportPath, `${JSON.stringify({ reportVersion: "v1", taskId: "g14-artifact-publish", commitId: shortCommitId, artifactPublish: { status: "published", mode: "publish", sourceCommitId: commitId, registryPrefix: "127.0.0.1:5000/hwlab", serviceCount: SERVICE_IDS.length, requiredServiceCount: SERVICE_IDS.length, disabledServiceCount: 0, publishedCount: 1, reusedCount: SERVICE_IDS.length - 1, publishPlan: { version: "v2", services: SERVICE_IDS.map((serviceId, index) => ({ serviceId, required: true, status: index === 0 ? "published" : "reused", digest: digestFor(index), image: `127.0.0.1:5000/hwlab/${serviceId}:${index === 0 ? shortCommitId : reusedTag}`, imageTag: index === 0 ? shortCommitId : reusedTag })) }, services: SERVICE_IDS.map((serviceId, index) => ({ serviceId, status: index === 0 ? "published" : "reused", artifactRequired: true, commitId: index === 0 ? shortCommitId : reusedTag, sourceCommitId: index === 0 ? commitId : reusedTag, image: `127.0.0.1:5000/hwlab/${serviceId}:${index === 0 ? shortCommitId : reusedTag}`, imageTag: index === 0 ? shortCommitId : reusedTag, digest: digestFor(index), repositoryDigest: `127.0.0.1:5000/hwlab/${serviceId}@${digestFor(index)}`, buildCreatedAt: "2026-05-24T00:00:00.000Z", buildSource: index === 0 ? `pikasTech/HWLAB@${commitId}` : `pikasTech/HWLAB@${reusedTag}`, componentCommitId: index === 0 ? commitId : reusedTag, componentInputHash: `${String(index + 1).padStart(64, "d")}`, dockerfileHash: `${String(index + 1).padStart(64, "e")}`, buildArgsHash: `${String(index + 1).padStart(64, "f")}`, ciAffected: index === 0, ciReason: index === 0 ? ["component-path-changed"] : ["component-inputs-unchanged"], reusedFrom: index === 0 ? null : `catalog:${reusedTag}` })) } }, null, 2)}\n`); const result = await execFileAsync(process.execPath, [ "scripts/refresh-artifact-catalog.mjs", "--target-ref", "HEAD", "--publish-report", reportPath, "--no-write" ], { cwd: repoRoot, timeout: 15000, maxBuffer: 10 * 1024 * 1024 }); const payload = JSON.parse(result.stdout); assert.equal(payload.status, "published"); assert.equal(payload.services[0].imageTag, shortCommitId); assert.equal(payload.services[1].imageTag, reusedTag); assert.equal(payload.services[1].publishState, "reused"); assert.equal(payload.services[1].reusedFrom, `catalog:${reusedTag}`); });