import chalk from 'chalk'; import inquirer from 'inquirer'; import Git from '../../Git'; import logger from '../../Logger'; import { Task } from '../../TasksRunner'; import { CommandOptions, Parcel, TaskArgs } from '../types'; import { prepareParcels } from './prepareParcels'; const { green, cyan, blue, yellow } = chalk; /** * Checks packages integrity and warns about violations. * Integrity is violated if the current version of a package: * - has no `gitHead` property in its package view. * - commit to which `gitHead` refers is not an ancestor of the current head commit. * - mismatches last version found in changelog. */ export const checkPackagesIntegrity = new Task( { name: 'checkPackagesIntegrity', dependsOn: [prepareParcels], }, async (parcels: Parcel[], options: CommandOptions): Promise => { logger.info('\nšŸ‘ Checking packages integrity...'); const resolver = async ({ pkg, pkgView, changelog }: Parcel): Promise => { if (!pkgView) { // If package view is not there, then the package hasn't been released yet - no need to check integrity. return true; } const isAncestor = !!pkgView.gitHead && (await Git.isAncestorAsync(pkgView.gitHead)); const lastChangelogVersion = await changelog.getLastPublishedVersionAsync(); const isVersionMatching = !lastChangelogVersion || pkgView.version === lastChangelogVersion; const integral = isAncestor && isVersionMatching; if (!integral) { logger.warn(`\nāš ļø Integrity checks failed for ${green(pkg.packageName)}.`); } if (!pkgView.gitHead) { logger.warn(` Cannot find ${blue('gitHead')} in package view.`); } else if (!isAncestor) { logger.warn( ` Local version ${cyan(pkgView.version)} has been published from different branch.` ); } if (!isVersionMatching) { logger.warn( ` Last version in changelog ${cyan(lastChangelogVersion!)}`, `doesn't match local version ${cyan(pkgView.version)}.` ); } return integral; }; const results = await Promise.all(parcels.map(resolver)); const somethingFailed = results.some((result) => !result); if (options.checkIntegrity) { if (somethingFailed) { logger.error('\n🚫 Integrity checks failed.'); } else { logger.success('\nāœ… All integrity checks passed.'); } return; } if (somethingFailed && (await shouldStopOnFailedIntegrityChecksAsync())) { if (process.env.CI) { throw new Error('Some integrity checks failed – it is prohibited on the CI.'); } return Task.STOP; } } ); /** * Resolves to a boolean value that means whether to stop the workflow if some integrity checks have failed. * It immediately returns `true` if it's run on the CI. */ async function shouldStopOnFailedIntegrityChecksAsync(): Promise { if (process.env.CI) { return true; } const { proceed } = await inquirer.prompt<{ proceed: boolean }>([ { type: 'confirm', name: 'proceed', prefix: 'ā”', message: yellow('Some integrity checks have failed. Do you want to proceed either way?'), default: true, }, ]); return !proceed; }