xref: /expo/packages/@expo/cli/src/utils/git.ts (revision 8a424beb)
18d307f52SEvan Baconimport spawnAsync from '@expo/spawn-async';
28d307f52SEvan Baconimport chalk from 'chalk';
38d307f52SEvan Bacon
4814b6fafSEvan Baconimport { env } from './env';
529128565SEvan Baconimport { isInteractive } from './interactive';
68d307f52SEvan Baconimport { confirmAsync } from './prompts';
7*8a424bebSJames Ideimport * as Log from '../log';
88d307f52SEvan Bacon
98d307f52SEvan Baconexport async function maybeBailOnGitStatusAsync(): Promise<boolean> {
10814b6fafSEvan Bacon  if (env.EXPO_NO_GIT_STATUS) {
118d307f52SEvan Bacon    Log.warn(
128d307f52SEvan Bacon      'Git status is dirty but the command will continue because EXPO_NO_GIT_STATUS is enabled...'
138d307f52SEvan Bacon    );
148d307f52SEvan Bacon    return false;
158d307f52SEvan Bacon  }
168d307f52SEvan Bacon  const isGitStatusClean = await validateGitStatusAsync();
178d307f52SEvan Bacon
188d307f52SEvan Bacon  // Give people a chance to bail out if git working tree is dirty
198d307f52SEvan Bacon  if (!isGitStatusClean) {
2029128565SEvan Bacon    if (!isInteractive()) {
218d307f52SEvan Bacon      Log.warn(
228d307f52SEvan Bacon        `Git status is dirty but the command will continue because the terminal is not interactive.`
238d307f52SEvan Bacon      );
248d307f52SEvan Bacon      return false;
258d307f52SEvan Bacon    }
268d307f52SEvan Bacon
278d307f52SEvan Bacon    Log.log();
288d307f52SEvan Bacon    const answer = await confirmAsync({
298d307f52SEvan Bacon      message: `Would you like to proceed?`,
308d307f52SEvan Bacon    });
318d307f52SEvan Bacon
328d307f52SEvan Bacon    if (!answer) {
338d307f52SEvan Bacon      return true;
348d307f52SEvan Bacon    }
358d307f52SEvan Bacon
368d307f52SEvan Bacon    Log.log();
378d307f52SEvan Bacon  }
388d307f52SEvan Bacon  return false;
398d307f52SEvan Bacon}
408d307f52SEvan Bacon
418d307f52SEvan Baconexport async function validateGitStatusAsync(): Promise<boolean> {
428d307f52SEvan Bacon  let workingTreeStatus = 'unknown';
438d307f52SEvan Bacon  try {
448d307f52SEvan Bacon    const result = await spawnAsync('git', ['status', '--porcelain']);
458d307f52SEvan Bacon    workingTreeStatus = result.stdout === '' ? 'clean' : 'dirty';
4698ecfc87SJames Ide  } catch {
478d307f52SEvan Bacon    // Maybe git is not installed?
488d307f52SEvan Bacon    // Maybe this project is not using git?
498d307f52SEvan Bacon  }
508d307f52SEvan Bacon
518d307f52SEvan Bacon  if (workingTreeStatus === 'clean') {
528d307f52SEvan Bacon    Log.log(`Your git working tree is ${chalk.green('clean')}`);
538d307f52SEvan Bacon    Log.log('To revert the changes after this command completes, you can run the following:');
548d307f52SEvan Bacon    Log.log('  git clean --force && git reset --hard');
558d307f52SEvan Bacon    return true;
568d307f52SEvan Bacon  } else if (workingTreeStatus === 'dirty') {
578d307f52SEvan Bacon    Log.log(`${chalk.bold('Warning!')} Your git working tree is ${chalk.red('dirty')}.`);
588d307f52SEvan Bacon    Log.log(
598d307f52SEvan Bacon      `It's recommended to ${chalk.bold(
608d307f52SEvan Bacon        'commit all your changes before proceeding'
618d307f52SEvan Bacon      )}, so you can revert the changes made by this command if necessary.`
628d307f52SEvan Bacon    );
638d307f52SEvan Bacon  } else {
648d307f52SEvan Bacon    Log.log("We couldn't find a git repository in your project directory.");
658d307f52SEvan Bacon    Log.log("It's recommended to back up your project before proceeding.");
668d307f52SEvan Bacon  }
678d307f52SEvan Bacon
688d307f52SEvan Bacon  return false;
698d307f52SEvan Bacon}
70