feat(triage): add args parser with env var fallback

This commit is contained in:
diegosouzapw
2026-05-19 01:37:28 -03:00
parent 7ba05fdae5
commit cfd2e19267
2 changed files with 158 additions and 0 deletions

View File

@@ -0,0 +1,110 @@
/**
* Args parser for feature-triage CLI.
* Precedence: CLI flag > env var > default.
*/
const DEFAULTS = {
quarantineDays: 14,
overrideThumbs: 5,
overrideCommenters: 3,
staleNeedsDays: 30,
staleDeferDays: 90,
ideiaDir: "_ideia",
changelog: "CHANGELOG.md",
output: null,
dryRun: false,
verbose: false,
onlyIssues: [],
};
const ENV_MAP = {
quarantineDays: "FEATURE_QUARANTINE_DAYS",
overrideThumbs: "FEATURE_OVERRIDE_THUMBS",
overrideCommenters: "FEATURE_OVERRIDE_COMMENTERS",
staleNeedsDays: "FEATURE_STALE_NEEDS_DAYS",
staleDeferDays: "FEATURE_STALE_DEFER_DAYS",
};
function takeNext(argv, i) {
if (i + 1 >= argv.length) {
throw new Error(`${argv[i]} requires a value`);
}
return argv[i + 1];
}
export function parseArgs(argv, env = process.env) {
const out = { ...DEFAULTS, owner: null, repo: null };
for (const [key, envKey] of Object.entries(ENV_MAP)) {
if (env[envKey] !== undefined) {
const n = Number(env[envKey]);
if (Number.isFinite(n)) out[key] = n;
}
}
for (let i = 0; i < argv.length; i++) {
const a = argv[i];
switch (a) {
case "--owner":
out.owner = takeNext(argv, i);
i++;
break;
case "--repo":
out.repo = takeNext(argv, i);
i++;
break;
case "--quarantine-days":
out.quarantineDays = Number(takeNext(argv, i));
i++;
break;
case "--override-thumbs":
out.overrideThumbs = Number(takeNext(argv, i));
i++;
break;
case "--override-commenters":
out.overrideCommenters = Number(takeNext(argv, i));
i++;
break;
case "--stale-needs-days":
out.staleNeedsDays = Number(takeNext(argv, i));
i++;
break;
case "--stale-defer-days":
out.staleDeferDays = Number(takeNext(argv, i));
i++;
break;
case "--ideia-dir":
out.ideiaDir = takeNext(argv, i);
i++;
break;
case "--changelog":
out.changelog = takeNext(argv, i);
i++;
break;
case "--output":
out.output = takeNext(argv, i);
i++;
break;
case "--dry-run":
out.dryRun = true;
break;
case "--verbose":
out.verbose = true;
break;
case "--only-issues":
out.onlyIssues = takeNext(argv, i)
.split(",")
.map((s) => Number(s.trim()))
.filter(Number.isFinite);
i++;
break;
default:
throw new Error(`Unknown arg: ${a}`);
}
}
if (!out.owner) throw new Error("--owner is required");
if (!out.repo) throw new Error("--repo is required");
return out;
}

View File

@@ -0,0 +1,48 @@
import { describe, it } from "node:test";
import assert from "node:assert/strict";
import { parseArgs } from "../../../scripts/features/lib/args.mjs";
describe("parseArgs", () => {
it("requires --owner and --repo", () => {
assert.throws(() => parseArgs([]), /--owner is required/);
assert.throws(() => parseArgs(["--owner", "x"]), /--repo is required/);
});
it("applies defaults when args/env missing", () => {
const args = parseArgs(["--owner", "diegosouzapw", "--repo", "OmniRoute"], {});
assert.equal(args.quarantineDays, 14);
assert.equal(args.overrideThumbs, 5);
assert.equal(args.overrideCommenters, 3);
assert.equal(args.staleNeedsDays, 30);
assert.equal(args.staleDeferDays, 90);
assert.equal(args.ideiaDir, "_ideia");
assert.equal(args.changelog, "CHANGELOG.md");
assert.equal(args.output, null);
assert.equal(args.dryRun, false);
assert.equal(args.verbose, false);
assert.deepEqual(args.onlyIssues, []);
});
it("CLI arg takes precedence over env var", () => {
const args = parseArgs(["--owner", "x", "--repo", "y", "--quarantine-days", "7"], {
FEATURE_QUARANTINE_DAYS: "21",
});
assert.equal(args.quarantineDays, 7);
});
it("env var used when CLI arg missing", () => {
const args = parseArgs(["--owner", "x", "--repo", "y"], { FEATURE_QUARANTINE_DAYS: "21" });
assert.equal(args.quarantineDays, 21);
});
it("parses --only-issues as comma-separated numbers", () => {
const args = parseArgs(["--owner", "x", "--repo", "y", "--only-issues", "1046,1041,980"], {});
assert.deepEqual(args.onlyIssues, [1046, 1041, 980]);
});
it("parses --dry-run and --verbose as booleans", () => {
const args = parseArgs(["--owner", "x", "--repo", "y", "--dry-run", "--verbose"], {});
assert.equal(args.dryRun, true);
assert.equal(args.verbose, true);
});
});