UNPKG

@salesforce/plugin-release-management

Version:
276 lines 13 kB
"use strict"; /* * Copyright (c) 2021, 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 }); const os = require("os"); const fs = require("fs/promises"); const path = require("path"); const fg = require("fast-glob"); const shelljs_1 = require("shelljs"); const command_1 = require("@salesforce/command"); const core_1 = require("@salesforce/core"); const ts_types_1 = require("@salesforce/ts-types"); const chalk_1 = require("chalk"); const kit_1 = require("@salesforce/kit"); const types_1 = require("../../../types"); core_1.Messages.importMessagesDirectory(__dirname); const messages = core_1.Messages.load('@salesforce/plugin-release-management', 'cli.tarballs.verify', [ 'description', 'examples', 'cli', 'windowsUsernameBuffer', ]); const PASSED = chalk_1.green.bold('PASSED'); const FAILED = chalk_1.red.bold('FAILED'); /** * Checks if a file path exists * * @param filePath the file path to check the existence of */ async function fileExists(filePath) { try { await fs.access(filePath); return true; } catch (err) { return false; } } /** * The functionality of this command is taken entirely from https://github.com/salesforcecli/sfdx-cli/blob/v7.109.0/scripts/verify-tarballs */ class Verify extends command_1.SfdxCommand { constructor() { super(...arguments); this.step = 1; this.totalSteps = 1; } async run() { const cli = (0, ts_types_1.ensure)(this.flags.cli); this.baseDir = path.join('tmp', cli); const cliRunLists = { [types_1.CLI.SFDX]: [ this.ensureNoWebDriverIO.bind(this), this.ensureNoHerokuCliUtilNyc.bind(this), this.ensureWindowsPathLengths.bind(this), this.ensureApexNode.bind(this), this.ensurePluginGenerateTestTemplates.bind(this), this.ensureTemplatesCommands.bind(this), this.ensureNoDistTestsOrMaps.bind(this), this.ensureNoUnexpectedFiles.bind(this), this.ensureSfIsIncluded.bind(this), ], [types_1.CLI.SF]: [ this.ensureNoDistTestsOrMaps.bind(this), this.ensureNoUnexpectedFiles.bind(this), this.ensureWindowsPathLengths.bind(this), this.ensureMdMessagesExist.bind(this), ], }; this.totalSteps = cliRunLists[cli].length; for (const test of cliRunLists[cli]) { await test(); } } async execute(msg, validate) { this.ux.startSpinner(`[${this.step}/${this.totalSteps}] ${msg}`); if (!(await validate())) { this.ux.stopSpinner(FAILED); return false; } this.step += 1; this.ux.stopSpinner(PASSED); return true; } async ensureNoWebDriverIO() { const webDriverIo = path.join(this.baseDir, 'node_modules', 'webdriverio', 'test'); const validate = async () => { return !(await fileExists(webDriverIo)); }; const passed = await this.execute('Ensure webdriverio does not exist', validate); if (!passed) { throw new core_1.SfError(`${webDriverIo} is present. Was the clean not aggressive enough?`); } } async ensureNoHerokuCliUtilNyc() { const herokuCliUtil = path.join(this.baseDir, 'node_modules', '@salesforce', 'plugin-templates', 'node_modules', 'salesforce-alm', 'node_modules', 'heroku-cli-util', '.nyc_output'); const validate = async () => { return !(await fileExists(herokuCliUtil)); }; const passed = await this.execute('Ensure heroku-cli-util/.nyc_output does not exist', validate); if (!passed) { throw new core_1.SfError(`${herokuCliUtil} is present. Was the clean not aggressive enough?`); } } /** * Ensure that the path lengths in the build tree are as windows safe as possible. * * The check fails if the path lengths DO NOT allow for a username longer than the --windows-username-buffer * * Warnings will be emitted for any path that does not allow for a username longer than 48 characters */ async ensureWindowsPathLengths() { const validate = async () => { const maxWindowsPath = 259; const cli = (0, ts_types_1.ensure)(this.flags.cli); const supportedUsernameLength = (0, ts_types_1.ensureNumber)(this.flags['windows-username-buffer']); const fakeSupportedUsername = 'u'.repeat(supportedUsernameLength); const supportedBaseWindowsPath = `C:\\Users\\${fakeSupportedUsername}\\AppData\\Local\\${cli}\\tmp\\${cli}-cli-v1.xxx.yyy-abcdef-windows-x64\\`; const maxUsernameLength = 64; const fakeMaxUsername = 'u'.repeat(maxUsernameLength); const maxBaseWindowsPath = `C:\\Users\\${fakeMaxUsername}\\AppData\\Local\\${cli}\\tmp\\${cli}-cli-v1.xxx.yyy-abcdef-windows-x64\\`; const supportedWindowsPathLength = maxWindowsPath - supportedBaseWindowsPath.length; const maxWindowsPathLength = maxWindowsPath - maxBaseWindowsPath.length; this.log('Windows Path Length Test:'); this.log(` - max windows path length: ${maxWindowsPath}`); this.log(' ---- Upper Limit ----'); this.log(` - ${cli} max username length: ${maxUsernameLength}`); this.log(` - ${cli} max base path length: ${maxBaseWindowsPath.length}`); this.log(` - ${cli} max allowable path length: ${maxWindowsPathLength}`); this.log(' ---- Supported Limit ----'); this.log(` - ${cli} supported username length: ${supportedUsernameLength}`); this.log(` - ${cli} supported base path length: ${supportedBaseWindowsPath.length}`); this.log(` - ${cli} supported allowable path length: ${supportedWindowsPathLength}`); const paths = (await fg(`${this.baseDir}/node_modules/**/*`)).map((p) => p.replace(`${this.baseDir}${path.sep}`, '')); const warnPaths = paths .filter((p) => p.length >= maxWindowsPathLength && p.length < supportedWindowsPathLength) .sort(); const errorPaths = paths.filter((p) => p.length >= supportedWindowsPathLength).sort(); if (warnPaths.length) { this.log(`${chalk_1.yellow.bold('WARNING:')} Some paths could result in errors for Windows users with usernames that are ${maxUsernameLength} characters!`); warnPaths.forEach((p) => this.log(`${p.length} - ${p}`)); } if (errorPaths.length) { this.log(`${chalk_1.red.bold('ERROR:')} Unacceptably long paths detected in base build!`); errorPaths.forEach((p) => this.log(`${p.length} - ${p}`)); return false; } return true; }; const passed = await this.execute('Ensure windows path lengths', validate); if (!passed) { throw new core_1.SfError('Unacceptably long paths detected in base build!'); } } async ensureApexNode() { const apexNodePath = path.join(this.baseDir, 'node_modules', '@salesforce', 'apex-node', 'lib', 'src', 'tests'); const validate = async () => fileExists(apexNodePath); const passed = await this.execute('Ensure apex-node exists', validate); if (!passed) { throw new core_1.SfError(`${apexNodePath} is missing!. Was the clean too aggressive?`); } } async ensurePluginGenerateTestTemplates() { const pluginGeneratorTestPath = path.join(this.baseDir, 'node_modules', '@salesforce', 'plugin-generator', 'templates', 'sfdxPlugin', 'test'); const validate = async () => fileExists(pluginGeneratorTestPath); const passed = await this.execute('Ensure plugin generator test template exists', validate); if (!passed) { throw new core_1.SfError(`${pluginGeneratorTestPath} is missing!. Was the clean too aggressive?`); } } async ensureTemplatesCommands() { const templatesPath = path.join(this.baseDir, 'node_modules', '@salesforce', 'plugin-templates'); const validate = async () => fileExists(templatesPath); const passed = await this.execute('Ensure templates commands exist', validate); if (!passed) { throw new core_1.SfError(`${templatesPath} is missing!. Was the doc clean too aggressive?`); } } async ensureNoDistTestsOrMaps() { const validate = async () => { const files = await fg([`${this.baseDir}/dist/*.test.js`, `${this.baseDir}/dist/*.js.map`]); if (files.length) { this.log(chalk_1.red.bold('Found the following in dist:')); for (const file of files) this.log(file); return false; } return true; }; const passed = await this.execute('Ensure no tests or maps in dist', validate); if (!passed) { throw new core_1.SfError(`Found .test.js and/or .js.map files in ${path.join(this.baseDir, 'dist')}`); } } async ensureNoUnexpectedFiles() { const validate = async () => { const expectedFileGlobs = [ `${this.baseDir}/package.json`, `${this.baseDir}/LICENSE.txt`, `${this.baseDir}/README.md`, `${this.baseDir}/CHANGELOG.md`, `${this.baseDir}/yarn.lock`, `${this.baseDir}/oclif.manifest.json`, `${this.baseDir}/bin/*`, `${this.baseDir}/dist/**/*.js`, `${this.baseDir}/dist/builtins/package.json`, `${this.baseDir}/scripts/*`, `${this.baseDir}/sf/**/*`, ]; const expectedFiles = await fg(expectedFileGlobs); const allFiles = await fg([`${this.baseDir}/**/*`, `!${this.baseDir}/node_modules/**/*`]); const unexpectedFiles = allFiles.filter((f) => !expectedFiles.includes(f)); if (unexpectedFiles.length) { this.log(chalk_1.red.bold('Found unexpected files in base build dir:')); for (const file of unexpectedFiles) this.log(file); return false; } return true; }; const passed = await this.execute('Ensure no unexpected files', validate); if (!passed) { throw new core_1.SfError('Unexpected file found in base build dir!'); } } async ensureMdMessagesExist() { const validate = async () => { const fileData = await fs.readFile(path.join(this.baseDir, 'package.json'), 'utf8'); const packageJson = (0, kit_1.parseJson)(fileData, path.join(this.baseDir, 'package.json'), false); const plugins = (0, ts_types_1.get)(packageJson, 'oclif.plugins', []); const globs = plugins.map((p) => `${this.baseDir}/node_modules/${p}/messages/*.md`); const files = await fg(globs); return Boolean(files.length); }; const passed = await this.execute('Ensure .md messages exist', validate); if (!passed) { throw new core_1.SfError('Found no .md message files. Was the clean too aggresive?'); } } async ensureSfIsIncluded() { const validate = async () => { const sfBin = path.join(this.baseDir, 'bin', 'sf'); const sfBinExists = await fileExists(sfBin); const sfCmd = path.join(this.baseDir, 'bin', 'sf.cmd'); const sfCmdExists = await fileExists(sfCmd); const version = (0, shelljs_1.exec)(`${sfBin} --version`, { silent: false }); const help = (0, shelljs_1.exec)(`${sfBin} --help`, { silent: false }); return sfBinExists && sfCmdExists && version.code === 0 && help.code === 0; }; const passed = await this.execute('Ensure sf is included\n', validate); if (!passed) { throw new core_1.SfError('sf was not included! Did include-sf.js succeed?'); } } } exports.default = Verify; Verify.description = messages.getMessage('description'); Verify.examples = messages.getMessage('examples').split(os.EOL); Verify.flagsConfig = { cli: command_1.flags.enum({ description: messages.getMessage('cli'), options: Object.values(types_1.CLI), default: 'sfdx', char: 'c', }), ['windows-username-buffer']: command_1.flags.number({ description: messages.getMessage('windowsUsernameBuffer'), default: 41, char: 'w', }), }; //# sourceMappingURL=verify.js.map