@salesforce/plugin-packaging
Version:
SF plugin that support Salesforce Packaging Platform
252 lines • 11.6 kB
JavaScript
/*
* Copyright 2025, Salesforce, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Flags, loglevel, orgApiVersionFlagWithDeprecations, SfCommand } from '@salesforce/sf-plugins-core';
import { Messages } from '@salesforce/core/messages';
import { PackageVersion, } from '@salesforce/packaging';
import chalk from 'chalk';
import { requiredHubFlag } from '../../../utils/hubFlag.js';
import { maybeGetProject } from '../../../utils/getProject.js';
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
const messages = Messages.loadMessages('@salesforce/plugin-packaging', 'package_version_report');
const pvlMessages = Messages.loadMessages('@salesforce/plugin-packaging', 'package_version_list');
const plMessages = Messages.loadMessages('@salesforce/plugin-packaging', 'package_list');
const omissions = [
'CodeCoverage',
'HasPassedCodeCoverageCheck',
'Package2',
'HasMetadataRemoved',
'DeveloperUsePkgZip',
];
export class PackageVersionReportCommand extends SfCommand {
static summary = messages.getMessage('summary');
static description = messages.getMessage('description');
static examples = messages.getMessages('examples');
static deprecateAliases = true;
static aliases = ['force:package:version:report'];
static flags = {
loglevel,
'target-dev-hub': requiredHubFlag,
'api-version': orgApiVersionFlagWithDeprecations,
package: Flags.string({
char: 'p',
summary: messages.getMessage('flags.package.summary'),
required: true,
}),
verbose: Flags.boolean({
summary: messages.getMessage('flags.verbose.summary'),
}),
};
haveCodeCoverageData = false;
async run() {
const { flags } = await this.parse(PackageVersionReportCommand);
const connection = flags['target-dev-hub'].getConnection(flags['api-version']);
const packageVersion = new PackageVersion({
connection,
project: await maybeGetProject(),
idOrAlias: flags.package,
});
const results = await packageVersion.report(flags.verbose);
const massagedResults = this.massageResultsForDisplay(results);
this.display(massagedResults, flags.verbose, connection);
return massagedResults;
}
display(record, verbose, connection) {
if (this.jsonEnabled()) {
return;
}
let dependencies;
// collect the Dependency 04ts into a comma-separated list for non-json output
if (verbose && record.SubscriberPackageVersion?.Dependencies != null) {
dependencies = record.SubscriberPackageVersion.Dependencies.ids
.map((d) => d.subscriberPackageVersionId)
.join(', ');
}
// transform the results into a table
const displayRecords = [
{
key: pvlMessages.getMessage('name'),
value: record.Name,
},
{
key: pvlMessages.getMessage('subscriberPackageVersionId'),
value: record.SubscriberPackageVersionId,
},
{ key: 'Id', value: record.Id },
{
key: pvlMessages.getMessage('package-id'),
value: record.Package2Id,
},
{
key: pvlMessages.getMessage('version'),
value: record.Version,
},
{
key: pvlMessages.getMessage('description'),
value: record.Description,
},
{
key: pvlMessages.getMessage('packageBranch'),
value: record.Branch,
},
{
key: pvlMessages.getMessage('packageTag'),
value: record.Tag,
},
{ key: messages.getMessage('isReleased'), value: `${record.IsReleased ?? '<undefined>'} ` },
{
key: pvlMessages.getMessage('validationSkipped'),
value: record.ValidationSkipped,
},
{
key: pvlMessages.getMessage('validatedAsync'),
value: record.ValidatedAsync,
},
{ key: messages.getMessage('ancestorId'), value: record.AncestorId },
{ key: messages.getMessage('ancestorVersion'), value: record.AncestorVersion },
{
key: pvlMessages.getMessage('codeCoverage'),
value: typeof record.CodeCoverage === 'string'
? record.CodeCoverage
: !record.CodeCoverage
? 'N/A'
: `${record.CodeCoverage.apexCodeCoveragePercentage.toFixed(2)}%`,
},
{
key: pvlMessages.getMessage('hasPassedCodeCoverageCheck'),
value: record.HasPassedCodeCoverageCheck,
},
{
key: pvlMessages.getMessage('convertedFromVersionId'),
value: record.ConvertedFromVersionId,
},
{
key: plMessages.getMessage('isOrgDependent'),
value: record.Package2.IsOrgDependent,
},
{
key: pvlMessages.getMessage('releaseVersion'),
value: record.ReleaseVersion === null ? '' : record.ReleaseVersion?.toFixed(1),
},
{
key: pvlMessages.getMessage('buildDurationInSeconds'),
value: record.BuildDurationInSeconds === null ? '' : record.BuildDurationInSeconds?.toFixed(1),
},
{
key: pvlMessages.getMessage('hasMetadataRemoved'),
value: record.HasMetadataRemoved,
},
{
key: messages.getMessage('dependencies'),
value: verbose && dependencies != null ? dependencies : ' ',
},
{
key: plMessages.getMessage('createdBy'),
value: record.CreatedById,
},
];
if (Number(connection.version) > 63) {
displayRecords.push({
key: '# MetadataFiles',
value: record.TotalNumberOfMetadataFiles?.toString() ?? '',
}, {
key: 'Metadata File Size',
value: (record.TotalSizeOfMetadataFiles
? `${Math.ceil(record.TotalSizeOfMetadataFiles / (1024 * 1024))} MB`
: ''),
});
}
const maximumNumClasses = 15; // Number of least code covered classes displayed on the cli output for better UX.
let codeCovStr = ''; // String to display when code coverage data is empty or null
let displayCoverageRecords = [];
// collect the code coverage data into an array of key value records for non-json output
if (verbose) {
const coverageData = record.CodeCoveragePercentages?.codeCovPercentages;
if (!coverageData) {
codeCovStr = 'N/A'; // Code coverage isn't calculated as part of version create command
}
else if (!coverageData.length) {
// Calculated code coverage data is too big to fit into a DB field. Retrieve it from the packageZip
codeCovStr =
'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.';
}
else {
displayCoverageRecords = coverageData.slice(0, maximumNumClasses).map((coverageDatum) => ({
key: coverageDatum.className,
value: `${coverageDatum.codeCoveragePercentage}%`,
}));
this.haveCodeCoverageData = displayCoverageRecords.length > 0;
}
displayRecords.push({
key: pvlMessages.getMessage('endToEndBuildDurationInSeconds'),
value: record.EndToEndBuildDurationInSeconds?.toFixed(1) ?? '',
});
}
// Always append code coverage column label ar the end
displayRecords.push({
key: messages.getMessage('codeCoveragePercentages'),
value: this.haveCodeCoverageData ? '...' : codeCovStr,
});
this.filterVerboseRecords(record, displayRecords, displayCoverageRecords, verbose);
this.table({ data: displayRecords, title: chalk.blue('Package Version') });
if (displayCoverageRecords.length > 0) {
this.table({ data: displayCoverageRecords });
}
}
// eslint-disable-next-line class-methods-use-this
filterVerboseRecords(record, displayRecords, displayCoverageRecords, verbose) {
if (!verbose) {
displayRecords.splice(displayRecords.map((e) => e.key).indexOf('Id'), 1);
if (!record.ConvertedFromVersionId?.trim()) {
displayRecords.splice(displayRecords.map((e) => e.key).indexOf(pvlMessages.getMessage('convertedFromVersionId')), 1);
}
displayRecords.splice(displayRecords.map((e) => e.key).indexOf(messages.getMessage('dependencies')), 1);
displayRecords.splice(displayRecords.map((e) => e.key).indexOf(messages.getMessage('codeCoveragePercentages')), 1);
displayCoverageRecords.splice(0, displayCoverageRecords.length);
}
}
// eslint-disable-next-line class-methods-use-this
massageResultsForDisplay(results) {
const record = Object.fromEntries(Object.entries(results).filter(([key, value]) => {
if (key === 'PackageType' && typeof value === 'string' && value === 'Unlocked') {
return false;
}
return !omissions.some((o) => o === key);
}));
record.Version = [record.MajorVersion, record.MinorVersion, record.PatchVersion, record.BuildNumber].join('.');
if (results.PackageType !== 'Managed') {
record.AncestorVersion = 'N/A';
record.AncestorId = 'N/A';
}
if (results.Package2.IsOrgDependent === true || results.ValidationSkipped === true) {
record.CodeCoverage = 'N/A';
}
else {
record.CodeCoverage = results.CodeCoverage;
}
record.HasPassedCodeCoverageCheck =
Boolean(results.Package2.IsOrgDependent) || results.ValidationSkipped
? 'N/A'
: results.HasPassedCodeCoverageCheck ?? undefined;
record.Package2 = {};
record.Package2.IsOrgDependent =
results.PackageType === 'Managed' ? 'N/A' : results.Package2.IsOrgDependent === true ? 'Yes' : 'No';
// set HasMetadataRemoved to N/A for Unlocked, and No when value is false or absent (pre-230)
record.HasMetadataRemoved = results.PackageType !== 'Managed' ? 'N/A' : results.HasMetadataRemoved ? 'Yes' : 'No';
record.ConvertedFromVersionId ??= ' ';
return record;
}
}
//# sourceMappingURL=report.js.map