1import chalk from 'chalk';
2
3import Git from '../../Git';
4import logger from '../../Logger';
5import { Task } from '../../TasksRunner';
6import { CommandOptions, Parcel, TaskArgs } from '../types';
7import { selectPackagesToPublish } from './selectPackagesToPublish';
8
9const { blue } = chalk;
10
11/**
12 * Commits staged changes made by all previous tasks.
13 */
14export const commitStagedChanges = new Task<TaskArgs>(
15  {
16    name: 'commitStagedChanges',
17    dependsOn: [selectPackagesToPublish],
18  },
19  async (parcels: Parcel[], options: CommandOptions) => {
20    const stagedFiles = await Git.getStagedFilesAsync();
21
22    if (stagedFiles.length === 0) {
23      // This may happen if versions have already been updated — manually or by previous publish
24      // that failed after committing and pushing to remote. It's safe to just skip this step
25      // and use the current head commit as the publish commit.
26      logger.info(`\n�� Nothing to commit — using previous commit as the publish commit`);
27      return;
28    }
29
30    const commitMessage = commitMessageForOptions(options);
31    const commitDescription = parcels
32      .map(({ pkg, state }) => `${pkg.packageName}@${state.releaseVersion}`)
33      .join('\n');
34
35    logger.info(`\n�� Committing changes with message: ${blue(commitMessage)}`);
36
37    await Git.commitAsync({
38      title: commitMessage,
39      body: commitDescription,
40    });
41  }
42);
43
44/**
45 * If commit message was provided as an option then it's returned.
46 * Otherwise it is auto-generated based on provided package names.
47 */
48function commitMessageForOptions(options: CommandOptions): string {
49  if (options.commitMessage) {
50    return options.commitMessage;
51  }
52  if (0 < options.packageNames.length && options.packageNames.length < 4) {
53    return `Publish ${options.packageNames.join(', ')}`;
54  }
55  return 'Publish packages';
56}
57