@@ -9,15 +9,19 @@ import * as os from 'os';
99import * as path from 'path' ;
1010import { flags , FlagsConfig } from '@salesforce/command' ;
1111import { Lifecycle , Messages } from '@salesforce/core' ;
12- import { DeployResult } from '@salesforce/source-deploy-retrieve' ;
12+ import { DeployResult , MetadataApiDeploy } from '@salesforce/source-deploy-retrieve' ;
1313import { Duration } from '@salesforce/kit' ;
1414import { asString , asArray , getBoolean , JsonCollection } from '@salesforce/ts-types' ;
1515import * as chalk from 'chalk' ;
16+ import cli from 'cli-ux' ;
17+ import { env } from '@salesforce/kit' ;
1618import { SourceCommand } from '../../../sourceCommand' ;
1719
1820Messages . importMessagesDirectory ( __dirname ) ;
1921const messages = Messages . loadMessages ( '@salesforce/plugin-source' , 'deploy' ) ;
2022
23+ type TestLevel = 'NoTestRun' | 'RunSpecifiedTests' | 'RunLocalTests' | 'RunAllTestsInOrg' ;
24+
2125export class Deploy extends SourceCommand {
2226 public static readonly description = messages . getMessage ( 'description' ) ;
2327 public static readonly examples = messages . getMessage ( 'examples' ) . split ( os . EOL ) ;
@@ -111,19 +115,24 @@ export class Deploy extends SourceCommand {
111115
112116 await hookEmitter . emit ( 'predeploy' , { packageXmlPath : cs . getPackageXml ( ) } ) ;
113117
114- const results = await cs
115- . deploy ( {
116- usernameOrConnection : this . org . getUsername ( ) ,
117- apiOptions : {
118- ignoreWarnings : getBoolean ( this . flags , 'ignorewarnings' , false ) ,
119- rollbackOnError : ! getBoolean ( this . flags , 'ignoreerrors' , false ) ,
120- checkOnly : getBoolean ( this . flags , 'checkonly' , false ) ,
121- runTests : asArray < string > ( this . flags . runtests ) ,
122- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
123- testLevel : this . flags . testlevel ,
124- } ,
125- } )
126- . start ( ) ;
118+ const deploy = cs . deploy ( {
119+ usernameOrConnection : this . org . getUsername ( ) ,
120+ apiOptions : {
121+ ignoreWarnings : getBoolean ( this . flags , 'ignorewarnings' , false ) ,
122+ rollbackOnError : ! getBoolean ( this . flags , 'ignoreerrors' , false ) ,
123+ checkOnly : getBoolean ( this . flags , 'checkonly' , false ) ,
124+ runTests : asArray < string > ( this . flags . runtests ) ,
125+ testLevel : this . flags . testlevel as TestLevel ,
126+ } ,
127+ } ) ;
128+
129+ // if SFDX_USE_PROGRESS_BAR is true and no --json flag use progress bar, if not, skip
130+ if ( env . getBoolean ( 'SFDX_USE_PROGRESS_BAR' , true ) && ! this . flags . json ) {
131+ this . progress ( deploy ) ;
132+ }
133+
134+ const results = await deploy . start ( ) ;
135+
127136 await hookEmitter . emit ( 'postdeploy' , results ) ;
128137
129138 // skip a lot of steps that would do nothing
@@ -134,6 +143,51 @@ export class Deploy extends SourceCommand {
134143 return results ;
135144 }
136145
146+ private progress ( deploy : MetadataApiDeploy ) : void {
147+ // cli.progress doesn't have typings
148+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
149+ const progressBar = cli . progress ( {
150+ format : 'SOURCE PROGRESS | {bar} | {value}/{total} Components' ,
151+ barCompleteChar : '\u2588' ,
152+ barIncompleteChar : '\u2591' ,
153+ linewrap : true ,
154+ } ) ;
155+ let printOnce = true ;
156+ deploy . onUpdate ( ( data ) => {
157+ // the numCompTot. isn't computed right away, wait to start until we know how many we have
158+ if ( data . numberComponentsTotal && printOnce ) {
159+ this . ux . log ( `Job ID | ${ data . id } ` ) ;
160+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
161+ progressBar . start ( data . numberComponentsTotal + data . numberTestsTotal ) ;
162+ printOnce = false ;
163+ }
164+
165+ // the numTestsTot. isn't computed until validated as tests by the server, update the PB once we know
166+ if ( data . numberTestsTotal ) {
167+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
168+ progressBar . setTotal ( data . numberComponentsTotal + data . numberTestsTotal ) ;
169+ }
170+
171+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-call
172+ progressBar . update ( data . numberComponentsDeployed + data . numberTestsCompleted ) ;
173+ } ) ;
174+
175+ deploy . onFinish ( ( ) => {
176+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
177+ progressBar . stop ( ) ;
178+ } ) ;
179+
180+ deploy . onCancel ( ( ) => {
181+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
182+ progressBar . stop ( ) ;
183+ } ) ;
184+
185+ deploy . onError ( ( ) => {
186+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
187+ progressBar . stop ( ) ;
188+ } ) ;
189+ }
190+
137191 private printComponentFailures ( result : DeployResult ) : void {
138192 if ( result . response . status === 'Failed' && result . components ) {
139193 // sort by filename then fullname
0 commit comments