feat(lanes): add isIntegrationEnabled toggle check (F3a)

This commit is contained in:
2026-08-05 16:12:58 +07:00
parent 36d66cda07
commit 8fc7a32490
2 changed files with 58 additions and 0 deletions
+29
View File
@@ -111,3 +111,32 @@ describe("resolveProfile — E2 declarations", () => {
assert.deepEqual(profile.generatedMergePaths, ["api/openapi.json", "api/client.ts"]); assert.deepEqual(profile.generatedMergePaths, ["api/openapi.json", "api/client.ts"]);
}); });
}); });
describe("isIntegrationEnabled", () => {
it("returns false when integrations.env doesn't exist at all", () => {
const lane = makeLane();
writeProfile(lane.cwd, "PORTS=api\n");
assert.equal(profileLib.isIntegrationEnabled(lanesLib.getLane(lane.id), "tracker"), false);
});
it("returns true when <NAME>_ENABLED=1 is set", () => {
const lane = makeLane();
writeProfile(lane.cwd, "PORTS=api\n");
fsMod.writeFileSync(
pathMod.join(lane.cwd, ".ccam", "profile", "integrations.env"),
"TRACKER_ENABLED=1\nTRACKER_PROJECT=demo\n"
);
assert.equal(profileLib.isIntegrationEnabled(lanesLib.getLane(lane.id), "tracker"), true);
});
it("returns false when the flag is 0 or absent from an existing file", () => {
const lane = makeLane();
writeProfile(lane.cwd, "PORTS=api\n");
fsMod.writeFileSync(
pathMod.join(lane.cwd, ".ccam", "profile", "integrations.env"),
"DEV_QC_ENABLED=0\n"
);
assert.equal(profileLib.isIntegrationEnabled(lanesLib.getLane(lane.id), "dev_qc"), false);
assert.equal(profileLib.isIntegrationEnabled(lanesLib.getLane(lane.id), "ci_wait"), false);
});
});
+29
View File
@@ -195,6 +195,34 @@ function profileSearchPaths(lane) {
.map((root) => path.join(root, PROFILE_SUBDIR)); .map((root) => path.join(root, PROFILE_SUBDIR));
} }
/**
* Whether a named integration is turned on for this lane — reads
* .ccam/profile/integrations.env (same two-location search as profile.env:
* the lane's own working copy first, the source repo second) and checks
* <NAME>_ENABLED=1. A missing file or missing key is off, never an error —
* same off-by-default shape every other optional declaration follows.
*
* @param {object} lane - Lane row (`cwd`, `source_repo`).
* @param {string} name - Lowercase integration name, e.g. "tracker", "dev_qc", "ci_wait".
* @returns {boolean}
*/
function isIntegrationEnabled(lane, name) {
const candidates = [lane.cwd, lane.source_repo].filter(Boolean);
const key = `${name.toUpperCase()}_ENABLED`;
for (const root of candidates) {
const filePath = path.join(root, PROFILE_SUBDIR, "integrations.env");
if (!fs.existsSync(filePath)) continue;
let declared;
try {
declared = parseEnvFile(fs.readFileSync(filePath, "utf8"));
} catch {
continue;
}
return declared[key] === "1";
}
return false;
}
/** /**
* A `harness_spawn` shell function, injected into every hook. * A `harness_spawn` shell function, injected into every hook.
* *
@@ -428,6 +456,7 @@ module.exports = {
parseQcBootEnv, parseQcBootEnv,
resolveProfile, resolveProfile,
profileSearchPaths, profileSearchPaths,
isIntegrationEnabled,
hookEnv, hookEnv,
runHook, runHook,
}; };