1import chalk from 'chalk'; 2import inquirer from 'inquirer'; 3 4import { UNPUBLISHED_VERSION_NAME } from '../../Changelogs'; 5import { link } from '../../Formatter'; 6import * as GitHub from '../../GitHub'; 7import logger from '../../Logger'; 8import { Task } from '../../TasksRunner'; 9import { runWithSpinner } from '../../Utils'; 10import { Parcel, TaskArgs } from '../types'; 11import { selectPackagesToPublish } from './selectPackagesToPublish'; 12 13// https://github.com/expo/expo/pulls?q=label:published 14const PUBLISHED_LABEL_NAME = 'published'; 15 16/** 17 * Adds "published" label to pull requests mentioned in changelog entries. 18 */ 19export const addPublishedLabelToPullRequests = new Task<TaskArgs>( 20 { 21 name: 'addPublishedLabelToPullRequests', 22 dependsOn: [selectPackagesToPublish], 23 }, 24 async (parcels: Parcel[]) => { 25 if (!process.env.GITHUB_TOKEN) { 26 logger.error( 27 'Environment variable `%s` must be set to add labels to pull requests', 28 chalk.magenta('GITHUB_TOKEN') 29 ); 30 return; 31 } 32 33 const pullRequestIds: number[] = []; 34 35 // Find all pull requests mentioned in changelogs 36 for (const { state } of parcels) { 37 const versionChanges = state.changelogChanges?.versions[UNPUBLISHED_VERSION_NAME]; 38 39 if (!versionChanges) { 40 continue; 41 } 42 for (const entry of Object.values(versionChanges).flat()) { 43 const { pullRequests } = entry; 44 if (pullRequests && pullRequests.length > 0) { 45 pullRequestIds.push(...pullRequests); 46 } 47 } 48 } 49 50 if (pullRequestIds.length === 0) { 51 return; 52 } 53 const pullRequestIdsSet = new Set<number>(pullRequestIds); 54 const pullRequestsToLabel: GitHub.PullRequest[] = []; 55 56 logger.info(`\n List of published pull requests (${pullRequestIdsSet.size}):`); 57 58 // Request and log all published pull requests 59 for (const pullRequestId of pullRequestIdsSet) { 60 const pr = await GitHub.getPullRequestAsync(pullRequestId, true); 61 62 logger.log(`${linkToPullRequest(pr)}: ${chalk.bold(pr.title)}`); 63 pullRequestsToLabel.push(pr); 64 } 65 66 // Ask whether to continue adding the label to pull requests logged above 67 if (!(await shouldLabelPullRequestsAsync())) { 68 return; 69 } 70 71 await runWithSpinner('Adding the label...', async (spinner) => { 72 // Finally add the label to each pull request 73 for (const pullRequest of pullRequestsToLabel) { 74 const hasLabel = pullRequest.labels.some((label) => label.name === PUBLISHED_LABEL_NAME); 75 76 if (!hasLabel) { 77 await GitHub.addIssueLabelsAsync(pullRequest.number, [PUBLISHED_LABEL_NAME]); 78 } 79 } 80 spinner.succeed('Added the published label'); 81 }); 82 83 logger.log(); 84 } 85); 86 87function linkToPullRequest(pr: GitHub.PullRequest): string { 88 return link(chalk.blue('#' + pr.number), pr.html_url); 89} 90 91/** 92 * Prompts the user whether to add the label to pull requests. 93 */ 94async function shouldLabelPullRequestsAsync(): Promise<boolean> { 95 if (process.env.CI) { 96 return true; 97 } 98 const { proceed } = await inquirer.prompt<{ proceed: boolean }>([ 99 { 100 type: 'confirm', 101 name: 'proceed', 102 prefix: '❔', 103 message: chalk.yellow( 104 `Do you want to add '${chalk.magenta(PUBLISHED_LABEL_NAME)}' label to these pull requests?` 105 ), 106 default: true, 107 }, 108 ]); 109 return proceed; 110} 111