1import chalk from 'chalk'; 2import inquirer from 'inquirer'; 3 4import Git from '../../Git'; 5import logger from '../../Logger'; 6import { Task } from '../../TasksRunner'; 7import { CommandOptions, Parcel, TaskArgs } from '../types'; 8 9const { cyan, yellow, blue } = chalk; 10 11/** 12 * Checks whether the current branch is correct and working dir is not dirty. 13 */ 14export const checkRepositoryStatus = new Task<TaskArgs>( 15 { 16 name: 'checkRepositoryStatus', 17 required: true, 18 backupable: false, 19 }, 20 async (parcels: Parcel[], options: CommandOptions): Promise<void | symbol> => { 21 if (options.skipRepoChecks) { 22 return; 23 } 24 logger.info(`\n️♂️ Checking repository status...`); 25 26 const currentBranch = await Git.getCurrentBranchNameAsync(); 27 const trackingBranch = await Git.getTrackingBranchNameAsync(); 28 29 // Check whether it's allowed to publish from the current branch. 30 if (!(await checkBranchNameAsync(currentBranch))) { 31 return Task.STOP; 32 } 33 34 // If tracking branch is set, then we must ensure it is still up-to-date with it. 35 if (trackingBranch) { 36 await Git.fetchAsync(); 37 38 const stats = await Git.compareBranchesAsync(currentBranch, trackingBranch); 39 40 if (stats.ahead + stats.behind > 0) { 41 logger.error( 42 ` Your local branch ${cyan(currentBranch)} is out of sync with remote branch.` 43 ); 44 return Task.STOP; 45 } 46 } 47 if (await Git.hasUnstagedChangesAsync()) { 48 logger.error(` Repository contains unstaged changes, please make sure to have it clear.`); 49 logger.error(` If you want to include them, they must be committed.`); 50 return Task.STOP; 51 } 52 } 53); 54 55/** 56 * Checks whether the command is run on main branch or package side-branch. 57 * Otherwise, it prompts to confirm that you know what you're doing. 58 * On CI it returns `true` only if run on `main` branch. 59 */ 60async function checkBranchNameAsync(branchName: string) { 61 if (process.env.CI) { 62 // CI is allowed to publish only from main. 63 return branchName === 'main'; 64 } 65 66 // Publishes can be run on `main` or package's side-branches like `expo-package/1.x.x` 67 if (branchName === 'main' || /^[\w\-@]+\/\d+\.(x\.x|\d+\.x)$/.test(branchName)) { 68 return true; 69 } 70 71 logger.warn( 72 '⚠️ ', 73 `It's recommended to publish from ${blue('main')} branch, while you're at ${blue(branchName)}` 74 ); 75 76 const { confirmed } = await inquirer.prompt<{ confirmed: boolean }>([ 77 { 78 type: 'confirm', 79 name: 'confirmed', 80 prefix: yellow('⚠️ '), 81 message: yellow(`Do you want to proceed?`), 82 default: true, 83 }, 84 ]); 85 logger.log(); 86 return confirmed; 87} 88