6
6
*/
7
7
8
8
import { flags , FlagsConfig , SfdxCommand } from '@salesforce/command' ;
9
- import { Messages , SfdxPropertyKeys } from '@salesforce/core' ;
9
+ import { Messages , OrgConfigProperties } from '@salesforce/core' ;
10
+ import { CodeCoverage , PackageVersion , PackageVersionReportResult , PackagingSObjects } from '@salesforce/packaging' ;
11
+ import * as pkgUtils from '@salesforce/packaging' ;
12
+ import * as chalk from 'chalk' ;
10
13
11
14
Messages . importMessagesDirectory ( __dirname ) ;
12
15
const messages = Messages . loadMessages ( '@salesforce/plugin-packaging' , 'package_version_report' ) ;
13
-
16
+ const pvlMessages = Messages . loadMessages ( '@salesforce/plugin-packaging' , 'package_version_list' ) ;
17
+ const plMessages = Messages . loadMessages ( '@salesforce/plugin-packaging' , 'package_list' ) ;
18
+ type PackageVersionReportResultModified = Omit <
19
+ PackageVersionReportResult ,
20
+ 'CodeCoverage' | 'HasPassedCodeCoverageCheck' | 'Package2' | 'HasMetadataRemoved'
21
+ > & {
22
+ CodeCoverage : CodeCoverage | string ;
23
+ HasPassedCodeCoverageCheck : boolean | string ;
24
+ Package2 : Partial < Omit < PackagingSObjects . Package2 , 'IsOrgDependent' > & { IsOrgDependent : boolean | string } > ;
25
+ HasMetadataRemoved : boolean | string ;
26
+ } ;
14
27
export class PackageVersionReportCommand extends SfdxCommand {
15
28
public static readonly description = messages . getMessage ( 'cliDescription' ) ;
16
29
public static readonly longDescription = messages . getMessage ( 'cliLongDescription' ) ;
17
30
public static readonly help = messages . getMessage ( 'help' ) ;
18
- public static readonly orgType = SfdxPropertyKeys . DEFAULT_DEV_HUB_USERNAME ;
31
+ public static readonly orgType = OrgConfigProperties . TARGET_DEV_HUB ;
19
32
public static readonly requiresDevhubUsername = true ;
20
33
public static readonly requiresProject = true ;
21
34
public static readonly flagsConfig : FlagsConfig = {
@@ -30,9 +43,199 @@ export class PackageVersionReportCommand extends SfdxCommand {
30
43
longDescription : messages . getMessage ( 'verboseLongDescription' ) ,
31
44
} ) ,
32
45
} ;
46
+ protected haveCodeCoverageData = false ;
47
+
48
+ public async run ( ) : Promise < PackageVersionReportResultModified > {
49
+ const packageVersion = new PackageVersion ( { connection : this . hubOrg . getConnection ( ) , project : this . project } ) ;
50
+ const results = await packageVersion . report ( this . flags . package , this . flags . verbose ) ;
51
+ const massagedResults = await this . massageResultsForDisplay ( results ) ;
52
+ this . display ( massagedResults ) ;
53
+ return massagedResults ;
54
+ }
55
+
56
+ private display ( record : PackageVersionReportResultModified ) : void {
57
+ if ( this . flags . json ) {
58
+ return ;
59
+ }
60
+
61
+ let dependencies : string = null ;
62
+
63
+ // collect the Dependency 04ts into a comma-separated list for non-json output
64
+ if ( this . flags . verbose && record . SubscriberPackageVersion . Dependencies != null ) {
65
+ dependencies = record . SubscriberPackageVersion . Dependencies . ids
66
+ . map ( ( d ) => d . subscriberPackageVersionId )
67
+ . join ( ', ' ) ;
68
+ }
69
+
70
+ // transform the results into a table
71
+ const displayRecords = [
72
+ {
73
+ key : pvlMessages . getMessage ( 'name' ) ,
74
+ value : record . Name ,
75
+ } ,
76
+ {
77
+ key : pvlMessages . getMessage ( 'subscriberPackageVersionId' ) ,
78
+ value : record . SubscriberPackageVersionId ,
79
+ } ,
80
+ { key : 'Id' , value : record . Id } ,
81
+ {
82
+ key : pvlMessages . getMessage ( 'packageId' ) ,
83
+ value : record . Package2Id ,
84
+ } ,
85
+ {
86
+ key : pvlMessages . getMessage ( 'version' ) ,
87
+ value : record . Version ,
88
+ } ,
89
+ {
90
+ key : pvlMessages . getMessage ( 'description' ) ,
91
+ value : record . Description === null ? 'null' : record . Description ,
92
+ } ,
93
+ {
94
+ key : pvlMessages . getMessage ( 'packageBranch' ) ,
95
+ value : record . Branch === null ? 'null' : record . Branch ,
96
+ } ,
97
+ {
98
+ key : pvlMessages . getMessage ( 'packageTag' ) ,
99
+ value : record . Tag === null ? 'null' : record . Tag ,
100
+ } ,
101
+ { key : messages . getMessage ( 'isReleased' ) , value : record . IsReleased . toString ( ) } ,
102
+ {
103
+ key : pvlMessages . getMessage ( 'validationSkipped' ) ,
104
+ value : record . ValidationSkipped ,
105
+ } ,
106
+ { key : messages . getMessage ( 'ancestorId' ) , value : record . AncestorId } ,
107
+ { key : messages . getMessage ( 'ancestorVersion' ) , value : record . AncestorVersion } ,
108
+ {
109
+ key : pvlMessages . getMessage ( 'codeCoverage' ) ,
110
+ value :
111
+ record . CodeCoverage === null
112
+ ? ' '
113
+ : record . CodeCoverage [ 'apexCodeCoveragePercentage' ] !== undefined
114
+ ? `${ record . CodeCoverage [ 'apexCodeCoveragePercentage' ] as number } %`
115
+ : 'N/A' , // N/A
116
+ } ,
117
+ {
118
+ key : pvlMessages . getMessage ( 'hasPassedCodeCoverageCheck' ) ,
119
+ value : record . HasPassedCodeCoverageCheck ,
120
+ } ,
121
+ {
122
+ key : pvlMessages . getMessage ( 'convertedFromVersionId' ) ,
123
+ value : record . ConvertedFromVersionId === null ? ' ' : record . ConvertedFromVersionId ,
124
+ } ,
125
+ {
126
+ key : plMessages . getMessage ( 'isOrgDependent' ) ,
127
+ value : record . Package2 . IsOrgDependent ,
128
+ } ,
129
+ {
130
+ key : pvlMessages . getMessage ( 'releaseVersion' ) ,
131
+ value : record . ReleaseVersion === null ? '' : record . ReleaseVersion . toFixed ( 1 ) ,
132
+ } ,
133
+ {
134
+ key : pvlMessages . getMessage ( 'buildDurationInSeconds' ) ,
135
+ value : record . BuildDurationInSeconds === null ? '' : record . BuildDurationInSeconds ,
136
+ } ,
137
+ {
138
+ key : pvlMessages . getMessage ( 'hasMetadataRemoved' ) ,
139
+ value : record . HasMetadataRemoved ,
140
+ } ,
141
+ {
142
+ key : messages . getMessage ( 'dependencies' ) ,
143
+ value : this . flags . verbose && dependencies != null ? dependencies : ' ' ,
144
+ } ,
145
+ {
146
+ key : plMessages . getMessage ( 'createdBy' ) ,
147
+ value : record . CreatedById ,
148
+ } ,
149
+ ] ;
150
+ const maximumNumClasses = 15 ; // Number of least code covered classes displayed on the cli output for better UX.
151
+ let codeCovStr = '' ; // String to display when code coverage data is empty or null
152
+ let displayCoverageRecords = [ ] ;
153
+ // collect the code coverage data into an array of key value records for non-json output
154
+ if ( this . flags . verbose ) {
155
+ const coverageData = record . CodeCoveragePercentages ?. codeCovPercentages ;
156
+ if ( ! coverageData ) {
157
+ codeCovStr = 'N/A' ; // Code coverage isn't calculated as part of version create command
158
+ } else if ( ! coverageData . length ) {
159
+ // Calculated code coverage data is too big to fit into a DB field. Retrieve it from the packageZip
160
+ codeCovStr =
161
+ 'The code coverage details are too large to display. To request code coverage details for this package version, log a case in the Salesforce Partner Community.' ;
162
+ } else {
163
+ displayCoverageRecords = coverageData . slice ( 0 , maximumNumClasses ) . map ( ( coverageDatum ) => {
164
+ return {
165
+ key : coverageDatum . className ,
166
+ value : `${ coverageDatum . codeCoveragePercentage } %` ,
167
+ } ;
168
+ } ) ;
169
+ this . haveCodeCoverageData = displayCoverageRecords . length > 0 ;
170
+ }
171
+ }
172
+
173
+ // Always append code coverage column label ar the end
174
+ displayRecords . push ( {
175
+ key : messages . getMessage ( 'codeCoveragePercentages' ) ,
176
+ value : this . haveCodeCoverageData === true ? '...' : codeCovStr ,
177
+ } ) ;
178
+ if ( ! this . flags . verbose ) {
179
+ displayRecords . splice ( displayRecords . map ( ( e ) => e . key ) . indexOf ( 'Id' ) , 1 ) ;
180
+ displayRecords . splice (
181
+ displayRecords . map ( ( e ) => e . key ) . indexOf ( pvlMessages . getMessage ( 'convertedFromVersionId' ) ) ,
182
+ 1
183
+ ) ;
184
+ displayRecords . splice ( displayRecords . map ( ( e ) => e . key ) . indexOf ( messages . getMessage ( 'dependencies' ) ) , 1 ) ;
185
+ displayRecords . splice (
186
+ displayRecords . map ( ( e ) => e . key ) . indexOf ( messages . getMessage ( 'codeCoveragePercentages' ) ) ,
187
+ 1
188
+ ) ;
189
+ displayCoverageRecords . splice ( 0 , displayCoverageRecords . length ) ;
190
+ }
191
+ this . ux . styledHeader ( chalk . blue ( 'Package Version' ) ) ;
192
+ this . ux . table ( displayRecords , { key : { header : 'Name' } , value : { header : 'Value' } } ) ;
193
+ if ( displayCoverageRecords . length > 0 ) {
194
+ this . ux . table ( displayCoverageRecords , { key : { header : 'Class Name' } , value : { header : 'Code Coverage' } } ) ;
195
+ }
196
+ }
197
+
198
+ private async massageResultsForDisplay (
199
+ results : PackageVersionReportResult
200
+ ) : Promise < PackageVersionReportResultModified > {
201
+ const record = results as PackageVersionReportResultModified ;
202
+ record . Version = [ record . MajorVersion , record . MinorVersion , record . PatchVersion , record . BuildNumber ] . join ( '.' ) ;
203
+
204
+ let ancestorVersion : string = null ;
205
+ const containerOptions = await pkgUtils . getContainerOptions ( [ record . Package2Id ] , this . hubOrg . getConnection ( ) ) ;
206
+ const packageType = containerOptions . get ( record . Package2Id ) ;
207
+ if ( record . AncestorId ) {
208
+ // lookup AncestorVersion value
209
+ const ancestorVersionMap = await pkgUtils . getPackageVersionStrings (
210
+ [ record . AncestorId ] ,
211
+ this . hubOrg . getConnection ( )
212
+ ) ;
213
+ ancestorVersion = ancestorVersionMap . get ( record . AncestorId ) ;
214
+ } else {
215
+ // otherwise display 'N/A' if package is Unlocked Packages
216
+ if ( packageType !== 'Managed' ) {
217
+ ancestorVersion = 'N/A' ;
218
+ record . AncestorId = 'N/A' ;
219
+ }
220
+ }
221
+
222
+ record . CodeCoverage =
223
+ record . Package2 . IsOrgDependent === true || record . ValidationSkipped === true ? 'N/A' : record . CodeCoverage ;
224
+
225
+ record . HasPassedCodeCoverageCheck =
226
+ record . Package2 . IsOrgDependent === true || record . ValidationSkipped === true
227
+ ? 'N/A'
228
+ : record . HasPassedCodeCoverageCheck ;
229
+
230
+ record . Package2 . IsOrgDependent =
231
+ packageType === 'Managed' ? 'N/A' : record . Package2 . IsOrgDependent === true ? 'Yes' : 'No' ;
232
+
233
+ // set HasMetadataRemoved to N/A for Unlocked, and No when value is false or absent (pre-230)
234
+ record . HasMetadataRemoved = packageType !== 'Managed' ? 'N/A' : record . HasMetadataRemoved === true ? 'Yes' : 'No' ;
235
+
236
+ // add AncestorVersion to the json record
237
+ record . AncestorVersion = ancestorVersion ;
33
238
34
- public async run ( ) : Promise < unknown > {
35
- process . exitCode = 1 ;
36
- return Promise . resolve ( 'Not yet implemented' ) ;
239
+ return record ;
37
240
}
38
241
}
0 commit comments