55 * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
66 */
77
8- import { Messages , Org } from '@salesforce/core' ;
9- import { Duration } from '@salesforce/kit' ;
8+ import { Messages , Org , SfProject } from '@salesforce/core' ;
109import { SfCommand , Flags } from '@salesforce/sf-plugins-core' ;
11- import { DeployResult , MetadataApiDeployStatus } from '@salesforce/source-deploy-retrieve' ;
12- import { buildComponentSet } from '../../../utils/deploy' ;
10+ import { ComponentSet , DeployResult , MetadataApiDeploy } from '@salesforce/source-deploy-retrieve' ;
11+ import { buildComponentSet , DeployOptions } from '../../../utils/deploy' ;
12+ import { DeployProgress } from '../../../utils/progressBar' ;
1313import { DeployCache } from '../../../utils/deployCache' ;
1414import { DeployReportResultFormatter } from '../../../formatters/deployReportResultFormatter' ;
1515import { DeployResultJson } from '../../../utils/types' ;
1616import { coverageFormattersFlag } from '../../../utils/flags' ;
1717
1818Messages . importMessagesDirectory ( __dirname ) ;
1919const messages = Messages . loadMessages ( '@salesforce/plugin-deploy-retrieve' , 'deploy.metadata.report' ) ;
20+ const deployMessages = Messages . loadMessages ( '@salesforce/plugin-deploy-retrieve' , 'deploy.metadata' ) ;
2021const testFlags = 'Test' ;
2122
2223export default class DeployMetadataReport extends SfCommand < DeployResultJson > {
@@ -27,6 +28,11 @@ export default class DeployMetadataReport extends SfCommand<DeployResultJson> {
2728 public static readonly deprecateAliases = true ;
2829
2930 public static readonly flags = {
31+ 'target-org' : Flags . optionalOrg ( {
32+ char : 'o' ,
33+ description : deployMessages . getMessage ( 'flags.target-org.description' ) ,
34+ summary : deployMessages . getMessage ( 'flags.target-org.summary' ) ,
35+ } ) ,
3036 'job-id' : Flags . salesforceId ( {
3137 char : 'i' ,
3238 startsWith : '0Af' ,
@@ -51,23 +57,77 @@ export default class DeployMetadataReport extends SfCommand<DeployResultJson> {
5157 summary : messages . getMessage ( 'flags.results-dir.summary' ) ,
5258 helpGroup : testFlags ,
5359 } ) ,
60+ // we want to allow undefined for a simple check deploy status
61+ // eslint-disable-next-line sf-plugin/flag-min-max-default
62+ wait : Flags . duration ( {
63+ char : 'w' ,
64+ summary : deployMessages . getMessage ( 'flags.wait.summary' ) ,
65+ description : deployMessages . getMessage ( 'flags.wait.description' ) ,
66+ unit : 'minutes' ,
67+ helpValue : '<minutes>' ,
68+ min : 1 ,
69+ } ) ,
5470 } ;
5571
5672 public async run ( ) : Promise < DeployResultJson > {
5773 const [ { flags } , cache ] = await Promise . all ( [ this . parse ( DeployMetadataReport ) , DeployCache . create ( ) ] ) ;
58- const jobId = cache . resolveLatest ( flags [ 'use-most-recent' ] , flags [ 'job-id' ] ) ;
74+ const jobId = cache . resolveLatest ( flags [ 'use-most-recent' ] , flags [ 'job-id' ] , false ) ;
75+
76+ const deployOpts = cache . get ( jobId ) ?? ( { } as DeployOptions & { isMdapi : boolean } ) ;
77+ const waitDuration = flags [ 'wait' ] ;
78+ const org = flags [ 'target-org' ] ?? ( await Org . create ( { aliasOrUsername : deployOpts [ 'target-org' ] } ) ) ;
5979
60- const deployOpts = cache . get ( jobId ) ;
61- const org = await Org . create ( { aliasOrUsername : deployOpts [ 'target-org' ] } ) ;
62- const [ deployStatus , componentSet ] = await Promise . all ( [
63- // we'll use whatever the org supports since we can't specify the org
80+ // if we're using mdapi we won't have a component set
81+ let componentSet = new ComponentSet ( ) ;
82+ if ( ! deployOpts . isMdapi ) {
83+ if ( ! cache . get ( jobId ) ) {
84+ // If the cache file isn't there, use the project package directories for the CompSet
85+ try {
86+ this . project = await SfProject . resolve ( ) ;
87+ const sourcepath = this . project . getUniquePackageDirectories ( ) . map ( ( pDir ) => pDir . fullPath ) ;
88+ componentSet = await buildComponentSet ( { 'source-dir' : sourcepath , wait : waitDuration } ) ;
89+ } catch ( err ) {
90+ // ignore the error. this was just to get improved command output.
91+ }
92+ } else {
93+ componentSet = await buildComponentSet ( { ...deployOpts , wait : waitDuration } ) ;
94+ }
95+ }
96+ const mdapiDeploy = new MetadataApiDeploy ( {
97+ // setting an API version here won't matter since we're just checking deploy status
6498 // eslint-disable-next-line sf-plugin/get-connection-with-version
65- org . getConnection ( ) . metadata . checkDeployStatus ( jobId , true ) ,
66- // if we're using mdapi, we won't have a component set
67- deployOpts . isMdapi ? undefined : buildComponentSet ( { ...deployOpts , wait : Duration . minutes ( deployOpts . wait ) } ) ,
68- ] ) ;
99+ usernameOrConnection : org . getConnection ( ) ,
100+ id : jobId ,
101+ components : componentSet ,
102+ apiOptions : {
103+ rest : deployOpts . api === 'REST' ,
104+ } ,
105+ } ) ;
106+
107+ const getDeployResult = async ( ) : Promise < DeployResult > => {
108+ const deployStatus = await mdapiDeploy . checkStatus ( ) ;
109+ return new DeployResult ( deployStatus , componentSet ) ;
110+ } ;
69111
70- const result = new DeployResult ( deployStatus as MetadataApiDeployStatus , componentSet ) ;
112+ let result : DeployResult ;
113+ if ( waitDuration ) {
114+ // poll for deploy results
115+ try {
116+ new DeployProgress ( mdapiDeploy , this . jsonEnabled ( ) ) . start ( ) ;
117+ result = await mdapiDeploy . pollStatus ( 500 , waitDuration . seconds ) ;
118+ } catch ( error ) {
119+ if ( error instanceof Error && error . message . includes ( 'The client has timed out' ) ) {
120+ this . debug ( '[project deploy report] polling timed out. Requesting status...' ) ;
121+ } else {
122+ throw error ;
123+ }
124+ } finally {
125+ result = await getDeployResult ( ) ;
126+ }
127+ } else {
128+ // check the deploy status
129+ result = await getDeployResult ( ) ;
130+ }
71131
72132 const formatter = new DeployReportResultFormatter ( result , {
73133 ...deployOpts ,
0 commit comments