Skip to content

Commit 1001da0

Browse files
Copiloticlantondmichon-msft
authored
[rush-lib] Add validation for parameterNamesToIgnore in rush-project.json (#5448)
* Initial plan * Add validation for parameterNamesToIgnore in rush-project.json Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com> * Remove unused test repository * Address PR feedback: combine error messages and add malformed parameter test Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com> * Add changelog entry for rush-lib Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com> * Add tests for parameter validation with valid parameters Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com> * Update libraries/rush-lib/src/api/test/RushProjectConfiguration.test.ts Co-authored-by: David Michon <dmichon@microsoft.com> * Update common/changes/@microsoft/rush-lib/copilot-validate-parameter-names-to-ignore_2025-11-13-23-26.json * Fix changelog entry to use @microsoft/rush package name Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com> Co-authored-by: Ian Clanton-Thuon <iclanton@users.noreply.github.com> Co-authored-by: David Michon <dmichon@microsoft.com>
1 parent 817e924 commit 1001da0

File tree

7 files changed

+149
-1
lines changed

7 files changed

+149
-1
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"changes": [
3+
{
4+
"comment": "",
5+
"type": "none",
6+
"packageName": "@microsoft/rush"
7+
}
8+
],
9+
"packageName": "@microsoft/rush",
10+
"email": "198982749+Copilot@users.noreply.github.com"
11+
}

libraries/rush-lib/src/api/RushProjectConfiguration.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,35 @@ export class RushProjectConfiguration {
341341
}
342342
}
343343
}
344+
345+
// Validate that parameter names to ignore actually exist for this operation
346+
if (operationSettings.parameterNamesToIgnore) {
347+
// Build a set of valid parameter names for this phase
348+
const validParameterNames: Set<string> = new Set<string>();
349+
for (const parameter of phase.associatedParameters) {
350+
validParameterNames.add(parameter.longName);
351+
}
352+
353+
// Collect all invalid parameter names
354+
const invalidParameterNames: string[] = [];
355+
for (const parameterName of operationSettings.parameterNamesToIgnore) {
356+
if (!validParameterNames.has(parameterName)) {
357+
invalidParameterNames.push(parameterName);
358+
}
359+
}
360+
361+
// Report all invalid parameters in a single message
362+
if (invalidParameterNames.length > 0) {
363+
terminal.writeErrorLine(
364+
`The project "${project.packageName}" has a ` +
365+
`"${RUSH_PROJECT_CONFIGURATION_FILE.projectRelativeFilePath}" configuration that specifies ` +
366+
`invalid parameter(s) in "parameterNamesToIgnore" for operation "${operationName}": ` +
367+
`${invalidParameterNames.join(', ')}. ` +
368+
`Valid parameters for this operation are: ${Array.from(validParameterNames).sort().join(', ') || '(none)'}.`
369+
);
370+
hasErrors = true;
371+
}
372+
}
344373
}
345374
}
346375

libraries/rush-lib/src/api/test/RushProjectConfiguration.test.ts

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// See LICENSE in the project root for license information.
33

44
import { StringBufferTerminalProvider, Terminal } from '@rushstack/terminal';
5+
import type { CommandLineParameter } from '@rushstack/ts-command-line';
56

