From 85918f1011acbfaf78978f182732b357604f21db Mon Sep 17 00:00:00 2001 From: Lyon <88232613+pikasTech@users.noreply.github.com> Date: Fri, 26 Jun 2026 06:13:04 +0800 Subject: [PATCH] fix: allow unchanged envreuse reused reports (#2179) --- scripts/artifact-publish.mjs | 16 ++++- scripts/ci-plan.test.mjs | 113 +++++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+), 1 deletion(-) diff --git a/scripts/artifact-publish.mjs b/scripts/artifact-publish.mjs index fca5611f..08b17a01 100644 --- a/scripts/artifact-publish.mjs +++ b/scripts/artifact-publish.mjs @@ -1842,6 +1842,20 @@ function artifactRecordFromExternalReport({ service, record, reportPath }) { }; } +function externalArtifactSourceCommitMismatchIsFatal({ artifact, service, commitId }) { + if (!artifact.sourceCommitId || artifact.sourceCommitId === commitId) return false; + if ( + artifact.status === "reused" && + (artifact.envReuse === true || service?.envReuse === true) && + artifact.affected === false && + artifact.codeChanged === false && + artifact.envChanged === false + ) { + return false; + } + return true; +} + async function readExternalServiceArtifacts({ dir, services, commitId }) { const artifacts = new Map(); const blockers = []; @@ -1886,7 +1900,7 @@ async function readExternalServiceArtifacts({ dir, services, commitId }) { next: "Inspect the corresponding service TaskRun logs and rerun the failed service build." })); } - if (artifact.sourceCommitId && artifact.sourceCommitId !== commitId) { + if (externalArtifactSourceCommitMismatchIsFatal({ artifact, service, commitId })) { blockers.push(blocker({ type: "contract_blocker", scope: service.serviceId, diff --git a/scripts/ci-plan.test.mjs b/scripts/ci-plan.test.mjs index 866ca28f..25d236ca 100644 --- a/scripts/ci-plan.test.mjs +++ b/scripts/ci-plan.test.mjs @@ -389,6 +389,119 @@ test("artifact publish ignores stale external reports for envreuse services that assert.equal(result.services[0].sourceCommitId, currentCommit); }); +test("artifact publish accepts stale reused envreuse external reports for unchanged services in buildServices", async () => { + const currentCommit = (await git(process.cwd(), ["rev-parse", "HEAD"])).stdout.trim(); + const oldCommit = "1".repeat(40); + const digest = `sha256:${"4".repeat(64)}`; + const tmp = await mkdtemp(path.join(os.tmpdir(), "hwlab-artifact-reused-report-")); + const reportDir = path.join(tmp, "service-results"); + await mkdir(reportDir, { recursive: true }); + const image = "127.0.0.1:5000/hwlab/hwlab-cloud-web-env:env-stable"; + const deployPath = path.join(tmp, "deploy.yaml"); + const catalogPath = path.join(tmp, "artifact-catalog.v03.json"); + const ciPlanPath = path.join(tmp, "ci-plan.json"); + const deploy = createDeployFixture({ serviceIds: ["hwlab-cloud-web"] }); + deploy.lanes.v03.namespace = "hwlab-v03"; + deploy.lanes.v03.endpoint = "https://hwlab.pikapython.com"; + await writeStructuredFile(process.cwd(), deployPath, deploy); + + const catalog = createCatalogFixture("ready", { + serviceIds: ["hwlab-cloud-web"], + sourceCommitId: oldCommit, + bootCommits: { "hwlab-cloud-web": oldCommit }, + environmentInputHashes: { "hwlab-cloud-web": "e".repeat(64) }, + codeInputHashes: { "hwlab-cloud-web": "c".repeat(64) } + }); + catalog.environment = "v03"; + catalog.profile = "v03"; + catalog.namespace = "hwlab-v03"; + catalog.allowedProfiles = ["v03"]; + catalog.forbiddenProfiles = ["dev", "prod"]; + for (const service of catalog.services) { + service.profile = "v03"; + service.namespace = "hwlab-v03"; + } + await writeFile(catalogPath, JSON.stringify(catalog, null, 2)); + await writeFile(ciPlanPath, JSON.stringify({ + planVersion: "v1", + sourceCommitId: currentCommit, + shortCommitId: currentCommit.slice(0, 12), + baseRef: "HEAD~1", + targetRef: "HEAD", + compatibility: { lane: "v03" }, + changedPathSummary: {}, + imageBuildRequired: true, + affectedServices: [], + buildServices: ["hwlab-cloud-web"], + rolloutServices: [], + reusedServices: [], + buildSkippedCount: 0, + ciCdPlan: {}, + services: [{ + serviceId: "hwlab-cloud-web", + runtimeMode: "env-reuse-git-mirror-checkout", + envReuse: true, + affected: false, + ciAffected: false, + buildRequired: true, + envChanged: false, + codeChanged: false, + environmentImage: image, + environmentDigest: digest, + environmentInputHash: "e".repeat(64), + codeInputHash: "c".repeat(64), + bootRepo: "git@github.com:pikasTech/HWLAB.git", + bootCommit: oldCommit, + bootSh: "deploy/runtime/boot/hwlab-cloud-web.sh", + reason: ["component-inputs-unchanged"], + reuse: { status: "ready", image, digest, reusedFrom: "e".repeat(64) } + }] + }, null, 2)); + await writeFile(path.join(reportDir, "hwlab-cloud-web.json"), JSON.stringify({ + artifactPublish: { + services: [{ + serviceId: "hwlab-cloud-web", + status: "reused", + commitId: oldCommit, + sourceCommitId: oldCommit, + image, + imageTag: "env-stable", + digest, + repositoryDigest: `${image}@${digest}`, + runtimeMode: "env-reuse-git-mirror-checkout", + envReuse: true, + affected: false, + ciAffected: false, + codeChanged: false, + envChanged: false + }] + } + }, null, 2)); + + const { stdout } = await execFileAsync(process.execPath, [ + "scripts/artifact-publish.mjs", + "--build", + "--lane", "v03", + "--services", "hwlab-cloud-web", + "--deploy-config", deployPath, + "--catalog-path", catalogPath, + "--ci-plan-path", ciPlanPath, + "--external-service-report-dir", reportDir, + "--registry-prefix", "127.0.0.1:5000/hwlab", + "--no-report" + ], { + cwd: process.cwd(), + env: { ...process.env, HWLAB_CI_PLAN_VERIFY_REUSE_REGISTRY: "false" } + }); + const result = JSON.parse(stdout); + const blockerText = JSON.stringify(result.blockers ?? []); + assert.equal(blockerText.includes(oldCommit), false); + assert.equal(result.status, "built"); + assert.equal(result.services[0].serviceId, "hwlab-cloud-web"); + assert.equal(result.services[0].status, "reused"); + assert.equal(result.services[0].sourceCommitId, oldCommit); +}); + test("component model uses service paths declared in deploy config", () => { const deploy = createDeployFixture(); const models = componentModelsForServices(["hwlab-cloud-api"], deploy.lanes.v02);