@salesforce/plugin-packaging
Version:
SF plugin that support Salesforce Packaging Platform
237 lines • 10.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 { Flags, loglevel, orgApiVersionFlagWithDeprecations, requiredOrgFlagWithDeprecations, SfCommand, } from '@salesforce/sf-plugins-core';
import { Lifecycle, Messages, SfError } from '@salesforce/core';
import { Duration } from '@salesforce/kit';
import { PackageEvents, SubscriberPackageVersion, } from '@salesforce/packaging';
import { Report } from './install/report.js';
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
const messages = Messages.loadMessages('@salesforce/plugin-packaging', 'package_install');
// maps of command flag values to PackageInstallRequest values
const securityType = { AllUsers: 'full', AdminsOnly: 'none' };
const upgradeType = { Delete: 'delete-only', DeprecateOnly: 'deprecate-only', Mixed: 'mixed-mode' };
export class Install extends SfCommand {
static summary = messages.getMessage('summary');
static description = messages.getMessage('description');
static examples = messages.getMessages('examples');
static deprecateAliases = true;
static aliases = ['force:package:install'];
static flags = {
loglevel,
'target-org': requiredOrgFlagWithDeprecations,
'api-version': orgApiVersionFlagWithDeprecations,
wait: Flags.duration({
unit: 'minutes',
char: 'w',
summary: messages.getMessage('flags.wait.summary'),
default: Duration.minutes(0),
}),
'installation-key': Flags.string({
char: 'k',
deprecateAliases: true,
aliases: ['installationkey'],
summary: messages.getMessage('flags.installation-key.summary'),
}),
'publish-wait': Flags.duration({
unit: 'minutes',
char: 'b',
deprecateAliases: true,
aliases: ['publishwait'],
summary: messages.getMessage('flags.publish-wait.summary'),
default: Duration.minutes(0),
}),
'no-prompt': Flags.boolean({
char: 'r',
deprecateAliases: true,
aliases: ['noprompt'],
summary: messages.getMessage('flags.no-prompt.summary'),
description: messages.getMessage('flags.no-prompt.description'),
}),
package: Flags.string({
char: 'p',
summary: messages.getMessage('flags.package.summary'),
required: true,
}),
'apex-compile': Flags.custom({
options: ['all', 'package'],
})({
char: 'a',
deprecateAliases: true,
aliases: ['apexcompile'],
summary: messages.getMessage('flags.apex-compile.summary'),
description: messages.getMessage('flags.apex-compile.description'),
default: 'all',
}),
'security-type': Flags.custom({
options: ['AllUsers', 'AdminsOnly'],
})({
char: 's',
deprecateAliases: true,
aliases: ['securitytype'],
summary: messages.getMessage('flags.security-type.summary'),
default: 'AdminsOnly',
}),
'upgrade-type': Flags.custom({
options: ['DeprecateOnly', 'Mixed', 'Delete'],
})({
char: 't',
deprecateAliases: true,
aliases: ['upgradetype'],
summary: messages.getMessage('flags.upgrade-type.summary'),
description: messages.getMessage('flags.upgrade-type.description'),
default: 'Mixed',
}),
'skip-handlers': Flags.string({
multiple: true,
options: ['FeatureEnforcement'],
char: 'l',
summary: messages.getMessage('flags.skip-handlers.summary'),
description: messages.getMessage('flags.skip-handlers.description'),
hidden: true,
}),
};
connection;
subscriberPackageVersion;
async run() {
const { flags } = await this.parse(Install);
const noPrompt = flags['no-prompt'];
this.connection = flags['target-org'].getConnection(flags['api-version']);
const apiVersion = parseInt(this.connection.getApiVersion(), 10);
if (apiVersion < 36) {
throw messages.createError('apiVersionTooLow');
}
this.subscriberPackageVersion = new SubscriberPackageVersion({
connection: this.connection,
aliasOrId: flags.package,
password: flags['installation-key'],
});
const request = {
SubscriberPackageVersionKey: await this.subscriberPackageVersion.getId(),
Password: flags['installation-key'],
ApexCompileType: flags['apex-compile'],
SecurityType: securityType[flags['security-type']],
SkipHandlers: flags['skip-handlers']?.join(','),
UpgradeType: upgradeType[flags['upgrade-type']],
};
// eslint-disable-next-line @typescript-eslint/require-await
Lifecycle.getInstance().on(PackageEvents.install.warning, async (warningMsg) => {
this.warn(warningMsg);
});
if (flags['publish-wait']?.milliseconds > 0) {
let timeThen = Date.now();
// waiting for publish to finish
let remainingTime = flags['publish-wait'];
Lifecycle.getInstance().on(PackageEvents.install['subscriber-status'],
// eslint-disable-next-line @typescript-eslint/require-await
async (publishStatus) => {
const elapsedTime = Duration.milliseconds(Date.now() - timeThen);
timeThen = Date.now();
remainingTime = Duration.milliseconds(remainingTime.milliseconds - elapsedTime.milliseconds);
const status = publishStatus === 'NO_ERRORS_DETECTED'
? messages.getMessage('availableForInstallation')
: messages.getMessage('unavailableForInstallation');
this.spinner.status = messages.getMessage('packagePublishWaitingStatus', [remainingTime.minutes, status]);
});
this.spinner.start(messages.getMessage('packagePublishWaitingStatus', [remainingTime.minutes, 'Querying Status']));
await this.subscriberPackageVersion.waitForPublish({
publishTimeout: flags['publish-wait'],
publishFrequency: Duration.seconds(10),
installationKey: flags['installation-key'],
});
// need to stop the spinner to avoid weird behavior with the prompts below
this.spinner.stop();
}
// If the user has specified --upgradetype Delete, then prompt for confirmation
// unless the noprompt option has been included.
if (flags['upgrade-type'] === 'Delete') {
await this.confirmUpgradeType(noPrompt);
}
// If the package has external sites, ask the user for permission to enable them
// unless the noprompt option has been included.
await this.confirmExternalSites(request, noPrompt);
let installOptions;
if (flags.wait) {
installOptions = {
pollingTimeout: flags.wait,
pollingFrequency: Duration.seconds(2),
};
let remainingTime = flags.wait;
let timeThen = Date.now();
this.spinner.start(messages.getMessage('packageInstallWaiting', [remainingTime.minutes]));
// waiting for package install to finish
Lifecycle.getInstance().on(PackageEvents.install.status,
// eslint-disable-next-line @typescript-eslint/require-await
async (piRequest) => {
const elapsedTime = Duration.milliseconds(Date.now() - timeThen);
timeThen = Date.now();
remainingTime = Duration.milliseconds(remainingTime.milliseconds - elapsedTime.milliseconds);
this.spinner.status = messages.getMessage('packageInstallWaitingStatus', [
remainingTime.minutes,
piRequest.Status,
]);
});
}
let pkgInstallRequest;
try {
pkgInstallRequest = await this.subscriberPackageVersion.install(request, installOptions);
this.spinner.stop();
}
catch (error) {
if (error instanceof SfError && error.data) {
pkgInstallRequest = error.data;
this.spinner.stop(messages.getMessage('packageInstallPollingTimeout'));
}
else {
throw error;
}
}
finally {
if (pkgInstallRequest) {
this.log(Report.parseStatus(this.config.bin, pkgInstallRequest, flags['target-org'].getUsername(), flags.package));
}
}
return pkgInstallRequest;
}
async finally(err) {
// Remove all the event listeners or they will still handle events
Lifecycle.getInstance().removeAllListeners(PackageEvents.install.warning);
Lifecycle.getInstance().removeAllListeners(PackageEvents.install.status);
Lifecycle.getInstance().removeAllListeners(PackageEvents.install['subscriber-status']);
await super.finally(err);
}
async confirmUpgradeType(noPrompt) {
if ((await this.subscriberPackageVersion.getPackageType()) === 'Unlocked' && !noPrompt) {
const promptMsg = messages.getMessage('prompt-upgrade-type');
if (!(await this.confirm({ message: promptMsg }))) {
throw messages.createError('promptUpgradeTypeDeny');
}
}
}
async confirmExternalSites(request, noPrompt) {
const extSites = await this.subscriberPackageVersion.getExternalSites();
if (extSites) {
let enableRss = true;
if (!noPrompt) {
const promptMsg = messages.getMessage('promptEnableRss', [extSites.join('\n')]);
enableRss = await this.confirm({ message: promptMsg });
}
if (enableRss) {
request.EnableRss = enableRss;
}
}
}
}
//# sourceMappingURL=install.js.map