diff --git a/scripts/dependency-changes-report.mjs b/scripts/dependency-changes-report.mjs index 04f5f879688..0634461f9a0 100644 --- a/scripts/dependency-changes-report.mjs +++ b/scripts/dependency-changes-report.mjs @@ -249,38 +249,46 @@ export function parseArgs(argv) { jsonPath: null, markdownPath: null, }; + const seen = new Set(); + const setOnce = (flag, key, value) => { + if (seen.has(flag)) { + throw new Error(`${flag} was provided more than once.`); + } + seen.add(flag); + options[key] = value; + }; for (let index = 0; index < argv.length; index += 1) { const arg = argv[index]; if (arg === "--") { continue; } if (arg === "--root") { - options.rootDir = readRequiredValue(argv, index, "--root"); + setOnce(arg, "rootDir", readRequiredValue(argv, index, "--root")); index += 1; continue; } if (arg === "--base-ref") { - options.baseRef = readRequiredValue(argv, index, "--base-ref"); + setOnce(arg, "baseRef", readRequiredValue(argv, index, "--base-ref")); index += 1; continue; } if (arg === "--base-lockfile") { - options.baseLockfile = readRequiredValue(argv, index, "--base-lockfile"); + setOnce(arg, "baseLockfile", readRequiredValue(argv, index, "--base-lockfile")); index += 1; continue; } if (arg === "--head-lockfile") { - options.headLockfile = readRequiredValue(argv, index, "--head-lockfile"); + setOnce(arg, "headLockfile", readRequiredValue(argv, index, "--head-lockfile")); index += 1; continue; } if (arg === "--json") { - options.jsonPath = readRequiredValue(argv, index, "--json"); + setOnce(arg, "jsonPath", readRequiredValue(argv, index, "--json")); index += 1; continue; } if (arg === "--markdown") { - options.markdownPath = readRequiredValue(argv, index, "--markdown"); + setOnce(arg, "markdownPath", readRequiredValue(argv, index, "--markdown")); index += 1; continue; } @@ -289,6 +297,9 @@ export function parseArgs(argv) { if (!options.baseRef && !options.baseLockfile) { throw new Error("Expected --base-ref or --base-lockfile ."); } + if (options.baseRef && options.baseLockfile) { + throw new Error("Use either --base-ref or --base-lockfile, not both."); + } return options; } diff --git a/scripts/dependency-ownership-surface-report.mjs b/scripts/dependency-ownership-surface-report.mjs index 7775990b233..47ed49f0a51 100644 --- a/scripts/dependency-ownership-surface-report.mjs +++ b/scripts/dependency-ownership-surface-report.mjs @@ -412,6 +412,14 @@ export function parseArgs(argv) { jsonPath: null, markdownPath: null, }; + const seen = new Set(); + const setOnce = (flag, key, value) => { + if (seen.has(flag)) { + throw new Error(`${flag} was provided more than once.`); + } + seen.add(flag); + options[key] = value; + }; for (let index = 0; index < argv.length; index += 1) { const arg = argv[index]; if (arg === "--") { @@ -422,6 +430,10 @@ export function parseArgs(argv) { continue; } if (arg === "--json") { + if (seen.has(arg)) { + throw new Error(`${arg} was provided more than once.`); + } + seen.add(arg); options.asJson = true; if (argv[index + 1] && !argv[index + 1].startsWith("-")) { options.jsonPath = argv[++index]; @@ -429,7 +441,7 @@ export function parseArgs(argv) { continue; } if (arg === "--markdown") { - options.markdownPath = readArtifactPath(argv, index, arg); + setOnce(arg, "markdownPath", readArtifactPath(argv, index, arg)); index += 1; continue; } diff --git a/test/scripts/dependency-changes-report.test.ts b/test/scripts/dependency-changes-report.test.ts index ef6218ce3c5..fbead670201 100644 --- a/test/scripts/dependency-changes-report.test.ts +++ b/test/scripts/dependency-changes-report.test.ts @@ -88,6 +88,24 @@ describe("dependency-changes-report", () => { } }); + it("rejects duplicate and conflicting dependency report inputs", () => { + for (const flag of [ + "--root", + "--base-ref", + "--base-lockfile", + "--head-lockfile", + "--json", + "--markdown", + ]) { + expect(() => parseArgs(["--base-ref", "main", flag, "one", flag, "two"])).toThrow( + `${flag} was provided more than once.`, + ); + } + expect(() => parseArgs(["--base-ref", "main", "--base-lockfile", "base-lock.yaml"])).toThrow( + "Use either --base-ref or --base-lockfile, not both.", + ); + }); + it("reports CLI argument errors without a Node stack trace", () => { const missingBase = runCli(); expect(missingBase.status).toBe(1); diff --git a/test/scripts/dependency-ownership-surface-report.test.ts b/test/scripts/dependency-ownership-surface-report.test.ts index 63d469a73a9..383d0b1b19f 100644 --- a/test/scripts/dependency-ownership-surface-report.test.ts +++ b/test/scripts/dependency-ownership-surface-report.test.ts @@ -57,6 +57,15 @@ describe("parseArgs", () => { }); expect(() => parseArgs(["--json", "-h"])).toThrow("Unsupported argument: -h"); }); + + it("rejects duplicate report artifact options", () => { + expect(() => parseArgs(["--json", "first.json", "--json", "second.json"])).toThrow( + "--json was provided more than once.", + ); + expect(() => + parseArgs(["--markdown", "first.md", "--markdown", "second.md"]), + ).toThrow("--markdown was provided more than once."); + }); }); describe("collectDependencyOwnershipSurfaceReport", () => {