UNPKG

@salesforce/plugin-packaging

Version:

SF plugin that support Salesforce Packaging Platform

252 lines 11.6 kB
/* * 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