UNPKG

@salesforce/plugin-release-management

Version:
236 lines 9.93 kB
#!/usr/bin/env node "use strict"; /* * Copyright (c) 2018, 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.api = void 0; /* eslint-disable no-underscore-dangle */ const fs = require("fs/promises"); const child_process_1 = require("child_process"); const os_1 = require("os"); const path_1 = require("path"); const core_1 = require("@salesforce/core"); const kit_1 = require("@salesforce/kit"); const ProxyAgent = require("proxy-agent"); const proxy_from_env_1 = require("proxy-from-env"); const SimplifiedSigning_1 = require("./SimplifiedSigning"); const error_1 = require("./error"); const NpmName_1 = require("./NpmName"); class PathGetter { constructor(target) { this._cwd = process.cwd(); if (!target) { this._target = this._cwd; } else if (target && target.includes(this._cwd)) { this._target = target; } else { this._target = (0, path_1.join)(this._cwd, target); } this._packageJson = (0, path_1.join)(this._target, PathGetter.packageJson); this._packageJsonBak = (0, path_1.join)(this._target, `${PathGetter.packageJson}.bak`); } get packageJson() { return this._packageJson; } get packageJsonBak() { return this._packageJsonBak; } get target() { return this._target; } getFile(filename) { return (0, path_1.join)(this._target, filename); } getIgnoreFile(filename) { return (0, path_1.join)(this._cwd, filename); } } PathGetter.packageJson = 'package.json'; let cliUx; let pathGetter; exports.api = { setUx(ux) { cliUx = ux; }, /** * call out to npm pack; */ pack() { if (!pathGetter) pathGetter = new PathGetter(); return new Promise((resolve, reject) => { const command = 'npm pack -p'; (0, child_process_1.exec)(command, { cwd: pathGetter.target, maxBuffer: 1024 * 4096 }, (error, stdout, stderr) => { if (error && error['code']) { return reject(new error_1.ExecProcessFailed(command, error['code'], stderr)); } else { const output = stdout.split(os_1.EOL); if (output.length > 1) { // note the output end with a newline; const path = output[output.length - 2]; if (path && path.endsWith('tgz')) { return resolve(pathGetter.getFile(path)); } else { return reject(new kit_1.NamedError('UnexpectedNpmFormat', `Npm pack did not return an expected tgz filename result: [${path}]`)); } } else { return reject(new kit_1.NamedError('UnexpectedNpmFormat', `The output from the npm utility is unexpected [${stdout}]`)); } } }); }); }, /** * read the package.json file for the target npm to be signed. */ retrievePackageJson() { return fs.readFile(pathGetter.packageJson, { encoding: 'utf8' }); }, /** * read the npm ignore file for the target npm * * @param filename - local path to the npmignore file */ retrieveIgnoreFile(filename) { return fs.readFile(pathGetter.getIgnoreFile(filename), { encoding: 'utf8' }); }, /** * checks the ignore content for the code signing patterns. *.tgz, *.sig package.json.bak * * @param content */ validateNpmIgnorePatterns(content) { const validate = (pattern) => { if (!content) { throw new kit_1.NamedError('MissingNpmIgnoreFile', 'Missing .npmignore file. The following patterns are required in for code signing: *.tgz, *.sig, package.json.bak.'); } if (!content.includes(pattern)) { throw new kit_1.NamedError('MissingNpmIgnorePattern', `.npmignore is missing ${pattern}. The following patterns are required for code signing: *.tgz, *.sig, package.json.bak`); } }; validate('*.tgz'); validate('*.sig'); validate('package.json.bak'); }, /** * checks the ignore content for the code signing patterns. *.tgz, *.sig package.json.bak * * @param content */ validateNpmFilePatterns(patterns) { const validate = (pattern) => { if (patterns.includes(pattern)) { throw new kit_1.NamedError('ForbiddenFilePattern', 'the files property in package.json should not include the following: *.tgz, *.sig, package.json.bak'); } }; validate('*.tgz'); validate('*.sig'); validate('package.json.bak'); }, /** * makes a backup copy pf package.json * * @param src - the package.json to backup * @param dest - package.json.bak */ async copyPackageDotJson(src, dest) { await fs.copyFile(src, dest); }, /** * used to update the contents of package.json * * @param pJson - the updated json content to write to disk */ writePackageJson(pJson) { return fs.writeFile(pathGetter.packageJson, JSON.stringify(pJson, null, 4)); }, async revertPackageJsonIfExists() { // Restore the package.json file so it doesn't show a git diff. await fs.access(pathGetter.packageJsonBak); cliUx.log(`Restoring package.json from ${pathGetter.packageJsonBak}`); await exports.api.copyPackageDotJson(pathGetter.packageJsonBak, pathGetter.packageJson); await fs.unlink(pathGetter.packageJsonBak); }, /** * main method to pack and sign an npm. * * @param args - reference to process.argv * @param ux - The cli ux interface usually provided by oclif. * @return {Promise<SigningResponse>} The SigningResponse */ async packSignVerifyModifyPackageJSON(targetPackagePath) { const logger = await core_1.Logger.child('packAndSign'); pathGetter = new PathGetter(targetPackagePath); try { // read package.json info const packageJsonContent = await exports.api.retrievePackageJson(); const packageJson = JSON.parse(packageJsonContent); logger.debug('parsed the package.json content'); if (packageJson.files) { // validate that files property does not include forbidden patterns exports.api.validateNpmFilePatterns(packageJson.files); } else { // validate npm ignore has what we name. const npmIgnoreContent = await exports.api.retrieveIgnoreFile('.npmignore'); exports.api.validateNpmIgnorePatterns(npmIgnoreContent); logger.debug('validated the expected npm ignore patterns'); } // Recommend updating git ignore to match npmignore. const filename = '.gitignore'; const gitIgnoreContent = await exports.api.retrieveIgnoreFile(filename); try { exports.api.validateNpmIgnorePatterns(gitIgnoreContent); logger.debug('validated the expected git ignore patterns'); } catch (e) { cliUx.warn(`WARNING: The following patterns are recommended in ${filename} for code signing: *.tgz, *.sig, package.json.bak.`); } // get the packageJson name/version const npmName = NpmName_1.NpmName.parse(packageJson.name); logger.debug(`parsed the following npmName components: ${JSON.stringify(npmName, null, 4)}`); npmName.tag = packageJson.version; // make a backup of the packageJson await exports.api.copyPackageDotJson(pathGetter.packageJson, pathGetter.packageJsonBak); logger.debug('made a backup of the package.json file.'); cliUx.log(`Backed up ${pathGetter.packageJson} to ${pathGetter.packageJsonBak}`); const packageNameWithOrWithoutScope = npmName.scope ? `@${npmName.scope}/${npmName.name}` : npmName.name; // we have to modify package.json with security URLs BEFORE packing // update the package.json object with the signature urls and write it to disk. packageJson.sfdx = (0, SimplifiedSigning_1.getSfdxProperty)(packageNameWithOrWithoutScope, npmName.tag); await exports.api.writePackageJson(packageJson); cliUx.log('Successfully updated package.json with public key and signature file locations.'); cliUx.logJson(packageJson.sfdx); const filepath = await exports.api.pack(); cliUx.log(`Packed tgz to ${filepath}`); const signResponse = await (0, SimplifiedSigning_1.signVerifyUpload)({ upload: true, targetFileToSign: filepath, packageName: packageNameWithOrWithoutScope, packageVersion: npmName.tag, }); return signResponse; } finally { // prevent any publish-time changes from persisting to git await exports.api.revertPackageJsonIfExists(); } }, getAgentForUri(url) { /* eslint-disable @typescript-eslint/no-unsafe-call */ const proxyUrl = (0, proxy_from_env_1.getProxyForUrl)(url); const agent = ProxyAgent(proxyUrl); /* eslint-disable @typescript-eslint/no-unsafe-call */ return { https: agent, http: agent }; }, }; //# sourceMappingURL=packAndSign.js.map