UNPKG

@process-engine/ci_tools

Version:
204 lines (161 loc) 6.71 kB
import * as chalk from 'chalk'; import { readFileSync } from 'fs'; import { asyncSh, sh } from '../cli/shell'; import { parseVersion } from '../versions/parse_version'; type Pattern = string | RegExp; const COMMAND_NAME = 'upgrade-dependencies-with-pre-versions'; const BADGE = `[${COMMAND_NAME}]\t`; const DOC = ` Upgrades alpha- and beta-pre-versions for the packages with a name starting with one of the given args. Example: ci_tools upgrade-dependencies-with-pre-versions @process-engine/ Upgrades all deps starting with \`@process-engine/\` if there is a newer version available in their release channel or if a stable version of the same base version has been released. This command does not change base version! `; // DOC: see above export async function run(...args): Promise<boolean> { const isDryRun = args.indexOf('--dry') !== -1; const patternList = args.filter((arg: string): boolean => !arg.startsWith('-')); const dependencies = getAllDependencies(); const dependenciesFilteredByName = filterDependenciesByName(dependencies, patternList); const dependenciesFilteredByRequirement = filterDependenciesByRequirement(dependencies, [/-(alpha|beta)\./]); console.log(`${BADGE}patternList:\n`, patternList); console.log(`${BADGE}dependenciesFilteredByName:\n`, dependenciesFilteredByName); console.log(`${BADGE}dependenciesFilteredByRequirement:\n`, dependenciesFilteredByRequirement); const versionsToInstall = []; Object.keys(dependenciesFilteredByRequirement).forEach((packageName: string): void => { const requirement = dependenciesFilteredByRequirement[packageName]; const parsedRequirement = parseVersion(requirement); if (parsedRequirement == null) { return; } const output = sh(`npm view ${packageName} --json`); let json = null; try { json = JSON.parse(output); } catch (e) { console.error('Parsing result from npm view did not work:'); console.error(e); } if (json != null) { const allPackageVersions = json.versions; const versionUpgradeTo = getVersionToUpgradeTo(requirement, allPackageVersions); if (versionUpgradeTo) { versionsToInstall.push(`${packageName}@${versionUpgradeTo}`); } } }); console.log(`${BADGE}versionsToInstall\n`, versionsToInstall); if (versionsToInstall.length > 0) { await annotatedSh(`npm install --save-exact ${versionsToInstall.join(' ')}`, isDryRun); } return true; } export function getShortDoc(): string { return DOC.trim().split('\n')[0]; } export function printHelp(): void { console.log(`Usage: ci_tools ${COMMAND_NAME} <package-pattern> [<package-pattern>...] [--dry]`); console.log(''); console.log(DOC.trim()); } export function getVersionToUpgradeTo(requirement: string, versions: string[]): string | null { const parsedRequirement = parseVersion(requirement); if (parsedRequirement == null) { return null; } const versionsWithSameBaseVersion = getVersionsWithSameBaseVersion(requirement, versions); let upgradeToVersion = null; if (parsedRequirement.releaseChannelNumber != null) { let currentParsedVersion = parsedRequirement; versionsWithSameBaseVersion.forEach((versionString: string): void => { const parsedVersion = parseVersion(versionString); const hasGreaterNumber = parsedVersion.releaseChannelNumber != null && parsedVersion.releaseChannelNumber > currentParsedVersion.releaseChannelNumber; if (hasGreaterNumber) { upgradeToVersion = versionString; currentParsedVersion = parsedVersion; } }); } const exactMatchForBaseVersion = versionsWithSameBaseVersion.find( (versionString: string): boolean => versionString === parsedRequirement.baseString ); if (exactMatchForBaseVersion != null) { upgradeToVersion = exactMatchForBaseVersion; } return upgradeToVersion === requirement ? null : upgradeToVersion; } function getVersionsWithSameBaseVersion(requirement: string, versions: string[]): string[] { const parsedRequirement = parseVersion(requirement); return versions.filter((versionString: string): boolean => { const parsedVersion = parseVersion(versionString); if (parsedVersion == null) { return false; } const isSameBaseVersion = parsedVersion.baseString === parsedRequirement.baseString; const isStableVersion = parsedVersion.releaseChannelName === 'stable'; const isSameReleaseChannel = parsedVersion.releaseChannelName === parsedRequirement.releaseChannelName; const hasNoNumber = parsedVersion.releaseChannelNumber == null; const hasGreaterNumber = parsedVersion.releaseChannelNumber != null && parsedVersion.releaseChannelNumber > parsedRequirement.releaseChannelNumber; return ( parsedVersion != null && isSameBaseVersion && (isStableVersion || isSameReleaseChannel) && (hasNoNumber || hasGreaterNumber) ); }); } function filterDependenciesByName(dependencyObject: any, patternList: Pattern[]): any { const result = {}; Object.keys(dependencyObject).forEach((packageName: string): void => { const patternMatched = matchesPatternList(packageName, patternList); if (patternMatched) { result[packageName] = dependencyObject[packageName]; } }); return result; } function filterDependenciesByRequirement(dependencyObject: any, patternList: Pattern[]): any { const result = {}; Object.keys(dependencyObject).forEach((packageName: string): void => { const requirement = dependencyObject[packageName]; const patternMatched = matchesPatternList(requirement, patternList); if (patternMatched) { result[packageName] = requirement; } }); return result; } function matchesPatternList(value: string, patternList: Pattern[]): boolean { return patternList.some((pattern: Pattern): boolean => { if (typeof pattern === 'string') { return value.startsWith(pattern); } return value.match(pattern) != null; }); } function getAllDependencies(): any { const packageJson = getPackageJson(); const allPackages = Object.assign({}, packageJson.dependencies, packageJson.devDependencies); return allPackages; } function getPackageJson(): any { const content = readFileSync('package.json').toString(); const json = JSON.parse(content); return json; } async function annotatedSh(command: string, isDryRun: boolean): Promise<void> { console.log(`${BADGE}`); console.log(`${BADGE}Running: ${chalk.cyan(command)}`); if (isDryRun) { console.log(chalk.yellow('\n [skipping execution due to --dry]\n')); return; } const output = await asyncSh(command); console.log(output); }