@salesforce/plugin-packaging
Version:
SF plugin that support Salesforce Packaging Platform
285 lines • 12.8 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 os from 'node:os';
import { Flags, loglevel, orgApiVersionFlagWithDeprecations, SfCommand } from '@salesforce/sf-plugins-core';
import { camelCaseToTitleCase, Duration, env } from '@salesforce/kit';
import { Lifecycle, Messages } from '@salesforce/core';
import { INSTALL_URL_BASE, PackageVersion, PackageVersionEvents, PackagingSObjects, } from '@salesforce/packaging';
import { requiredHubFlag } from '../../../utils/hubFlag.js';
var Package2VersionStatus = PackagingSObjects.Package2VersionStatus;
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
const messages = Messages.loadMessages('@salesforce/plugin-packaging', 'package_version_create');
const fileCountThreshold = 7000;
const maxFileCountLimit = 10_000;
const fileSizeThreshold = 420; // 420MB
const maxFileSizeLimit = 600; // 600MB
export class PackageVersionCreateCommand extends SfCommand {
static summary = messages.getMessage('summary');
static description = messages.getMessage('description');
static examples = messages.getMessages('examples');
static requiresProject = true;
static deprecateAliases = true;
static aliases = ['force:package:version:create'];
static flags = {
loglevel,
'target-dev-hub': requiredHubFlag,
'api-version': orgApiVersionFlagWithDeprecations,
branch: Flags.string({
char: 'b',
summary: messages.getMessage('flags.branch.summary'),
}),
'build-instance': Flags.string({
deprecateAliases: true,
aliases: ['buildinstance'],
char: 's',
summary: messages.getMessage('flags.build-instance.summary'),
hidden: true,
}),
'code-coverage': Flags.boolean({
deprecateAliases: true,
aliases: ['codecoverage'],
char: 'c',
summary: messages.getMessage('flags.code-coverage.summary'),
description: messages.getMessage('flags.code-coverage.description'),
default: false,
exclusive: ['skip-validation'],
}),
'definition-file': Flags.file({
deprecateAliases: true,
exists: true,
aliases: ['definitionfile'],
char: 'f',
summary: messages.getMessage('flags.definition-file.summary'),
description: messages.getMessage('flags.definition-file.description'),
}),
'installation-key': Flags.string({
deprecateAliases: true,
aliases: ['installationkey'],
char: 'k',
summary: messages.getMessage('flags.installation-key.summary'),
exactlyOne: ['installation-key', 'installation-key-bypass'],
}),
'installation-key-bypass': Flags.boolean({
char: 'x',
deprecateAliases: true,
aliases: ['installationkeybypass'],
summary: messages.getMessage('flags.installation-key-bypass.summary'),
description: messages.getMessage('flags.installation-key-bypass.description'),
exactlyOne: ['installation-key', 'installation-key-bypass'],
}),
package: Flags.string({
char: 'p',
summary: messages.getMessage('flags.package.summary'),
}),
path: Flags.directory({
char: 'd',
summary: messages.getMessage('flags.path.summary'),
}),
'post-install-script': Flags.string({
deprecateAliases: true,
aliases: ['postinstallscript'],
summary: messages.getMessage('flags.post-install-script.summary'),
description: messages.getMessage('flags.post-install-script.description'),
}),
'post-install-url': Flags.string({
deprecateAliases: true,
aliases: ['postinstallurl'],
summary: messages.getMessage('flags.post-install-url.summary'),
description: messages.getMessage('flags.post-install-url.description'),
}),
preserve: Flags.boolean({
char: 'r',
summary: messages.getMessage('flags.preserve.summary'),
hidden: true,
}),
'releasenotes-url': Flags.string({
deprecateAliases: true,
aliases: ['releasenotesurl'],
summary: messages.getMessage('flags.releasenotes-url.summary'),
description: messages.getMessage('flags.releasenotes-url.description'),
}),
'skip-ancestor-check': Flags.boolean({
deprecateAliases: true,
aliases: ['skipancestorcheck'],
summary: messages.getMessage('flags.skip-ancestor-check.summary'),
default: false,
}),
'skip-validation': Flags.boolean({
deprecateAliases: true,
aliases: ['skipvalidation'],
summary: messages.getMessage('flags.skip-validation.summary'),
description: messages.getMessage('flags.skip-validation.description'),
default: false,
exclusive: ['code-coverage', 'async-validation'],
}),
'async-validation': Flags.boolean({
summary: messages.getMessage('flags.async-validation.summary'),
description: messages.getMessage('flags.async-validation.description'),
default: false,
exclusive: ['skip-validation'],
}),
tag: Flags.string({
char: 't',
summary: messages.getMessage('flags.tag.summary'),
}),
'uninstall-script': Flags.string({
deprecateAliases: true,
aliases: ['uninstallscript'],
summary: messages.getMessage('flags.uninstall-script.summary'),
description: messages.getMessage('flags.uninstall-script.description'),
}),
'validate-schema': Flags.boolean({
deprecateAliases: true,
aliases: ['validateschema'],
char: 'j',
summary: messages.getMessage('flags.validate-schema.summary'),
hidden: true,
}),
'version-description': Flags.string({
deprecateAliases: true,
aliases: ['versiondescription'],
char: 'e',
summary: messages.getMessage('flags.version-description.summary'),
}),
'version-name': Flags.string({
deprecateAliases: true,
aliases: ['versionname'],
char: 'a',
summary: messages.getMessage('flags.version-name.summary'),
}),
'version-number': Flags.string({
deprecateAliases: true,
aliases: ['versionnumber'],
char: 'n',
summary: messages.getMessage('flags.version-number.summary'),
description: messages.getMessage('flags.version-number.description'),
}),
wait: Flags.duration({
unit: 'minutes',
char: 'w',
summary: messages.getMessage('flags.wait.summary'),
default: Duration.minutes(0),
}),
language: Flags.string({
summary: messages.getMessage('flags.language.summary'),
description: messages.getMessage('flags.language.description'),
}),
verbose: Flags.boolean({
summary: messages.getMessage('flags.verbose.summary'),
description: messages.getMessage('flags.verbose.description'),
}),
};
async run() {
const { flags } = await this.parse(PackageVersionCreateCommand);
if (flags.path && flags.package) {
this.warn('Starting in v59.0 or later, specifying both the --package and --path flag will no longer be supported. Only one is required.');
void Lifecycle.getInstance().emitTelemetry({ Name: 'PathAndPackageFlag' });
}
if (flags['skip-validation']) {
this.warn(messages.getMessage('skip-validation-warning'));
}
const frequency = flags.wait && flags['skip-validation'] ? Duration.seconds(5) : Duration.seconds(30);
Lifecycle.getInstance().on(PackageVersionEvents.create.progress,
// no async methods
// eslint-disable-next-line @typescript-eslint/require-await
async (data) => {
if (data.Status !== Package2VersionStatus.success &&
data.Status !== Package2VersionStatus.error &&
data.Status !== Package2VersionStatus.performingValidations) {
const status = messages.getMessage('packageVersionCreateWaitingStatus', [
data.remainingWaitTime.minutes,
data.Status,
]);
if (flags.verbose) {
this.log(status);
}
else {
this.spinner.status = status;
}
}
});
Lifecycle.getInstance().on(PackageVersionEvents.create['preserve-files'],
// eslint-disable-next-line @typescript-eslint/require-await
async (data) => {
this.log(messages.getMessage('tempFileLocation', [data.location]));
});
const startMsg = messages.getMessage('requestInProgress');
// verbose does not use a spinner to ensure a separate line for each status update.
if (flags.verbose) {
this.log(`${startMsg}..`);
}
else {
this.spinner.start(startMsg);
}
// Set the SF_APPLY_REPLACEMENTS_ON_CONVERT env var so that
// string replacements happen automatically.
env.setBoolean('SF_APPLY_REPLACEMENTS_ON_CONVERT', true);
const result = await PackageVersion.create({
connection: flags['target-dev-hub'].getConnection(flags['api-version']),
project: this.project,
...Object.fromEntries(Object.entries(flags).map(([key, value]) => [key.replace(/-/g, ''), value])),
packageId: flags.package,
path: flags.path,
}, {
timeout: flags.wait,
frequency,
});
const finalStatusMsg = messages.getMessage('packageVersionCreateFinalStatus', [result.Status]);
if (flags.verbose) {
this.log(finalStatusMsg);
}
else {
this.spinner.stop(finalStatusMsg);
}
switch (result.Status) {
case Package2VersionStatus.error:
throw messages.createError('multipleErrors', [
result.Error?.map((e, i) => `${os.EOL}(${i + 1}) ${e}`).join(''),
]);
case Package2VersionStatus.performingValidations:
this.log(messages.getMessage('packageVersionCreatePerformingValidations'));
this.log(messages.getMessage(Package2VersionStatus.success, [
result.Id,
result.SubscriberPackageVersionId,
INSTALL_URL_BASE.toString(),
result.SubscriberPackageVersionId,
this.config.bin,
]));
break;
case Package2VersionStatus.success:
this.log(messages.getMessage(result.Status, [
result.Id,
result.SubscriberPackageVersionId,
INSTALL_URL_BASE.toString(),
result.SubscriberPackageVersionId,
this.config.bin,
]));
if ((result.TotalNumberOfMetadataFiles ?? 0) > fileCountThreshold) {
this.warn(messages.getMessage('warnOnTotalFileCountExceedingThreshold', [fileCountThreshold, maxFileCountLimit]));
}
if (result.TotalSizeOfMetadataFiles !== null &&
result.TotalSizeOfMetadataFiles !== undefined &&
result.TotalSizeOfMetadataFiles / (1024 * 1024) > fileSizeThreshold) {
this.warn(messages.getMessage('warnOnTotalFileSizeExceedingThreshold', [maxFileSizeLimit]));
}
break;
default:
this.log(messages.getMessage('InProgress', [camelCaseToTitleCase(result.Status), this.config.bin, result.Id]));
}
return result;
}
}
//# sourceMappingURL=create.js.map