67
import type { IPhase } from '../CommandLineConfiguration';
78
import type { RushConfigurationProject } from '../RushConfigurationProject';
@@ -57,7 +58,36 @@ function validateConfiguration(rushProjectConfiguration: RushProjectConfiguratio
5758
try {
5859
rushProjectConfiguration.validatePhaseConfiguration(
5960
Array.from(rushProjectConfiguration.operationSettingsByOperationName.keys()).map(
60-
(phaseName) => ({ name: phaseName }) as IPhase
61+
(phaseName) => ({ name: phaseName, associatedParameters: new Set() }) as IPhase
62+
),
63+
terminal
64+
);
65+
} finally {
66+
expect(terminalProvider.getOutput()).toMatchSnapshot('validation: terminal output');
67+
expect(terminalProvider.getErrorOutput()).toMatchSnapshot('validation: terminal error');
68+
expect(terminalProvider.getWarningOutput()).toMatchSnapshot('validation: terminal warning');
69+
expect(terminalProvider.getVerboseOutput()).toMatchSnapshot('validation: terminal verbose');
70+
}
71+
}
72+
}
73+
74+
function validateConfigurationWithParameters(
75+
rushProjectConfiguration: RushProjectConfiguration | undefined,
76+
parameterNames: string[]
77+
): void {
78+
const terminalProvider: StringBufferTerminalProvider = new StringBufferTerminalProvider();
79+
const terminal: Terminal = new Terminal(terminalProvider);
80+
81+
if (rushProjectConfiguration) {
82+
try {
83+
// Create mock parameters with the specified names
84+
const mockParameters = new Set<CommandLineParameter>(
85+
parameterNames.map((name) => ({ longName: name }) as CommandLineParameter)
86+
);
87+
88+
rushProjectConfiguration.validatePhaseConfiguration(
89+
Array.from(rushProjectConfiguration.operationSettingsByOperationName.keys(),
90+
(phaseName) => ({ name: phaseName, associatedParameters: mockParameters }) as IPhase
6191
),
6292
terminal
6393
);
@@ -100,6 +130,33 @@ describe(RushProjectConfiguration.name, () => {
100130

101131
expect(() => validateConfiguration(rushProjectConfiguration)).toThrowError();
102132
});
133+
134+
it('validates that parameters in parameterNamesToIgnore exist for the operation', async () => {
135+
const rushProjectConfiguration: RushProjectConfiguration | undefined =
136+
await loadProjectConfigurationAsync('test-project-e');
137+
138+
expect(() => validateConfiguration(rushProjectConfiguration)).toThrowError();
139+
});
140+
141+
it('validates nonexistent parameters when operation has valid parameters', async () => {
142+
const rushProjectConfiguration: RushProjectConfiguration | undefined =
143+
await loadProjectConfigurationAsync('test-project-f');
144+
145+
// Provide some valid parameters for the operation
146+
expect(() =>
147+
validateConfigurationWithParameters(rushProjectConfiguration, ['--production', '--verbose'])
148+
).toThrowError();
149+
});
150+
151+
it('validates mix of existent and nonexistent parameters', async () => {
152+
const rushProjectConfiguration: RushProjectConfiguration | undefined =
153+
await loadProjectConfigurationAsync('test-project-g');
154+
155+
// Provide some valid parameters, test-project-g references both valid and invalid ones
156+
expect(() =>
157+
validateConfigurationWithParameters(rushProjectConfiguration, ['--production', '--verbose'])
158+
).toThrowError();
159+
});
103160
});
104161

105162
describe(RushProjectConfiguration.prototype.getCacheDisabledReason.name, () => {

libraries/rush-lib/src/api/test/__snapshots__/RushProjectConfiguration.test.ts.snap

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,27 @@ exports[`RushProjectConfiguration operationSettingsByOperationName loads a rush-
7171
exports[`RushProjectConfiguration operationSettingsByOperationName loads a rush-project.json config that extends another config file: validation: terminal warning 1`] = `""`;
7272

7373
exports[`RushProjectConfiguration operationSettingsByOperationName throws an error when loading a rush-project.json config that lists an operation twice 1`] = `"The operation \\"_phase:a\\" occurs multiple times in the \\"operationSettings\\" array in \\"<testFolder>/config/rush-project.json\\"."`;
74+
75+
exports[`RushProjectConfiguration operationSettingsByOperationName validates mix of existent and nonexistent parameters: validation: terminal error 1`] = `"The project \\"test-project-g\\" has a \\"config/rush-project.json\\" configuration that specifies invalid parameter(s) in \\"parameterNamesToIgnore\\" for operation \\"_phase:build\\": --nonexistent-param. Valid parameters for this operation are: --production, --verbose.[n]"`;
76+
77+
exports[`RushProjectConfiguration operationSettingsByOperationName validates mix of existent and nonexistent parameters: validation: terminal output 1`] = `""`;
78+
79+
exports[`RushProjectConfiguration operationSettingsByOperationName validates mix of existent and nonexistent parameters: validation: terminal verbose 1`] = `""`;
80+
81+
exports[`RushProjectConfiguration operationSettingsByOperationName validates mix of existent and nonexistent parameters: validation: terminal warning 1`] = `""`;
82+
83+
exports[`RushProjectConfiguration operationSettingsByOperationName validates nonexistent parameters when operation has valid parameters: validation: terminal error 1`] = `"The project \\"test-project-f\\" has a \\"config/rush-project.json\\" configuration that specifies invalid parameter(s) in \\"parameterNamesToIgnore\\" for operation \\"_phase:build\\": --nonexistent-param, --another-nonexistent. Valid parameters for this operation are: --production, --verbose.[n]"`;
84+
85+
exports[`RushProjectConfiguration operationSettingsByOperationName validates nonexistent parameters when operation has valid parameters: validation: terminal output 1`] = `""`;
86+
87+
exports[`RushProjectConfiguration operationSettingsByOperationName validates nonexistent parameters when operation has valid parameters: validation: terminal verbose 1`] = `""`;
88+
89+
exports[`RushProjectConfiguration operationSettingsByOperationName validates nonexistent parameters when operation has valid parameters: validation: terminal warning 1`] = `""`;
90+
91+
exports[`RushProjectConfiguration operationSettingsByOperationName validates that parameters in parameterNamesToIgnore exist for the operation: validation: terminal error 1`] = `"The project \\"test-project-e\\" has a \\"config/rush-project.json\\" configuration that specifies invalid parameter(s) in \\"parameterNamesToIgnore\\" for operation \\"_phase:build\\": --invalid-parameter, --another-invalid, -malformed-parameter. Valid parameters for this operation are: (none).[n]"`;
92+
93+
exports[`RushProjectConfiguration operationSettingsByOperationName validates that parameters in parameterNamesToIgnore exist for the operation: validation: terminal output 1`] = `""`;
94+
95+
exports[`RushProjectConfiguration operationSettingsByOperationName validates that parameters in parameterNamesToIgnore exist for the operation: validation: terminal verbose 1`] = `""`;
96+
97+
exports[`RushProjectConfiguration operationSettingsByOperationName validates that parameters in parameterNamesToIgnore exist for the operation: validation: terminal warning 1`] = `""`;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"operationSettings": [
3+
{
4+
"operationName": "_phase:build",
5+
"outputFolderNames": ["lib"],
6+
"parameterNamesToIgnore": ["--invalid-parameter", "--another-invalid", "-malformed-parameter"]
7+
}
8+
]
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"operationSettings": [
3+
{
4+
"operationName": "_phase:build",
5+
"outputFolderNames": ["lib"],
6+
"parameterNamesToIgnore": ["--nonexistent-param", "--another-nonexistent"]
7+
}
8+
]
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"operationSettings": [
3+
{
4+
"operationName": "_phase:build",
5+
"outputFolderNames": ["lib"],
6+
"parameterNamesToIgnore": ["--production", "--nonexistent-param", "--verbose"]
7+
}
8+
]
9+
}

0 commit comments

Comments
 (0)