1 | 'use strict';
|
2 | const inquirer = require('inquirer');
|
3 | const chalk = require('chalk');
|
4 | const githubUrlFromGit = require('github-url-from-git');
|
5 | const isScoped = require('is-scoped');
|
6 | const util = require('./util');
|
7 | const git = require('./git-util');
|
8 | const {prereleaseTags, checkIgnoreStrategy} = require('./npm/util');
|
9 | const version = require('./version');
|
10 | const prettyVersionDiff = require('./pretty-version-diff');
|
11 |
|
12 | const printCommitLog = async repoUrl => {
|
13 | const latest = await git.latestTagOrFirstCommit();
|
14 | const log = await git.commitLogFromRevision(latest);
|
15 |
|
16 | if (!log) {
|
17 | return {
|
18 | hasCommits: false,
|
19 | releaseNotes: () => {}
|
20 | };
|
21 | }
|
22 |
|
23 | const commits = log.split('\n')
|
24 | .map(commit => {
|
25 | const splitIndex = commit.lastIndexOf(' ');
|
26 | return {
|
27 | message: commit.slice(0, splitIndex),
|
28 | id: commit.slice(splitIndex + 1)
|
29 | };
|
30 | });
|
31 |
|
32 | const history = commits.map(commit => {
|
33 | const commitMessage = util.linkifyIssues(repoUrl, commit.message);
|
34 | const commitId = util.linkifyCommit(repoUrl, commit.id);
|
35 | return `- ${commitMessage} ${commitId}`;
|
36 | }).join('\n');
|
37 |
|
38 | const releaseNotes = nextTag => commits.map(commit =>
|
39 | `- ${commit.message} ${commit.id}`
|
40 | ).join('\n') + `\n\n${repoUrl}/compare/${latest}...${nextTag}`;
|
41 |
|
42 | const commitRange = util.linkifyCommitRange(repoUrl, `${latest}...master`);
|
43 |
|
44 | console.log(`${chalk.bold('Commits:')}\n${history}\n\n${chalk.bold('Commit Range:')}\n${commitRange}\n`);
|
45 |
|
46 | return {
|
47 | hasCommits: true,
|
48 | releaseNotes
|
49 | };
|
50 | };
|
51 |
|
52 | module.exports = async (options, pkg) => {
|
53 | const oldVersion = pkg.version;
|
54 | const extraBaseUrls = ['gitlab.com'];
|
55 | const repoUrl = pkg.repository && githubUrlFromGit(pkg.repository.url, {extraBaseUrls});
|
56 |
|
57 | checkIgnoreStrategy(pkg);
|
58 |
|
59 | console.log(`\nPublish a new version of ${chalk.bold.magenta(pkg.name)} ${chalk.dim(`(current: ${oldVersion})`)}\n`);
|
60 |
|
61 | const prompts = [
|
62 | {
|
63 | type: 'list',
|
64 | name: 'version',
|
65 | message: 'Select semver increment or specify new version',
|
66 | pageSize: version.SEMVER_INCREMENTS.length + 2,
|
67 | choices: version.SEMVER_INCREMENTS
|
68 | .map(inc => ({
|
69 | name: `${inc} ${prettyVersionDiff(oldVersion, inc)}`,
|
70 | value: inc
|
71 | }))
|
72 | .concat([
|
73 | new inquirer.Separator(),
|
74 | {
|
75 | name: 'Other (specify)',
|
76 | value: null
|
77 | }
|
78 | ]),
|
79 | filter: input => version.isValidInput(input) ? version(oldVersion).getNewVersionFrom(input) : input
|
80 | },
|
81 | {
|
82 | type: 'input',
|
83 | name: 'version',
|
84 | message: 'Version',
|
85 | when: answers => !answers.version,
|
86 | filter: input => version.isValidInput(input) ? version(pkg.version).getNewVersionFrom(input) : input,
|
87 | validate: input => {
|
88 | if (!version.isValidInput(input)) {
|
89 | return 'Please specify a valid semver, for example, `1.2.3`. See http://semver.org';
|
90 | }
|
91 |
|
92 | if (version(oldVersion).isLowerThanOrEqualTo(input)) {
|
93 | return `Version must be greater than ${oldVersion}`;
|
94 | }
|
95 |
|
96 | return true;
|
97 | }
|
98 | },
|
99 | {
|
100 | type: 'list',
|
101 | name: 'tag',
|
102 | message: 'How should this pre-release version be tagged in npm?',
|
103 | when: answers => !pkg.private && version.isPrereleaseOrIncrement(answers.version) && !options.tag,
|
104 | choices: async () => {
|
105 | const existingPrereleaseTags = await prereleaseTags(pkg.name);
|
106 |
|
107 | return [
|
108 | ...existingPrereleaseTags,
|
109 | new inquirer.Separator(),
|
110 | {
|
111 | name: 'Other (specify)',
|
112 | value: null
|
113 | }
|
114 | ];
|
115 | }
|
116 | },
|
117 | {
|
118 | type: 'input',
|
119 | name: 'tag',
|
120 | message: 'Tag',
|
121 | when: answers => !pkg.private && version.isPrereleaseOrIncrement(answers.version) && !options.tag && !answers.tag,
|
122 | validate: input => {
|
123 | if (input.length === 0) {
|
124 | return 'Please specify a tag, for example, `next`.';
|
125 | }
|
126 |
|
127 | if (input.toLowerCase() === 'latest') {
|
128 | return 'It\'s not possible to publish pre-releases under the `latest` tag. Please specify something else, for example, `next`.';
|
129 | }
|
130 |
|
131 | return true;
|
132 | }
|
133 | },
|
134 | {
|
135 | type: 'confirm',
|
136 | name: 'publishScoped',
|
137 | when: isScoped(pkg.name) && !options.exists && options.publish && !pkg.private,
|
138 | message: `This scoped repo ${chalk.bold.magenta(pkg.name)} hasn't been published. Do you want to publish it publicly?`,
|
139 | default: false
|
140 | }
|
141 | ];
|
142 |
|
143 | const {hasCommits, releaseNotes} = await printCommitLog(repoUrl);
|
144 |
|
145 | if (options.version) {
|
146 | return {
|
147 | ...options,
|
148 | confirm: true,
|
149 | repoUrl,
|
150 | releaseNotes
|
151 | };
|
152 | }
|
153 |
|
154 | if (!hasCommits) {
|
155 | const answers = await inquirer.prompt([{
|
156 | type: 'confirm',
|
157 | name: 'confirm',
|
158 | message: 'No commits found since previous release, continue?',
|
159 | default: false
|
160 | }]);
|
161 |
|
162 | if (!answers.confirm) {
|
163 | return {
|
164 | ...options,
|
165 | ...answers
|
166 | };
|
167 | }
|
168 | }
|
169 |
|
170 | const answers = await inquirer.prompt(prompts);
|
171 |
|
172 | return {
|
173 | ...options,
|
174 | ...answers,
|
175 | confirm: true,
|
176 | repoUrl,
|
177 | releaseNotes
|
178 | };
|
179 | };
|