@salesforce/plugin-release-management
Version:
A plugin for preparing and publishing npm packages
205 lines • 8.04 kB
JavaScript
;
/*
* Copyright (c) 2020, salesforce.com, inc.
* All rights reserved.
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.PackageRepo = void 0;
const os = require("os");
const ts_types_1 = require("@salesforce/ts-types");
const shelljs_1 = require("shelljs");
const core_1 = require("@salesforce/core");
const kit_1 = require("@salesforce/kit");
const ts_types_2 = require("@salesforce/ts-types");
const chalk = require("chalk");
const package_1 = require("./package");
const registry_1 = require("./registry");
const inspectCommits_1 = require("./inspectCommits");
const packAndSign_1 = require("./codeSigning/packAndSign");
class Repository extends kit_1.AsyncOptionalCreatable {
constructor(options) {
super(options);
this.stepCounter = 1;
this.options = options;
this.ux = options.ux;
this.shouldBePublished = options.shouldBePublished;
this.env = new kit_1.Env();
this.registry = new registry_1.Registry();
}
install(silent = false) {
this.execCommand(`yarn install ${this.registry.getRegistryParameter()}`, silent);
}
build(silent = false) {
this.execCommand('yarn build', silent);
}
run(script, location, silent = false) {
if (location) {
this.execCommand(`(cd ${location} && yarn run ${script})`, silent);
}
else {
this.execCommand(`(yarn run ${script})`, silent);
}
}
test() {
this.execCommand('yarn test');
}
getBranchName() {
const branch = this.env.getString('CIRCLE_BRANCH', null) || (0, shelljs_1.exec)('git branch --show-current', { silent: true }).stdout;
return (0, ts_types_1.ensureString)(branch);
}
pushChangesToGit() {
const branch = this.getBranchName();
const cmd = `git push --set-upstream --no-verify --follow-tags origin ${branch}`;
this.execCommand(cmd, false);
}
stageChanges() {
this.execCommand('git add .', false);
}
revertUnstagedChanges() {
const changedFiles = (0, shelljs_1.exec)('git diff --name-only', { silent: true })
.stdout.split(os.EOL)
.filter((f) => !!f);
changedFiles.forEach((file) => {
(0, shelljs_1.exec)(`git checkout -- ${file}`, { silent: false });
});
}
revertAllChanges() {
(0, shelljs_1.exec)('git reset --hard HEAD', { silent: true });
}
printStage(msg) {
this.ux.log(chalk.green.bold(`${os.EOL}${this.stepCounter}) ${msg}`));
this.stepCounter += 1;
}
async writeNpmToken() {
const home = this.env.getString('HOME') ?? os.homedir();
await this.registry.setNpmAuth(home);
await this.registry.setNpmRegistry(home);
}
execCommand(cmd, silent) {
if (!silent)
this.ux.log(`${chalk.dim(cmd)}${os.EOL}`);
const result = (0, shelljs_1.exec)(cmd, { silent });
if (result.code !== 0) {
throw new core_1.SfError(result.stderr, 'FailedCommandExecution');
}
else {
return result;
}
}
async poll(checkFn) {
const isNonTTY = this.env.getBoolean('CI') || this.env.getBoolean('CIRCLECI');
let found = false;
let attempts = 0;
const maxAttempts = 300;
const start = isNonTTY ? (msg) => this.ux.log(msg) : (msg) => this.ux.startSpinner(msg);
const update = isNonTTY
? (msg) => this.ux.log(msg)
: (msg) => this.ux.setSpinnerStatus(msg);
const stop = isNonTTY ? (msg) => this.ux.log(msg) : (msg) => this.ux.stopSpinner(msg);
start('Polling for new version(s) to become available on npm');
while (attempts < maxAttempts && !found) {
attempts += 1;
update(`attempt: ${attempts} of ${maxAttempts}`);
found = checkFn();
await (0, kit_1.sleep)(1000);
}
stop(attempts >= maxAttempts ? 'failed' : 'done');
return found;
}
/**
* If the commit type isn't fix (patch bump), feat (minor bump), or breaking (major bump),
* then standard-version always defaults to a patch bump.
* See https://github.com/conventional-changelog/standard-version/issues/577
*
* We, however, don't want to publish a new version for chore, docs, etc. So we analyze
* the commits to see if any of them indicate that a new release should be published.
*/
async isReleasable(pkg) {
const commitInspection = await (0, inspectCommits_1.inspectCommits)(pkg);
return commitInspection.shouldRelease;
}
}
class PackageRepo extends Repository {
constructor(options) {
super(options);
}
validate() {
return this.package.validateNextVersion();
}
prepare(opts = {}) {
const { dryrun } = opts;
if (this.package.hasScript('version')) {
this.run('version');
this.stageChanges();
}
let cmd = 'npx standard-version --commit-all --releaseCommitMessageFormat="chore(release): {{currentTag}} [ci skip]"';
if (dryrun)
cmd += ' --dry-run';
cmd += ` --release-as ${this.nextVersion}`;
this.execCommand(cmd);
}
async sign() {
packAndSign_1.api.setUx(this.ux);
return packAndSign_1.api.packSignVerifyModifyPackageJSON(this.package.location);
}
async revertChanges() {
return packAndSign_1.api.revertPackageJsonIfExists();
}
getPkgInfo() {
return {
name: this.name,
nextVersion: this.nextVersion,
registryParam: this.registry.getRegistryParameter(),
};
}
async publish(opts = {}) {
const { dryrun, signatures, access, tag } = opts;
if (!dryrun)
await this.writeNpmToken();
let cmd = 'npm publish';
if (signatures?.[0]?.fileTarPath)
cmd += ` ${signatures[0]?.fileTarPath}`;
if (tag)
cmd += ` --tag ${tag}`;
if (dryrun)
cmd += ' --dry-run';
cmd += ` ${this.registry.getRegistryParameter()}`;
cmd += ` --access ${access || 'public'}`;
this.execCommand(cmd);
}
async waitForAvailability() {
return this.poll(() => this.package.nextVersionIsAvailable());
}
getSuccessMessage() {
return chalk.green.bold(`Successfully released ${this.name}@${this.nextVersion}`);
}
async init() {
this.logger = await core_1.Logger.child(this.constructor.name);
this.package = await package_1.Package.create();
this.shouldBePublished = await this.isReleasable(this.package);
this.nextVersion = this.determineNextVersion();
this.package.setNextVersion(this.nextVersion);
this.name = this.package.npmPackage.name;
}
determineNextVersion() {
if (this.package.nextVersionIsHardcoded()) {
this.logger.debug(`${this.package.packageJson.name}@${this.package.packageJson.version} does not exist in the registry. Assuming that it's the version we want published`);
return this.package.packageJson.version;
}
else {
this.logger.debug('Using standard-version to determine next version');
let command = 'npx standard-version --dry-run --skip.tag --skip.commit --skip.changelog';
// It can be an empty string if they want
if ((0, ts_types_2.isString)(this.options.useprerelease)) {
command += ` --prerelease ${this.options.useprerelease}`;
}
const result = this.execCommand(command, true);
const nextVersionRegex = /(?<=to\s)([0-9]{1,}\.|.){2,}/gi;
return result.match(nextVersionRegex)[0];
}
}
}
exports.PackageRepo = PackageRepo;
//# sourceMappingURL=repository.js.map