fix(verify): ignore stale broad cargo preferences
This commit is contained in:
parent
d1ca3d035c
commit
c5850c8039
2 changed files with 87 additions and 8 deletions
|
|
@ -26,7 +26,7 @@ test("discoverCommands_when_preferences_reference_missing_repo_dirs_falls_back_t
|
|||
const root = makeProject();
|
||||
writeFileSync(join(root, "pyproject.toml"), "[project]\nname = 'demo'\n");
|
||||
const staleCommand =
|
||||
"bash -c 'set -e; for d in \"scanner\" \"supervisor\"; do (cd \"$d\" && cargo check); done'";
|
||||
'bash -c \'set -e; for d in "scanner" "supervisor"; do (cd "$d" && cargo check); done\'';
|
||||
|
||||
const result = discoverCommands({
|
||||
cwd: root,
|
||||
|
|
@ -44,7 +44,7 @@ test("discoverCommands_when_for_loop_dirs_exist_keeps_preference_commands", () =
|
|||
mkdirSync(join(root, "scanner"));
|
||||
mkdirSync(join(root, "supervisor"));
|
||||
const command =
|
||||
"bash -c 'set -e; for d in \"scanner\" \"supervisor\"; do (cd \"$d\" && cargo check); done'";
|
||||
'bash -c \'set -e; for d in "scanner" "supervisor"; do (cd "$d" && cargo check); done\'';
|
||||
|
||||
const result = discoverCommands({
|
||||
cwd: root,
|
||||
|
|
@ -57,13 +57,56 @@ test("discoverCommands_when_for_loop_dirs_exist_keeps_preference_commands", () =
|
|||
});
|
||||
});
|
||||
|
||||
test("discoverCommands_when_broad_cargo_preferences_conflict_with_task_plan_uses_task_plan", () => {
|
||||
const root = makeProject();
|
||||
mkdirSync(join(root, "scanner"));
|
||||
mkdirSync(join(root, "supervisor"));
|
||||
writeFileSync(join(root, "pyproject.toml"), "[project]\nname = 'demo'\n");
|
||||
const command =
|
||||
'bash -c \'set -e; for d in "scanner" "supervisor"; do (cd "$d" && cargo check); done\'';
|
||||
|
||||
const result = discoverCommands({
|
||||
cwd: root,
|
||||
preferenceCommands: [command, "uv run pytest -x"],
|
||||
taskPlanVerify: "make pytest",
|
||||
});
|
||||
|
||||
assert.deepEqual(result, {
|
||||
commands: ["make pytest"],
|
||||
source: "task-plan",
|
||||
});
|
||||
});
|
||||
|
||||
test("discoverCommands_when_broad_cargo_preferences_conflict_with_makefile_uses_python_project", () => {
|
||||
const root = makeProject();
|
||||
mkdirSync(join(root, "scanner"));
|
||||
mkdirSync(join(root, "supervisor"));
|
||||
writeFileSync(join(root, "pyproject.toml"), "[project]\nname = 'demo'\n");
|
||||
writeFileSync(
|
||||
join(root, "Makefile"),
|
||||
"verify-python:\n\tpython -m pytest -q\n",
|
||||
);
|
||||
const command =
|
||||
'bash -c \'set -e; for d in "scanner" "supervisor"; do (cd "$d" && cargo check); done\'';
|
||||
|
||||
const result = discoverCommands({
|
||||
cwd: root,
|
||||
preferenceCommands: [command, "uv run pytest -x"],
|
||||
});
|
||||
|
||||
assert.deepEqual(result, {
|
||||
commands: ["python -m pytest -q"],
|
||||
source: "python-project",
|
||||
});
|
||||
});
|
||||
|
||||
test("hasMissingProjectPathReferences_detects_absent_for_loop_cd_targets", () => {
|
||||
const root = makeProject();
|
||||
|
||||
assert.equal(
|
||||
hasMissingProjectPathReferences(
|
||||
root,
|
||||
"bash -c 'for d in \"scanner\"; do (cd \"$d\" && cargo test); done'",
|
||||
'bash -c \'for d in "scanner"; do (cd "$d" && cargo test); done\'',
|
||||
),
|
||||
true,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -22,7 +22,9 @@ function truncate(value, maxBytes) {
|
|||
const PACKAGE_SCRIPT_KEYS = ["typecheck", "lint", "test"];
|
||||
|
||||
function extractForLoopCdDirs(command) {
|
||||
const match = command.match(/for\s+d\s+in\s+(.+?);\s*do\s*\(\s*cd\s+["']?\$d["']?/s);
|
||||
const match = command.match(
|
||||
/for\s+d\s+in\s+(.+?);\s*do\s*\(\s*cd\s+["']?\$d["']?/s,
|
||||
);
|
||||
if (!match) return null;
|
||||
const segment = match[1];
|
||||
const dirs = [];
|
||||
|
|
@ -51,10 +53,42 @@ export function hasMissingProjectPathReferences(cwd, command) {
|
|||
return dirs.some((dir) => !existsSync(join(cwd, dir)));
|
||||
}
|
||||
|
||||
function normalizeConfiguredCommands(cwd, commands) {
|
||||
function isBroadMultiProjectCargoCommand(command) {
|
||||
const dirs = extractForLoopCdDirs(command);
|
||||
if (!dirs || dirs.length < 2) return false;
|
||||
return /\bcargo\s+(fmt|check|test|clippy)\b/.test(command);
|
||||
}
|
||||
|
||||
function hasMakefileVerifyTarget(cwd) {
|
||||
const makefilePath = join(cwd, "Makefile");
|
||||
if (!existsSync(makefilePath)) return false;
|
||||
try {
|
||||
return /^verify[-\w]*:/m.test(readFileSync(makefilePath, "utf-8"));
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function hasProjectLocalVerificationSignal(cwd, taskPlanVerify) {
|
||||
if (taskPlanVerify?.trim()) return true;
|
||||
if (hasMakefileVerifyTarget(cwd)) return true;
|
||||
return (
|
||||
existsSync(join(cwd, "package.json")) ||
|
||||
existsSync(join(cwd, "pyproject.toml"))
|
||||
);
|
||||
}
|
||||
|
||||
function looksLikeStalePreferenceSet(cwd, commands, taskPlanVerify) {
|
||||
if (!hasProjectLocalVerificationSignal(cwd, taskPlanVerify)) return false;
|
||||
return commands.some((cmd) => isBroadMultiProjectCargoCommand(cmd));
|
||||
}
|
||||
|
||||
function normalizeConfiguredCommands(cwd, commands, taskPlanVerify = "") {
|
||||
const filtered = (commands ?? []).map((c) => c.trim()).filter(Boolean);
|
||||
if (filtered.length === 0) return { commands: [], stale: false };
|
||||
const stale = filtered.some((cmd) => hasMissingProjectPathReferences(cwd, cmd));
|
||||
const stale =
|
||||
filtered.some((cmd) => hasMissingProjectPathReferences(cwd, cmd)) ||
|
||||
looksLikeStalePreferenceSet(cwd, filtered, taskPlanVerify);
|
||||
return { commands: stale ? [] : filtered, stale };
|
||||
}
|
||||
|
||||
|
|
@ -102,6 +136,7 @@ export function discoverCommands(options) {
|
|||
const configured = normalizeConfiguredCommands(
|
||||
options.cwd,
|
||||
options.preferenceCommands,
|
||||
options.taskPlanVerify,
|
||||
);
|
||||
if (configured.commands.length > 0) {
|
||||
return { commands: configured.commands, source: "preference" };
|
||||
|
|
@ -114,8 +149,9 @@ export function discoverCommands(options) {
|
|||
.map((c) => c.trim())
|
||||
.filter(Boolean);
|
||||
const configured = normalizeConfiguredCommands(options.cwd, rawCommands);
|
||||
const commands = configured.commands
|
||||
.filter((c) => sanitizeCommand(c) !== null);
|
||||
const commands = configured.commands.filter(
|
||||
(c) => sanitizeCommand(c) !== null,
|
||||
);
|
||||
if (commands.length > 0) {
|
||||
return { commands, source: "task-plan" };
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue