Skip to content

Commit 316bed2

Browse files
authored
Add --release-draft-only flag (sindresorhus#578)
1 parent 3466903 commit 316bed2

5 files changed

Lines changed: 107 additions & 37 deletions

File tree

‎readme.md‎

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,19 +50,20 @@ $ np --help
5050
patch | minor | major | prepatch | preminor | premajor | prerelease | 1.2.3
5151
5252
Options
53-
--any-branch Allow publishing from any branch
54-
--branch Name of the release branch (default: master)
55-
--no-cleanup Skips cleanup of node_modules
56-
--no-tests Skips tests
57-
--yolo Skips cleanup and testing
58-
--no-publish Skips publishing
59-
--preview Show tasks without actually executing them
60-
--tag Publish under a given dist-tag
61-
--no-yarn Don't use Yarn
62-
--contents Subdirectory to publish
63-
--no-release-draft Skips opening a GitHub release draft
64-
--test-script Name of npm run script to run tests before publishing (default: test)
65-
--no-2fa Don't enable 2FA on new packages (not recommended)
53+
--any-branch Allow publishing from any branch
54+
--branch Name of the release branch (default: master)
55+
--no-cleanup Skips cleanup of node_modules
56+
--no-tests Skips tests
57+
--yolo Skips cleanup and testing
58+
--no-publish Skips publishing
59+
--preview Show tasks without actually executing them
60+
--tag Publish under a given dist-tag
61+
--no-yarn Don't use Yarn
62+
--contents Subdirectory to publish
63+
--no-release-draft Skips opening a GitHub release draft
64+
--release-draft-only Only opens a GitHub release draft
65+
--test-script Name of npm run script to run tests before publishing (default: test)
66+
--no-2fa Don't enable 2FA on new packages (not recommended)
6667
6768
Examples
6869
$ np

‎source/cli-implementation.js‎

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,20 @@ const cli = meow(`
2121
${version.SEMVER_INCREMENTS.join(' | ')} | 1.2.3
2222
2323
Options
24-
--any-branch Allow publishing from any branch
25-
--branch Name of the release branch (default: master)
26-
--no-cleanup Skips cleanup of node_modules
27-
--no-tests Skips tests
28-
--yolo Skips cleanup and testing
29-
--no-publish Skips publishing
30-
--preview Show tasks without actually executing them
31-
--tag Publish under a given dist-tag
32-
--no-yarn Don't use Yarn
33-
--contents Subdirectory to publish
34-
--no-release-draft Skips opening a GitHub release draft
35-
--test-script Name of npm run script to run tests before publishing (default: test)
36-
--no-2fa Don't enable 2FA on new packages (not recommended)
24+
--any-branch Allow publishing from any branch
25+
--branch Name of the release branch (default: master)
26+
--no-cleanup Skips cleanup of node_modules
27+
--no-tests Skips tests
28+
--yolo Skips cleanup and testing
29+
--no-publish Skips publishing
30+
--preview Show tasks without actually executing them
31+
--tag Publish under a given dist-tag
32+
--no-yarn Don't use Yarn
33+
--contents Subdirectory to publish
34+
--no-release-draft Skips opening a GitHub release draft
35+
--release-draft-only Only opens a GitHub release draft for the latest published version
36+
--test-script Name of npm run script to run tests before publishing (default: test)
37+
--no-2fa Don't enable 2FA on new packages (not recommended)
3738
3839
Examples
3940
$ np
@@ -65,6 +66,9 @@ const cli = meow(`
6566
releaseDraft: {
6667
type: 'boolean'
6768
},
69+
releaseDraftOnly: {
70+
type: 'boolean'
71+
},
6872
tag: {
6973
type: 'string'
7074
},
@@ -113,14 +117,15 @@ updateNotifier({pkg: cli.pkg}).notify();
113117
flags['2fa'] = flags['2Fa'];
114118
}
115119

116-
const runPublish = flags.publish && !pkg.private;
120+
const runPublish = !flags.releaseDraftOnly && flags.publish && !pkg.private;
117121

118122
const availability = flags.publish ? await isPackageNameAvailable(pkg) : {
119123
isAvailable: false,
120124
isUnknown: false
121125
};
122126

123-
const version = cli.input.length > 0 ? cli.input[0] : false;
127+
// Use current (latest) version when 'releaseDraftOnly', otherwise use the first argument.
128+
const version = flags.releaseDraftOnly ? pkg.version : (cli.input.length > 0 ? cli.input[0] : false);
124129

125130
const options = await ui({
126131
...flags,
@@ -136,7 +141,7 @@ updateNotifier({pkg: cli.pkg}).notify();
136141
console.log(); // Prints a newline for readability
137142
const newPkg = await np(options.version, options);
138143

139-
if (options.preview) {
144+
if (options.preview || options.releaseDraftOnly) {
140145
return;
141146
}
142147

‎source/git-util.js‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,21 @@ const firstCommit = async () => {
2929
return stdout;
3030
};
3131

32+
exports.previousTagOrFirstCommit = async () => {
33+
const {stdout} = await execa('git', ['tag']);
34+
const tags = stdout.split('\n');
35+
36+
if (tags.length === 0) {
37+
return;
38+
}
39+
40+
if (tags.length === 1) {
41+
return firstCommit();
42+
}
43+
44+
return tags[tags.length - 2];
45+
};
46+
3247
exports.latestTagOrFirstCommit = async () => {
3348
let latest;
3449
try {

‎source/index.js‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ module.exports = async (input = 'patch', options) => {
5858
const testScript = options.testScript || 'test';
5959
const testCommand = options.testScript ? ['run', testScript] : [testScript];
6060

61+
if (options.releaseDraftOnly) {
62+
await releaseTaskHelper(options, pkg);
63+
return pkg;
64+
}
65+
6166
let publishStatus = 'UNKNOWN';
6267
let pushedObjects;
6368

‎source/ui.js‎

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,26 @@ const {prereleaseTags, checkIgnoreStrategy, getRegistryUrl, isExternalRegistry}
1111
const version = require('./version');
1212
const prettyVersionDiff = require('./pretty-version-diff');
1313

14-
const printCommitLog = async (repoUrl, registryUrl) => {
15-
const latest = await git.latestTagOrFirstCommit();
16-
const log = await git.commitLogFromRevision(latest);
14+
const printCommitLog = async (repoUrl, registryUrl, fromLatestTag) => {
15+
const revision = fromLatestTag ? await git.latestTagOrFirstCommit() : await git.previousTagOrFirstCommit();
16+
if (!revision) {
17+
throw new Error('The package has not been published yet.');
18+
}
19+
20+
const log = await git.commitLogFromRevision(revision);
1721

1822
if (!log) {
1923
return {
2024
hasCommits: false,
25+
hasUnreleasedCommits: false,
2126
releaseNotes: () => {}
2227
};
2328
}
2429

25-
const commits = log.split('\n')
30+
let hasUnreleasedCommits = false;
31+
let commitRangeText = `${revision}...master`;
32+
33+
let commits = log.split('\n')
2634
.map(commit => {
2735
const splitIndex = commit.lastIndexOf(' ');
2836
return {
@@ -31,6 +39,20 @@ const printCommitLog = async (repoUrl, registryUrl) => {
3139
};
3240
});
3341

42+
if (!fromLatestTag) {
43+
const latestTag = await git.latestTag();
44+
const versionBumpCommitName = latestTag.slice(1); // Name v1.0.1 becomes 1.0.1
45+
const versionBumpCommitIndex = commits.findIndex(commit => commit.message === versionBumpCommitName);
46+
47+
if (versionBumpCommitIndex > 0) {
48+
commitRangeText = `${revision}...${latestTag}`;
49+
hasUnreleasedCommits = true;
50+
}
51+
52+
// Get rid of unreleased commits and of the version bump commit.
53+
commits = commits.slice(versionBumpCommitIndex + 1);
54+
}
55+
3456
const history = commits.map(commit => {
3557
const commitMessage = util.linkifyIssues(repoUrl, commit.message);
3658
const commitId = util.linkifyCommit(repoUrl, commit.id);
@@ -39,14 +61,15 @@ const printCommitLog = async (repoUrl, registryUrl) => {
3961

4062
const releaseNotes = nextTag => commits.map(commit =>
4163
`- ${htmlEscape(commit.message)} ${commit.id}`
42-
).join('\n') + `\n\n${repoUrl}/compare/${latest}...${nextTag}`;
64+
).join('\n') + `\n\n${repoUrl}/compare/${revision}...${nextTag}`;
4365

44-
const commitRange = util.linkifyCommitRange(repoUrl, `${latest}...master`);
66+
const commitRange = util.linkifyCommitRange(repoUrl, commitRangeText);
4567

4668
console.log(`${chalk.bold('Commits:')}\n${history}\n\n${chalk.bold('Commit Range:')}\n${commitRange}\n\n${chalk.bold('Registry:')}\n${registryUrl}\n`);
4769

4870
return {
4971
hasCommits: true,
72+
hasUnreleasedCommits,
5073
releaseNotes
5174
};
5275
};
@@ -92,7 +115,11 @@ module.exports = async (options, pkg) => {
92115
}
93116
}
94117

95-
console.log(`\nPublish a new version of ${chalk.bold.magenta(pkg.name)} ${chalk.dim(`(current: ${oldVersion})`)}\n`);
118+
if (options.releaseDraftOnly) {
119+
console.log(`\nCreate a release draft on GitHub for ${chalk.bold.magenta(pkg.name)} ${chalk.dim(`(current: ${oldVersion})`)}\n`);
120+
} else {
121+
console.log(`\nPublish a new version of ${chalk.bold.magenta(pkg.name)} ${chalk.dim(`(current: ${oldVersion})`)}\n`);
122+
}
96123

97124
const prompts = [
98125
{
@@ -176,7 +203,24 @@ module.exports = async (options, pkg) => {
176203
}
177204
];
178205

179-
const {hasCommits, releaseNotes} = await printCommitLog(repoUrl, registryUrl);
206+
const useLatestTag = !options.releaseDraftOnly;
207+
const {hasCommits, hasUnreleasedCommits, releaseNotes} = await printCommitLog(repoUrl, registryUrl, useLatestTag);
208+
209+
if (hasUnreleasedCommits && options.releaseDraftOnly) {
210+
const answers = await inquirer.prompt([{
211+
type: 'confirm',
212+
name: 'confirm',
213+
message: 'Unreleased commits found. They won\'t be included in the release draft. Continue?',
214+
default: false
215+
}]);
216+
217+
if (!answers.confirm) {
218+
return {
219+
...options,
220+
...answers
221+
};
222+
}
223+
}
180224

181225
if (options.version) {
182226
return {

0 commit comments

Comments
 (0)