1import chalk from 'chalk';
2
3import { formatCommitHash } from '../Formatter';
4import Git from '../Git';
5import logger from '../Logger';
6import { getListOfPackagesAsync } from '../Packages';
7import { ActionOptions } from './types';
8
9const { yellow } = chalk;
10
11async function safeGetMergeBaseAsync(ref: string): Promise<string | null> {
12  try {
13    return await Git.mergeBaseAsync(ref);
14  } catch (e) {
15    logger.error(`�� Cannot get merge base for reference: ${yellow(ref)}\n`, e.stack);
16    return null;
17  }
18}
19
20/**
21 * Resolves which packages should go through checks based on given options.
22 */
23export default async function getPackagesToCheckAsync(options: ActionOptions) {
24  const { all, packageNames } = options;
25
26  const allPackages = (await getListOfPackagesAsync()).filter((pkg) => {
27    // If the package doesn't have build or test script, just skip it.
28    return pkg.scripts.build || pkg.scripts.test;
29  });
30
31  if (all) {
32    return allPackages;
33  }
34  if (packageNames.length > 0) {
35    return allPackages.filter((pkg) => {
36      return packageNames.includes(pkg.packageName);
37    });
38  }
39
40  const sinceRef = options.since ?? 'main';
41  const mergeBase = await safeGetMergeBaseAsync(sinceRef);
42
43  if (!mergeBase) {
44    logger.warn(
45      `�� Couldn't find merge base with ${yellow(sinceRef)}, falling back to all packages\n`
46    );
47    return allPackages;
48  }
49
50  logger.info(`�� Using incremental checks since ${formatCommitHash(mergeBase)} commit\n`);
51  const changedFiles = await Git.logFilesAsync({ fromCommit: mergeBase });
52
53  return allPackages.filter((pkg) => {
54    const pkgPath = pkg.path.replace(/([^\/])$/, '$1/');
55    return changedFiles.some(({ path }) => path.startsWith(pkgPath));
56  });
57}
58