44import path from 'node:path' ;
55import { JsonFile } from '@rushstack/node-core-library' ;
66import { ConsoleTerminalProvider , Terminal } from '@rushstack/terminal' ;
7+ import {
8+ CommandLineAction ,
9+ type CommandLineParameter ,
10+ type CommandLineFlagParameter ,
11+ type CommandLineStringParameter ,
12+ type CommandLineChoiceParameter ,
13+ type CommandLineStringListParameter
14+ } from '@rushstack/ts-command-line' ;
715
816import { RushConfiguration } from '../../../api/RushConfiguration' ;
9- import { CommandLineConfiguration , type IPhasedCommandConfig } from '../../../api/CommandLineConfiguration' ;
17+ import {
18+ CommandLineConfiguration ,
19+ type IPhasedCommandConfig ,
20+ type IParameterJson
21+ } from '../../../api/CommandLineConfiguration' ;
1022import type { Operation } from '../Operation' ;
11- import type { ICommandLineJson } from '../../../api/CommandLineJson' ;
23+ import type { ICommandLineJson , ParameterJson } from '../../../api/CommandLineJson' ;
1224import { PhasedOperationPlugin } from '../PhasedOperationPlugin' ;
1325import { ShellOperationRunnerPlugin } from '../ShellOperationRunnerPlugin' ;
1426import {
1527 type ICreateOperationsContext ,
1628 PhasedCommandHooks
1729} from '../../../pluginFramework/PhasedCommandHooks' ;
1830import { RushProjectConfiguration } from '../../../api/RushProjectConfiguration' ;
31+ import { RushConstants } from '../../RushConstants' ;
1932
2033interface ISerializedOperation {
2134 name : string ;
@@ -29,6 +42,106 @@ function serializeOperation(operation: Operation): ISerializedOperation {
2942 } ;
3043}
3144
45+ /**
46+ * Helper function to create CommandLineParameter instances from parameter definitions.
47+ * Extracted logic from BaseScriptAction.defineScriptParameters()
48+ */
49+ function createCommandLineParameters (
50+ action : CommandLineAction ,
51+ associatedParameters : Set < IParameterJson >
52+ ) : Map < string , CommandLineParameter > {
53+ const customParameters : Map < string , CommandLineParameter > = new Map ( ) ;
54+
55+ for ( const parameter of associatedParameters ) {
56+ let tsCommandLineParameter : CommandLineParameter | undefined ;
57+
58+ switch ( parameter . parameterKind ) {
59+ case 'flag' :
60+ tsCommandLineParameter = action . defineFlagParameter ( {
61+ parameterShortName : parameter . shortName ,
62+ parameterLongName : parameter . longName ,
63+ description : parameter . description ,
64+ required : parameter . required
65+ } ) ;
66+ break ;
67+ case 'choice' :
68+ tsCommandLineParameter = action . defineChoiceParameter ( {
69+ parameterShortName : parameter . shortName ,
70+ parameterLongName : parameter . longName ,
71+ description : parameter . description ,
72+ required : parameter . required ,
73+ alternatives : parameter . alternatives . map ( ( x ) => x . name ) ,
74+ defaultValue : parameter . defaultValue
75+ } ) ;
76+ break ;
77+ case 'string' :
78+ tsCommandLineParameter = action . defineStringParameter ( {
79+ parameterLongName : parameter . longName ,
80+ parameterShortName : parameter . shortName ,
81+ description : parameter . description ,
82+ required : parameter . required ,
83+ argumentName : parameter . argumentName
84+ } ) ;
85+ break ;
86+ case 'integer' :
87+ tsCommandLineParameter = action . defineIntegerParameter ( {
88+ parameterLongName : parameter . longName ,
89+ parameterShortName : parameter . shortName ,
90+ description : parameter . description ,
91+ required : parameter . required ,
92+ argumentName : parameter . argumentName
93+ } ) ;
94+ break ;
95+ case 'stringList' :
96+ tsCommandLineParameter = action . defineStringListParameter ( {
97+ parameterLongName : parameter . longName ,
98+ parameterShortName : parameter . shortName ,
99+ description : parameter . description ,
100+ required : parameter . required ,
101+ argumentName : parameter . argumentName
102+ } ) ;
103+ break ;
104+ case 'integerList' :
105+ tsCommandLineParameter = action . defineIntegerListParameter ( {
106+ parameterLongName : parameter . longName ,
107+ parameterShortName : parameter . shortName ,
108+ description : parameter . description ,
109+ required : parameter . required ,
110+ argumentName : parameter . argumentName
111+ } ) ;
112+ break ;
113+ case 'choiceList' :
114+ tsCommandLineParameter = action . defineChoiceListParameter ( {
115+ parameterShortName : parameter . shortName ,
116+ parameterLongName : parameter . longName ,
117+ description : parameter . description ,
118+ required : parameter . required ,
119+ alternatives : parameter . alternatives . map ( ( x ) => x . name )
120+ } ) ;
121+ break ;
122+ default :
123+ throw new Error (
124+ `${ RushConstants . commandLineFilename } defines a parameter "${
125+ ( parameter as ParameterJson ) . longName
126+ } " using an unsupported parameter kind "${ ( parameter as ParameterJson ) . parameterKind } "`
127+ ) ;
128+ }
129+
130+ customParameters . set ( parameter . longName , tsCommandLineParameter ) ;
131+ }
132+
133+ return customParameters ;
134+ }
135+
136+ /**
137+ * Test implementation of CommandLineAction for testing parameter handling
138+ */
139+ class TestCommandLineAction extends CommandLineAction {
140+ protected async onExecuteAsync ( ) : Promise < void > {
141+ // No-op for testing
142+ }
143+ }
144+
32145describe ( ShellOperationRunnerPlugin . name , ( ) => {
33146 it ( 'shellCommand "echo custom shellCommand" should be set to commandToRun' , async ( ) => {
34147 const rushJsonFile : string = path . resolve ( __dirname , `../../test/customShellCommandinBulkRepo/rush.json` ) ;
@@ -148,6 +261,58 @@ describe(ShellOperationRunnerPlugin.name, () => {
148261 terminal
149262 ) ;
150263
264+ // Create a dummy CommandLineAction to host the parameters
265+ const action : TestCommandLineAction = new TestCommandLineAction ( {
266+ actionName : 'build' ,
267+ summary : 'Test build action' ,
268+ documentation : 'Test'
269+ } ) ;
270+
271+ // Create CommandLineParameter instances from the parameter definitions
272+ const customParametersMap : Map < string , CommandLineParameter > = createCommandLineParameters (
273+ action ,
274+ buildCommand . associatedParameters
275+ ) ;
276+
277+ // Set values on the parameters to test filtering
278+ // Set --production flag
279+ const productionParam = customParametersMap . get ( '--production' ) as CommandLineFlagParameter | undefined ;
280+ if ( productionParam ) {
281+ ( productionParam as unknown as { _setValue ( value : boolean ) : void } ) . _setValue ( true ) ;
282+ }
283+
284+ // Set --verbose flag
285+ const verboseParam = customParametersMap . get ( '--verbose' ) as CommandLineFlagParameter | undefined ;
286+ if ( verboseParam ) {
287+ ( verboseParam as unknown as { _setValue ( value : boolean ) : void } ) . _setValue ( true ) ;
288+ }
289+
290+ // Set --config parameter
291+ const configParam = customParametersMap . get ( '--config' ) as CommandLineStringParameter | undefined ;
292+ if ( configParam ) {
293+ ( configParam as unknown as { _setValue ( value : string ) : void } ) . _setValue ( '/path/to/config.json' ) ;
294+ }
295+
296+ // Set --mode parameter
297+ const modeParam = customParametersMap . get ( '--mode' ) as CommandLineChoiceParameter | undefined ;
298+ if ( modeParam ) {
299+ ( modeParam as unknown as { _setValue ( value : string ) : void } ) . _setValue ( 'prod' ) ;
300+ }
301+
302+ // Set --tags parameter
303+ const tagsParam = customParametersMap . get ( '--tags' ) as CommandLineStringListParameter | undefined ;
304+ if ( tagsParam ) {
305+ ( tagsParam as unknown as { _setValue ( value : string [ ] ) : void } ) . _setValue ( [ 'tag1' , 'tag2' ] ) ;
306+ }
307+
308+ // Update the phase's associatedParameters to use our created CommandLineParameters
309+ for ( const phase of buildCommand . phases ) {
310+ phase . associatedParameters . clear ( ) ;
311+ for ( const param of customParametersMap . values ( ) ) {
312+ phase . associatedParameters . add ( param ) ;
313+ }
314+ }
315+
151316 const fakeCreateOperationsContext : Pick <
152317 ICreateOperationsContext ,
153318 | 'phaseOriginal'
@@ -164,7 +329,7 @@ describe(ShellOperationRunnerPlugin.name, () => {
164329 projectsInUnknownState : new Set ( rushConfiguration . projects ) ,
165330 projectConfigurations,
166331 rushConfiguration,
167- customParameters : new Map ( )
332+ customParameters : customParametersMap
168333 } ;
169334
170335 const hooks : PhasedCommandHooks = new PhasedCommandHooks ( ) ;
@@ -185,12 +350,21 @@ describe(ShellOperationRunnerPlugin.name, () => {
185350 const commandHashA = operationA ! . runner ! . getConfigHash ( ) ;
186351 // Should not contain --production but should contain other parameters
187352 expect ( commandHashA ) . not . toContain ( '--production' ) ;
353+ expect ( commandHashA ) . toContain ( '--verbose' ) ;
354+ expect ( commandHashA ) . toContain ( '--config' ) ;
355+ expect ( commandHashA ) . toContain ( '--mode' ) ;
356+ expect ( commandHashA ) . toContain ( '--tags' ) ;
188357
189358 // Verify that project 'b' has all parameters (no filtering)
190359 const operationB = Array . from ( operations ) . find ( ( op ) => op . name === 'b' ) ;
191360 expect ( operationB ) . toBeDefined ( ) ;
361+ const commandHashB = operationB ! . runner ! . getConfigHash ( ) ;
192362 // Should contain all parameters since no filtering is configured
193- // Note: Parameters only appear if they are provided, so we can't test for them without setting them
363+ expect ( commandHashB ) . toContain ( '--production' ) ;
364+ expect ( commandHashB ) . toContain ( '--verbose' ) ;
365+ expect ( commandHashB ) . toContain ( '--config' ) ;
366+ expect ( commandHashB ) . toContain ( '--mode' ) ;
367+ expect ( commandHashB ) . toContain ( '--tags' ) ;
194368
195369 // All projects snapshot
196370 expect ( Array . from ( operations , serializeOperation ) ) . toMatchSnapshot ( ) ;
0 commit comments