UNPKG

@adinsure-ops/ops-cli

Version:

Operations CLI for working with AdInsure

286 lines (285 loc) 11.8 kB
import { __awaiter } from "tslib"; import { Args, Flags, ux } from '@oclif/core'; import axios from 'axios'; import chalk from 'chalk'; import fs from 'fs-extra'; import { globby } from 'globby'; import moment from 'moment'; import path from 'path'; import replaceInFile from 'replace-in-file'; import semver from 'semver'; import CommandBase from '../command.base.js'; class Version extends CommandBase { /** * Update NPM packages with wsrun * @param type Release type of NPM package * @param version Version * @param filter Regular Expression filter for packages * @param excludeDirs Excluded directories * @returns {Promise<void>} Void */ bumpNpm(type, version, filter, excludeDirs) { return __awaiter(this, void 0, void 0, function* () { const currentPrereleaseDate = moment().format('YYYYMM'); const bumpedPackageName = []; // Find all package.json files that are not in node modules const searchPattern = ['**/package.json', '.**/package.json', '!**/node_modules']; if (excludeDirs) { for (const exclude of excludeDirs) { searchPattern.push(`!${exclude}`); } } const paths = yield globby(searchPattern); for (const path of paths) { try { // Read the data from it const data = yield fs.readJSON(path); // Check if version exists if (!data.version || !data.name) { continue; } const regexp = new RegExp(filter); if (!regexp.test(data.name)) { continue; } // Increment the package version depending on the increment type if (type === 'new') { data.version = version; } else if (type === 'prerelease' && this.checkPrerelease(data.version, currentPrereleaseDate)) { data.version = semver.inc(data.version, type, true); } else { data.version = semver.inc(data.version, type, false, currentPrereleaseDate); } bumpedPackageName.push(data.name); // Save file yield fs.writeJSON(path, data, { spaces: 4, EOL: '\r\n' }); // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error) { if (error instanceof SyntaxError) { this.warn(`Error reading package.json: ${chalk.yellow(error.message)}. Consider using '--exclude-dir'.`); } else { return this.error(`Unknown error: ${chalk.red(error)}`); } } } return bumpedPackageName; }); } /** * Validate args and flags */ // eslint-disable-next-line @typescript-eslint/no-explicit-any validateOptions(args, flags) { if (args.type !== 'new' && flags.version) { this.error(`${chalk.red('Version flag can only be used with new type')}`, { exit: 2 }); } if (args.type === 'new' && !flags.version) { this.error(`${chalk.red('Version flag is required with new type')}`, { exit: 2 }); } if (args.type === 'new' && flags.increment) { this.error(`${chalk.red('Increment flag not supported with new type')}`, { exit: 2 }); } return true; } /** * Update dependencies in package.json files for @adinsure packages * @param version Version * @param updatedPackages Array of updated packages * @returns {void} */ updateDependencies(version, updatedPackages) { return __awaiter(this, void 0, void 0, function* () { // Find all package.json files that are not in node modules const paths = yield globby(['**/package.json', '.**/package.json', '!**/node_modules']); for (const path of paths) { for (const packageName of updatedPackages) { const re = new RegExp('"' + packageName + '": "\\d+\\.\\d+\\..*"', 'g'); yield replaceInFile({ files: path, from: re, to: `"${packageName}": "${version}"`, }); } } }); } // eslint-disable-next-line @typescript-eslint/no-explicit-any insertScriptRequest(token, version, prefix) { return __awaiter(this, void 0, void 0, function* () { return axios.post(`https://ops.adinsure.com/api/script`, { version, prefix }, { headers: { Authorization: 'Bearer ' + token, 'Content-Type': 'application/json', }, }); }); } insertScript(token_1, version_1, prefix_1) { return __awaiter(this, arguments, void 0, function* (token, version, prefix, skip = false) { ux.action.start('Inserting new script version', undefined, { stdout: true }); if (skip) { ux.action.stop(`${chalk.blue('skip')}`); } else if (token) { yield this.insertScriptRequest(token, version, prefix) .catch((error) => { switch (error.response.status) { case 401: { this.error(`${chalk.red('You don\'t have permissions. If this was intentional command, please contact Operations for permissions, do git reset and rerun command.')}`); } default: { this.error(`${chalk.red(error)}`); } } }); ux.action.stop(`${chalk.green('done')}`); } else { ux.action.stop(`${chalk.red('error')}`); } }); } /** * Check if version has valid prerelease as 'rc' or '201910' (year month) * @param version Version * @param month Month of release * @returns {boolean} True or false */ checkPrerelease(version, month) { const prereleaseVersion = semver.prerelease(version, true); if (!prereleaseVersion) return false; if (prereleaseVersion[0].toString() === month) return true; if (prereleaseVersion[0].toString() === 'rc') return true; return false; } /** * Write new version in VERSION file * @param version The new version * @returns {void} */ setVersion(version) { return __awaiter(this, void 0, void 0, function* () { const versionPath = path.resolve(yield this.getGitRoot(), 'VERSION'); if (!(yield fs.pathExists(versionPath))) { throw new Error('VERSION file not found. File will not be updated.'); } return fs.writeFileSync(versionPath, version, 'utf8'); }); } run() { const _super = Object.create(null, { run: { get: () => super.run } }); return __awaiter(this, void 0, void 0, function* () { _super.run.call(this); const { args, flags } = yield this.parse(Version); const currentPrereleaseDate = moment().format('YYYYMM'); // Check if token is correct and save it for later use let token = ''; if (!flags['no-script']) { token = yield this.security.getToken(); } // exit if validation fails if (!this.validateOptions(args, flags)) { return; } let newVersion = null; if (args.type === 'new' && flags.version) { this.log(`Using 'new' command. VERSION file ignored.`); newVersion = semver.valid(flags.version); } else { // Get current version const fullVersion = yield this.getVersion(); this.log(`Current version is ${chalk.green(fullVersion)}`); // Calculate new version newVersion = semver.inc(fullVersion, args.type, true, currentPrereleaseDate); // Force prerelease if prerelease is in same month and increment RC correctly if (args.type === 'prerelease' && this.checkPrerelease(fullVersion, currentPrereleaseDate)) { newVersion = semver.inc(fullVersion, args.type, true); } // Allow npm packages to be versioned seperately. Ultimately we want to force npm package to have same verion as VERSION file. if (!flags.independent) { args.type = 'new'; } } // Stop if version invalid if (!newVersion) { return this.error(`${chalk.red('Version in incorrect format')}`); } this.log(`Setting new version to ${chalk.green(newVersion)}`); try { yield this.setVersion(newVersion); // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error) { this.warn(`${chalk.yellow(error.message)}`); } ux.action.start('Bumping NPM packages', undefined, { stdout: true }); const packages = yield this.bumpNpm(args.type, newVersion, flags['only-npm-packages'], flags['exclude-dir']); ux.action.stop(`${chalk.green('done')}`); ux.action.start('Updating NPM dependencies in package.json files', undefined, { stdout: true }); if (flags['no-deps']) { ux.action.stop(`${chalk.blue('skip')}`); } else { yield this.updateDependencies(newVersion, packages); ux.action.stop(`${chalk.green('done')}`); } yield this.insertScript(token, newVersion, flags['script-prefix'], flags['no-script']); }); } } Version.description = 'release new version'; Version.usage = 'version <type> [flags]'; Version.examples = [ `$ ops version patch`, `$ ops version prerelease -i`, `$ ops version new -v 3.4.3`, ]; Version.args = { type: Args.string({ description: 'Type of release', options: ['patch', 'minor', 'major', 'prerelease', 'preminor', 'premajor', 'new'], required: true, }), }; Version.flags = { independent: Flags.boolean({ char: 'i', description: 'increment npm packages independent of VERSION file', default: false, }), version: Flags.string({ char: 'v', description: 'force version', }), 'script-prefix': Flags.string({ description: 'prefix for db script version creation. (default: 7.10)', default: '7.10', }), 'no-deps': Flags.boolean({ description: 'skip dependency update', default: false, }), 'no-script': Flags.boolean({ description: 'skip creation of script', default: false, }), 'only-npm-packages': Flags.string({ description: 'regular expression for which NPM packages to include', default: '.*', }), 'exclude-dir': Flags.string({ description: 'exclude directories where to search for package.json', multiple: true, }), }; export default Version;