fix(cicd): preserve semantic package scope

This commit is contained in:
root
2026-07-21 12:41:02 +02:00
parent 34d7b8445e
commit 7d2c66bebd
3 changed files with 17 additions and 5 deletions
+1
View File
@@ -18,6 +18,7 @@
"harnessrl:native:l1-smoke": "node scripts/harnessrl-native-l1-smoke.mjs", "harnessrl:native:l1-smoke": "node scripts/harnessrl-native-l1-smoke.mjs",
"tasktree:backup:l1-smoke": "bun scripts/tasktree-backup-native-l1-smoke.mjs", "tasktree:backup:l1-smoke": "bun scripts/tasktree-backup-native-l1-smoke.mjs",
"tasktree:backup:restore:l1-smoke": "bun scripts/tasktree-backup-restore-l1-smoke.mjs", "tasktree:backup:restore:l1-smoke": "bun scripts/tasktree-backup-restore-l1-smoke.mjs",
"tasktree:schema:concurrency-smoke": "bun scripts/tasktree-schema-concurrency-smoke.mjs",
"workbench:api:dev": "bun --watch cmd/hwlab-workbench-api/main.ts", "workbench:api:dev": "bun --watch cmd/hwlab-workbench-api/main.ts",
"workbench:worker:dev": "bun --watch cmd/hwlab-workbench-worker/main.ts", "workbench:worker:dev": "bun --watch cmd/hwlab-workbench-worker/main.ts",
"workbench:web:dev": "cd web/hwlab-cloud-web && WORKBENCH_VITE_USE_POLLING=1 bun run dev", "workbench:web:dev": "cd web/hwlab-cloud-web && WORKBENCH_VITE_USE_POLLING=1 bun run dev",
+8 -2
View File
@@ -1159,8 +1159,13 @@ test("v02 planner treats shared trace renderer changes as cloud-web code rollout
assert.deepEqual(cloudWeb.reason, ["code-input-changed", "component-path-changed"]); assert.deepEqual(cloudWeb.reason, ["code-input-changed", "component-path-changed"]);
}); });
test("planner ignores package script cleanup for runtime images", async () => { test("planner ignores package scripts-only changes even when package is a component path", async () => {
const repo = await createFixtureRepo(); const repo = await createFixtureRepo();
const deployPath = path.join(repo, "deploy/deploy.yaml");
const deploy = await readStructuredFile(repo, deployPath);
deploy.lanes.v02.serviceDeclarations["hwlab-cloud-api"].componentPaths.push("package.json");
deploy.lanes.v02.serviceDeclarations["hwlab-cloud-web"].componentPaths.push("package.json");
await writeStructuredFile(repo, deployPath, deploy);
await writeFile(path.join(repo, "package.json"), JSON.stringify({ await writeFile(path.join(repo, "package.json"), JSON.stringify({
type: "module", type: "module",
scripts: { scripts: {
@@ -1170,7 +1175,7 @@ test("planner ignores package script cleanup for runtime images", async () => {
"@openai/codex": "^0.128.0" "@openai/codex": "^0.128.0"
} }
}, null, 2)); }, null, 2));
await git(repo, ["add", "package.json"]); await git(repo, ["add", "deploy/deploy.yaml", "package.json"]);
await git(repo, ["commit", "-m", "seed package scripts"]); await git(repo, ["commit", "-m", "seed package scripts"]);
await refreshFixtureCatalogForHead(repo); await refreshFixtureCatalogForHead(repo);
@@ -1191,6 +1196,7 @@ test("planner ignores package script cleanup for runtime images", async () => {
const plan = await planV02CoreServices(repo, { baseRef: "HEAD~1", targetRef: "HEAD" }); const plan = await planV02CoreServices(repo, { baseRef: "HEAD~1", targetRef: "HEAD" });
assert.deepEqual(plan.affectedServices, []); assert.deepEqual(plan.affectedServices, []);
assert.deepEqual(plan.buildServices, []); assert.deepEqual(plan.buildServices, []);
assert.equal(plan.services.every((service) => !service.changedPaths.includes("package.json")), true);
assert.equal(plan.imageBuildRequired, false); assert.equal(plan.imageBuildRequired, false);
assert.equal(plan.buildSkippedCount, 2); assert.equal(plan.buildSkippedCount, 2);
}); });
+8 -3
View File
@@ -124,7 +124,8 @@ export async function createCiPlan(options = {}) {
const services = []; const services = [];
for (const model of componentModels) { for (const model of componentModels) {
const directMatches = matchingPaths(normalizedChangedPaths, model.componentPaths); const directMatches = matchingPaths(normalizedChangedPaths, model.componentPaths)
.filter((item) => !model.runtimeDeps.includes(item) || semanticRuntimeDepPaths.has(item));
const sharedMatches = matchingPaths(normalizedChangedPaths, model.sharedPaths); const sharedMatches = matchingPaths(normalizedChangedPaths, model.sharedPaths);
const rawRuntimeDepMatches = matchingPaths(normalizedChangedPaths, model.runtimeDeps); const rawRuntimeDepMatches = matchingPaths(normalizedChangedPaths, model.runtimeDeps);
const runtimeDepMatches = rawRuntimeDepMatches.filter((item) => semanticRuntimeDepPaths.has(item)); const runtimeDepMatches = rawRuntimeDepMatches.filter((item) => semanticRuntimeDepPaths.has(item));
@@ -147,7 +148,10 @@ export async function createCiPlan(options = {}) {
? matchingPaths(normalizedChangedPaths, envInputPaths) ? matchingPaths(normalizedChangedPaths, envInputPaths)
.filter((item) => !model.runtimeDeps.includes(item) || semanticRuntimeDepPaths.has(item)) .filter((item) => !model.runtimeDeps.includes(item) || semanticRuntimeDepPaths.has(item))
: []; : [];
const codeMatches = envReuse ? matchingPaths(normalizedChangedPaths, codeInputPaths) : []; const codeMatches = envReuse
? matchingPaths(normalizedChangedPaths, codeInputPaths)
.filter((item) => !model.runtimeDeps.includes(item) || semanticRuntimeDepPaths.has(item))
: [];
const relevantEnvMatches = envMatches.filter((item) => !isTestOnlyPath(item) && !isDocsOnlyPath(item)); const relevantEnvMatches = envMatches.filter((item) => !isTestOnlyPath(item) && !isDocsOnlyPath(item));
const relevantCodeMatches = codeMatches.filter((item) => !isTestOnlyPath(item) && !isDocsOnlyPath(item)); const relevantCodeMatches = codeMatches.filter((item) => !isTestOnlyPath(item) && !isDocsOnlyPath(item));
const imageRelevantChangedPaths = uniqueSorted([ const imageRelevantChangedPaths = uniqueSorted([
@@ -162,7 +166,8 @@ export async function createCiPlan(options = {}) {
semanticPackageJson: true semanticPackageJson: true
}) : null; }) : null;
const codeInputHash = envReuse ? await hashGitPaths(repoRoot, targetRef, codeInputPaths, { const codeInputHash = envReuse ? await hashGitPaths(repoRoot, targetRef, codeInputPaths, {
skipPath: (filePath) => isTestOnlyPath(filePath) || isDocsOnlyPath(filePath) skipPath: (filePath) => isTestOnlyPath(filePath) || isDocsOnlyPath(filePath),
semanticPackageJson: true
}) : null; }) : null;
const currentEnvironmentImage = envReuse ? envReuseImageRef(registryPrefix, model.serviceId, environmentInputHash, envArtifactGroup) : null; const currentEnvironmentImage = envReuse ? envReuseImageRef(registryPrefix, model.serviceId, environmentInputHash, envArtifactGroup) : null;
const catalogEnvironmentImage = envReuse ? environmentImageFromCatalog(model.serviceId, catalogRecord) : null; const catalogEnvironmentImage = envReuse ? environmentImageFromCatalog(model.serviceId, catalogRecord) : null;