1+ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
2+ // See LICENSE in the project root for license information.
3+
4+ import * as fsx from 'fs-extra' ;
5+ import * as os from 'os' ;
6+ import * as colors from 'colors' ;
7+
8+ import {
9+ Event
10+ } from '../../index' ;
11+
12+ import {
13+ CustomOption ,
14+ ICustomEnumValue
15+ } from '../../data/CommandLineConfiguration' ;
16+
17+ import {
18+ CommandLineFlagParameter ,
19+ CommandLineIntegerParameter ,
20+ CommandLineStringListParameter ,
21+ CommandLineOptionParameter ,
22+ ICommandLineActionOptions
23+ } from '@microsoft/ts-command-line' ;
24+
25+ import RushCommandLineParser from './RushCommandLineParser' ;
26+ import { BaseRushAction } from './BaseRushAction' ;
27+ import { TaskSelector } from '../utilities/TaskSelector' ;
28+ import { Stopwatch } from '../../utilities/Stopwatch' ;
29+
30+ interface ICustomOptionInstance {
31+ optionDefinition : CustomOption ;
32+ parameterValue ?: CommandLineFlagParameter | CommandLineOptionParameter ;
33+ }
34+
35+ export class CustomRushAction extends BaseRushAction {
36+ private customOptions : Map < string , ICustomOptionInstance > = new Map < string , ICustomOptionInstance > ( ) ;
37+
38+ private _fromFlag : CommandLineStringListParameter ;
39+ private _toFlag : CommandLineStringListParameter ;
40+ private _verboseParameter : CommandLineFlagParameter ;
41+ private _parallelismParameter : CommandLineIntegerParameter ;
42+
43+ constructor ( private _parser : RushCommandLineParser ,
44+ options : ICommandLineActionOptions ,
45+ private _parallelized : boolean = true ) {
46+
47+ super ( options ) ;
48+ }
49+
50+ /**
51+ * Registers a custom option to a task. This custom option is then registered during onDefineParameters()
52+ * @param longName the long name of the option, e.g. "--verbose"
53+ * @param option the Custom Option definition
54+ */
55+ public addCustomOption ( longName : string , option : CustomOption ) : void {
56+ if ( this . customOptions . get ( longName ) ) {
57+ throw new Error ( `Cannot define two custom options with the same name: "${ longName } "` ) ;
58+ }
59+ this . customOptions . set ( longName , {
60+ optionDefinition : option
61+ } ) ;
62+ }
63+
64+ public run ( ) : void {
65+ if ( ! fsx . existsSync ( this . rushConfiguration . rushLinkJsonFilename ) ) {
66+ throw new Error ( `File not found: ${ this . rushConfiguration . rushLinkJsonFilename } ` +
67+ `${ os . EOL } Did you run "rush link"?` ) ;
68+ }
69+ this . eventHooksManager . handle ( Event . preRushBuild ) ;
70+
71+ const stopwatch : Stopwatch = Stopwatch . start ( ) ;
72+
73+ const isQuietMode : boolean = ! ( this . _verboseParameter . value ) ;
74+ const parallelism : number = ( this . _parallelized ? this . _parallelismParameter . value : 1 ) ;
75+
76+ // collect all custom flags here
77+ const customFlags : string [ ] = [ ] ;
78+ this . customOptions . forEach ( ( customOption : ICustomOptionInstance , longName : string ) => {
79+ if ( customOption . parameterValue ! . value ) {
80+ if ( customOption . optionDefinition . optionType === 'flag' ) {
81+ customFlags . push ( longName ) ;
82+ } else if ( customOption . optionDefinition . optionType === 'enum' ) {
83+ customFlags . push ( `${ longName } =${ customOption . parameterValue ! . value } ` ) ;
84+ }
85+ }
86+ } ) ;
87+
88+ const tasks : TaskSelector = new TaskSelector (
89+ {
90+ rushConfiguration : this . _parser . rushConfig ,
91+ toFlags : this . _toFlag . value ,
92+ fromFlags : this . _fromFlag . value ,
93+ commandToRun : this . options . actionVerb ,
94+ customFlags,
95+ isQuietMode,
96+ parallelism,
97+ isIncrementalBuildAllowed : this . options . actionVerb === 'build'
98+ }
99+ ) ;
100+
101+ tasks . execute ( ) . then (
102+ ( ) => {
103+ stopwatch . stop ( ) ;
104+ console . log ( colors . green ( `rush ${ this . options . actionVerb } (${ stopwatch . toString ( ) } )` ) ) ;
105+ this . _collectTelemetry ( stopwatch , true ) ;
106+ this . _parser . flushTelemetry ( ) ;
107+ this . eventHooksManager . handle ( Event . postRushBuild , this . _parser . isDebug ) ;
108+ } ,
109+ ( ) => {
110+ stopwatch . stop ( ) ;
111+ console . log ( colors . red ( `rush ${ this . options . actionVerb } - Errors! (${ stopwatch . toString ( ) } )` ) ) ;
112+ this . _collectTelemetry ( stopwatch , false ) ;
113+ this . _parser . flushTelemetry ( ) ;
114+ this . eventHooksManager . handle ( Event . postRushBuild , this . _parser . isDebug ) ;
115+ this . _parser . exitWithError ( ) ;
116+ } ) ;
117+ }
118+
119+ protected onDefineParameters ( ) : void {
120+ this . _parallelismParameter = this . defineIntegerParameter ( {
121+ parameterLongName : '--parallelism' ,
122+ parameterShortName : '-p' ,
123+ key : 'COUNT' ,
124+ description : 'Change limit the number of simultaneous builds. This value defaults to the number of CPU cores'
125+ } ) ;
126+ this . _toFlag = this . defineStringListParameter ( {
127+ parameterLongName : '--to' ,
128+ parameterShortName : '-t' ,
129+ key : 'PROJECT1' ,
130+ description : 'Build the specified project and all of its dependencies'
131+ } ) ;
132+ this . _fromFlag = this . defineStringListParameter ( {
133+ parameterLongName : '--from' ,
134+ parameterShortName : '-f' ,
135+ key : 'PROJECT2' ,
136+ description : 'Build all projects that directly or indirectly depend on the specified project'
137+ } ) ;
138+ this . _verboseParameter = this . defineFlagParameter ( {
139+ parameterLongName : '--verbose' ,
140+ parameterShortName : '-v' ,
141+ description : 'Display the logs during the build, rather than just displaying the build status summary'
142+ } ) ;
143+
144+ // @TODO we should throw if they are trying to overwrite built in flags
145+
146+ this . customOptions . forEach ( ( customOption : ICustomOptionInstance , longName : string ) => {
147+ if ( customOption . optionDefinition . optionType === 'flag' ) {
148+ customOption . parameterValue = this . defineFlagParameter ( {
149+ parameterShortName : customOption . optionDefinition . shortName ,
150+ parameterLongName : longName ,
151+ description : customOption . optionDefinition . description
152+ } ) ;
153+ } else if ( customOption . optionDefinition . optionType === 'enum' ) {
154+ customOption . parameterValue = this . defineOptionParameter ( {
155+ parameterShortName : customOption . optionDefinition . shortName ,
156+ parameterLongName : longName ,
157+ description : customOption . optionDefinition . description ,
158+ defaultValue : customOption . optionDefinition . defaultValue ,
159+ options : customOption . optionDefinition . enumValues . map ( ( enumValue : ICustomEnumValue ) => {
160+ return enumValue . name ;
161+ } )
162+ } ) ;
163+ }
164+ } ) ;
165+ }
166+
167+ private _collectTelemetry ( stopwatch : Stopwatch , success : boolean ) : void {
168+ const extraData : { [ key : string ] : string } = {
169+ command_to : ( ! ! this . _toFlag . value ) . toString ( ) ,
170+ command_from : ( ! ! this . _fromFlag . value ) . toString ( )
171+ } ;
172+
173+ this . customOptions . forEach ( ( customOption : ICustomOptionInstance , longName : string ) => {
174+ if ( customOption . parameterValue ! . value ) {
175+ extraData [ `${ this . options . actionVerb } _${ longName } ` ] =
176+ customOption . parameterValue ! . value . toString ( ) ;
177+ }
178+ } ) ;
179+
180+ this . _parser . telemetry . log ( {
181+ name : this . options . actionVerb ,
182+ duration : stopwatch . duration ,
183+ result : success ? 'Succeeded' : 'Failed' ,
184+ extraData
185+ } ) ;
186+ }
187+ }
0 commit comments