release-it 14.1.0-next.0 → 14.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -241,8 +241,8 @@ The "Releases" tab on GitHub projects links to a page to store the changelog cq.
241
241
  [GitHub releases](https://help.github.com/articles/creating-releases) in your release-it flow:
242
242
 
243
243
  - Configure `github.release: true`
244
- - Obtain a [personal access token](https://github.com/settings/tokens) (release-it only needs "repo" access; no "admin"
245
- or other scopes).
244
+ - Obtain a [personal access token](https://github.com/settings/tokens/new?scopes=repo&description=release-it)
245
+ (release-it only needs "repo" access; no "admin" or other scopes).
246
246
  - Make sure the token is [available as an environment variable](./docs/environment-variables.md).
247
247
 
248
248
  → See [GitHub Releases](./docs/github-releases.md) for more details.
@@ -49,7 +49,8 @@ class GitBase extends Plugin {
49
49
 
50
50
  bump(version) {
51
51
  const { tagTemplate } = this.getContext();
52
- const tagName = format(tagTemplate, { version }) || version;
52
+ const context = Object.assign(this.config.getContext(), { version });
53
+ const tagName = format(tagTemplate, context) || version;
53
54
  this.setContext({ version, tagName });
54
55
  this.config.setContext({ tagName });
55
56
  }
@@ -30,6 +30,7 @@ class Plugin {
30
30
  getName() {}
31
31
  getLatestVersion() {}
32
32
  getChangelog() {}
33
+ getIncrement() {}
33
34
  getIncrementedVersionCI() {}
34
35
  getIncrementedVersion() {}
35
36
  beforeBump() {}
@@ -57,9 +57,11 @@ class Version extends Plugin {
57
57
  return '0.0.0';
58
58
  }
59
59
 
60
+ getIncrement(options) {
61
+ return options.increment;
62
+ }
63
+
60
64
  getIncrementedVersionCI(options) {
61
- const { isCI } = this.config;
62
- options.increment = options.increment == null && isCI ? 'patch' : options.increment;
63
65
  return this.incrementVersion(options);
64
66
  }
65
67
 
@@ -103,12 +105,15 @@ class Version extends Plugin {
103
105
  return increment;
104
106
  }
105
107
 
106
- const _isPreRelease = isPreRelease || (increment || '').startsWith('pre');
107
- if (_isPreRelease && latestIsPreRelease) {
108
+ if (isPreRelease && !increment && latestIsPreRelease) {
108
109
  return semver.inc(latestVersion, 'prerelease', preReleaseId);
109
110
  }
110
111
 
111
- const normalizedType = RELEASE_TYPES.includes(increment) && _isPreRelease ? `pre${increment}` : increment;
112
+ if (this.config.isCI && !increment) {
113
+ return semver.inc(latestVersion, 'patch');
114
+ }
115
+
116
+ const normalizedType = RELEASE_TYPES.includes(increment) && isPreRelease ? `pre${increment}` : increment;
112
117
  if (ALL_RELEASE_TYPES.includes(normalizedType)) {
113
118
  return semver.inc(latestVersion, normalizedType, preReleaseId);
114
119
  }
package/lib/tasks.js CHANGED
@@ -68,6 +68,8 @@ const runTasks = async (opts, di) => {
68
68
  const changelog = await reduceUntil(plugins, plugin => plugin.getChangelog(latestVersion));
69
69
 
70
70
  const incrementBase = { latestVersion, increment, isPreRelease, preReleaseId };
71
+ incrementBase.increment = await reduceUntil(plugins, plugin => plugin.getIncrement(incrementBase));
72
+
71
73
  let version = await reduceUntil(plugins, plugin => plugin.getIncrementedVersionCI(incrementBase));
72
74
 
73
75
  config.setContext({ name, latestVersion, version, changelog });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "release-it",
3
- "version": "14.1.0-next.0",
3
+ "version": "14.2.2",
4
4
  "description": "Generic CLI tool to automate versioning and package publishing related tasks.",
5
5
  "keywords": [
6
6
  "build",
@@ -56,18 +56,18 @@
56
56
  "license": "MIT",
57
57
  "dependencies": {
58
58
  "@iarna/toml": "2.2.5",
59
- "@octokit/rest": "18.0.6",
59
+ "@octokit/rest": "18.0.9",
60
60
  "async-retry": "1.3.1",
61
61
  "chalk": "4.1.0",
62
62
  "cosmiconfig": "7.0.0",
63
- "debug": "4.1.1",
63
+ "debug": "4.3.1",
64
64
  "deprecated-obj": "2.0.0",
65
- "execa": "4.0.3",
65
+ "execa": "4.1.0",
66
66
  "find-up": "5.0.0",
67
67
  "form-data": "3.0.0",
68
- "git-url-parse": "11.2.0",
68
+ "git-url-parse": "11.4.0",
69
69
  "globby": "11.0.1",
70
- "got": "11.6.2",
70
+ "got": "11.8.0",
71
71
  "import-cwd": "3.0.0",
72
72
  "inquirer": "7.3.3",
73
73
  "is-ci": "2.0.0",
@@ -78,29 +78,29 @@
78
78
  "parse-json": "5.1.0",
79
79
  "semver": "7.3.2",
80
80
  "shelljs": "0.8.4",
81
- "update-notifier": "4.1.1",
81
+ "update-notifier": "5.0.1",
82
82
  "url-join": "4.0.1",
83
- "uuid": "8.3.0",
83
+ "uuid": "8.3.1",
84
84
  "yaml": "1.10.0",
85
- "yargs-parser": "20.0.0"
85
+ "yargs-parser": "20.2.4"
86
86
  },
87
87
  "devDependencies": {
88
- "@octokit/request-error": "2.0.2",
89
- "ava": "3.12.1",
90
- "codecov": "3.7.2",
91
- "eslint": "7.9.0",
92
- "eslint-config-prettier": "6.11.0",
88
+ "@octokit/request-error": "2.0.3",
89
+ "ava": "3.13.0",
90
+ "codecov": "3.8.1",
91
+ "eslint": "7.14.0",
92
+ "eslint-config-prettier": "6.15.0",
93
93
  "eslint-plugin-ava": "11.0.0",
94
- "eslint-plugin-import": "2.22.0",
94
+ "eslint-plugin-import": "2.22.1",
95
95
  "eslint-plugin-prettier": "3.1.4",
96
96
  "markdown-toc": "1.2.0",
97
97
  "mock-fs": "4.13.0",
98
98
  "mock-stdio": "1.0.3",
99
- "nock": "13.0.4",
99
+ "nock": "13.0.5",
100
100
  "nyc": "15.1.0",
101
- "prettier": "2.1.1",
101
+ "prettier": "2.2.0",
102
102
  "proxyquire": "2.1.3",
103
- "sinon": "9.0.3",
103
+ "sinon": "9.2.1",
104
104
  "strip-ansi": "6.0.0"
105
105
  },
106
106
  "engines": {
package/test/git.init.js CHANGED
@@ -21,32 +21,32 @@ test.serial('should throw if on wrong branch', async t => {
21
21
  const options = { git: { requireBranch: 'dev' } };
22
22
  const gitClient = factory(Git, { options });
23
23
  sh.exec('git remote remove origin');
24
- await t.throwsAsync(gitClient.init(), null, 'Must be on branch dev');
24
+ await t.throwsAsync(gitClient.init(), { message: /^Must be on branch dev/ });
25
25
  });
26
26
 
27
27
  test.serial('should throw if there is no remote Git url', async t => {
28
28
  const gitClient = factory(Git, { options: { git } });
29
29
  sh.exec('git remote remove origin');
30
- await t.throwsAsync(gitClient.init(), null, 'Could not get remote Git url');
30
+ await t.throwsAsync(gitClient.init(), { message: /^Could not get remote Git url/ });
31
31
  });
32
32
 
33
33
  test.serial('should throw if working dir is not clean', async t => {
34
34
  const gitClient = factory(Git, { options: { git } });
35
35
  sh.exec('rm file');
36
- await t.throwsAsync(gitClient.init(), { message: /Working dir must be clean/ });
36
+ await t.throwsAsync(gitClient.init(), { message: /^Working dir must be clean/ });
37
37
  });
38
38
 
39
39
  test.serial('should throw if no upstream is configured', async t => {
40
40
  const gitClient = factory(Git, { options: { git } });
41
41
  sh.exec('git checkout -b foo');
42
- await t.throwsAsync(gitClient.init(), null, 'No upstream configured for current branch');
42
+ await t.throwsAsync(gitClient.init(), { message: /^No upstream configured for current branch/ });
43
43
  });
44
44
 
45
45
  test.serial('should throw if there are no commits', async t => {
46
46
  const options = { git: { requireCommits: true } };
47
47
  const gitClient = factory(Git, { options });
48
48
  sh.exec('git tag 1.0.0');
49
- await t.throwsAsync(gitClient.init(), null, 'There are no commits since the latest tag');
49
+ await t.throwsAsync(gitClient.init(), { message: /^There are no commits since the latest tag/ });
50
50
  });
51
51
 
52
52
  test.serial('should not throw if there are commits', async t => {
@@ -95,11 +95,12 @@ test.serial('should detect and exclude version prefix (configured)', async t =>
95
95
  });
96
96
 
97
97
  test.serial('should honor custom tagName configuration', async t => {
98
- const gitClient = factory(Git, { options: { git: { tagName: 'TAGNAME-v${version}' } } });
98
+ const gitClient = factory(Git, { options: { git: { tagName: 'TAGNAME-${repo.project}-v${version}' } } });
99
99
  sh.exec('git tag 1.0.0');
100
100
  await gitClient.init();
101
101
  await gitClient.bump('1.0.1');
102
- t.is(gitClient.getContext('tagName'), 'TAGNAME-v1.0.1');
102
+ const { project } = gitClient.getContext('repo');
103
+ t.is(gitClient.getContext('tagName'), `TAGNAME-${project}-v1.0.1`);
103
104
  });
104
105
 
105
106
  test.serial('should get the latest tag after fetch', async t => {
package/test/github.js CHANGED
@@ -24,7 +24,7 @@ test.serial('should validate token', async t => {
24
24
  delete process.env[tokenRef];
25
25
 
26
26
  await t.throwsAsync(github.init(), {
27
- message: /Environment variable "MY_GITHUB_TOKEN" is required for GitHub releases/
27
+ message: /^Environment variable "MY_GITHUB_TOKEN" is required for GitHub releases/
28
28
  });
29
29
  process.env[tokenRef] = '123'; // eslint-disable-line require-atomic-updates
30
30
 
@@ -193,11 +193,9 @@ test('should throw for unauthenticated user', async t => {
193
193
  const stub = sinon.stub(github.client.users, 'getAuthenticated');
194
194
  stub.throws(new RequestError('Bad credentials', 401, { request: { url: '', headers: {} } }));
195
195
 
196
- await t.throwsAsync(
197
- runTasks(github),
198
- null,
199
- 'Could not authenticate with GitHub using environment variable "GITHUB_TOKEN". Generate a new token at https://github.com/settings/tokens'
200
- );
196
+ await t.throwsAsync(runTasks(github), {
197
+ message: /^Could not authenticate with GitHub using environment variable "GITHUB_TOKEN"/
198
+ });
201
199
 
202
200
  t.is(stub.callCount, 1);
203
201
  stub.restore();
@@ -210,7 +208,7 @@ test('should throw for non-collaborator', async t => {
210
208
  const stub = sinon.stub(github.client.repos, 'checkCollaborator');
211
209
  stub.throws(new RequestError('HttpError', 401, { request: { url: '', headers: {} } }));
212
210
 
213
- await t.throwsAsync(runTasks(github), null, 'User john is not a collaborator for user/repo.');
211
+ await t.throwsAsync(runTasks(github), { message: /^User john is not a collaborator for user\/repo/ });
214
212
 
215
213
  stub.restore();
216
214
  });
@@ -243,7 +241,7 @@ test('should handle octokit client error (without retries)', async t => {
243
241
  interceptAuthentication();
244
242
  interceptCollaborator();
245
243
 
246
- await t.throwsAsync(runTasks(github), null, '404 (Not found)');
244
+ await t.throwsAsync(runTasks(github), { message: /^404 \(Not found\)/ });
247
245
 
248
246
  t.is(stub.callCount, 1);
249
247
  stub.restore();
@@ -257,7 +255,7 @@ test('should handle octokit client error (with retries)', async t => {
257
255
  interceptAuthentication();
258
256
  interceptCollaborator();
259
257
 
260
- await t.throwsAsync(runTasks(github), null, '500 (Request failed)');
258
+ await t.throwsAsync(runTasks(github), { message: /^500 \(Request failed\)/ });
261
259
 
262
260
  t.is(stub.callCount, 3);
263
261
  stub.restore();
package/test/gitlab.js CHANGED
@@ -20,7 +20,9 @@ test.serial('should validate token', async t => {
20
20
  const gitlab = factory(GitLab, { options });
21
21
  delete process.env[tokenRef];
22
22
 
23
- await t.throwsAsync(gitlab.init(), null, 'Environment variable "MY_GITLAB_TOKEN" is required for GitLab releases');
23
+ await t.throwsAsync(gitlab.init(), {
24
+ message: /^Environment variable "MY_GITLAB_TOKEN" is required for GitLab releases/
25
+ });
24
26
  process.env[tokenRef] = '123'; // eslint-disable-line require-atomic-updates
25
27
 
26
28
  interceptUser();
@@ -115,11 +117,9 @@ test.serial('should throw for unauthenticated user', async t => {
115
117
  const scope = nock(host);
116
118
  scope.get(`/api/v4/user`).reply(401);
117
119
 
118
- await t.throwsAsync(
119
- runTasks(gitlab),
120
- null,
121
- 'Could not authenticate with GitLab using environment variable "GITLAB_TOKEN".'
122
- );
120
+ await t.throwsAsync(runTasks(gitlab), {
121
+ message: /^Could not authenticate with GitLab using environment variable "GITLAB_TOKEN"/
122
+ });
123
123
  });
124
124
 
125
125
  test.serial('should throw for non-collaborator', async t => {
@@ -131,7 +131,7 @@ test.serial('should throw for non-collaborator', async t => {
131
131
  scope.get(`/api/v4/projects/john%2Frepo/members/all/1`).reply(200, { username: 'emma' });
132
132
  interceptUser({ owner: 'john' });
133
133
 
134
- await t.throwsAsync(runTasks(gitlab), null, 'User john is not a collaborator for john/repo.');
134
+ await t.throwsAsync(runTasks(gitlab), { message: /^User john is not a collaborator for john\/repo/ });
135
135
  });
136
136
 
137
137
  test.serial('should throw for insufficient access level', async t => {
@@ -143,7 +143,7 @@ test.serial('should throw for insufficient access level', async t => {
143
143
  scope.get(`/api/v4/projects/john%2Frepo/members/all/1`).reply(200, { username: 'john', access_level: 10 });
144
144
  interceptUser({ owner: 'john' });
145
145
 
146
- await t.throwsAsync(runTasks(gitlab), null, 'User john is not a collaborator for john/repo.');
146
+ await t.throwsAsync(runTasks(gitlab), { message: /^User john is not a collaborator for john\/repo/ });
147
147
  });
148
148
 
149
149
  test.serial('should fallback for gitlab < v12.4', async t => {
package/test/npm.js CHANGED
@@ -129,7 +129,7 @@ test('should throw if npm is down', async t => {
129
129
  const npmClient = factory(npm);
130
130
  const exec = sinon.stub(npmClient.shell, 'exec').resolves();
131
131
  exec.withArgs('npm ping').rejects();
132
- await t.throwsAsync(runTasks(npmClient), { message: /Unable to reach npm registry/ });
132
+ await t.throwsAsync(runTasks(npmClient), { message: /^Unable to reach npm registry/ });
133
133
  exec.restore();
134
134
  });
135
135
 
@@ -177,7 +177,7 @@ test('should throw if user is not authenticated', async t => {
177
177
  const npmClient = factory(npm);
178
178
  const exec = sinon.stub(npmClient.shell, 'exec').resolves();
179
179
  exec.withArgs('npm whoami').rejects();
180
- await t.throwsAsync(runTasks(npmClient), { message: /Not authenticated with npm/ });
180
+ await t.throwsAsync(runTasks(npmClient), { message: /^Not authenticated with npm/ });
181
181
  exec.restore();
182
182
  });
183
183
 
@@ -186,7 +186,7 @@ test('should throw if user is not a collaborator', async t => {
186
186
  const exec = sinon.stub(npmClient.shell, 'exec').resolves();
187
187
  exec.withArgs('npm whoami').resolves('ada');
188
188
  exec.withArgs('npm access ls-collaborators release-it').resolves(JSON.stringify({ john: ['write'] }));
189
- await t.throwsAsync(runTasks(npmClient), { message: /User ada is not a collaborator for release-it/ });
189
+ await t.throwsAsync(runTasks(npmClient), { message: /^User ada is not a collaborator for release-it/ });
190
190
  exec.restore();
191
191
  });
192
192
 
package/test/plugins.js CHANGED
@@ -1,3 +1,4 @@
1
+ const path = require('path');
1
2
  const test = require('ava');
2
3
  const sh = require('shelljs');
3
4
  const proxyquire = require('proxyquire');
@@ -94,6 +95,7 @@ test.serial('should instantiate plugins and execute all release-cycle methods',
94
95
  'init',
95
96
  'getName',
96
97
  'getLatestVersion',
98
+ 'getIncrement',
97
99
  'getIncrementedVersionCI',
98
100
  'beforeBump',
99
101
  'bump',
@@ -105,7 +107,8 @@ test.serial('should instantiate plugins and execute all release-cycle methods',
105
107
  t.is(myLocalPlugin[method].callCount, 1);
106
108
  });
107
109
 
108
- const incrementBase = { latestVersion: '0.0.0', increment: 'patch', isPreRelease: false, preReleaseId: undefined };
110
+ const incrementBase = { latestVersion: '0.0.0', increment: undefined, isPreRelease: false, preReleaseId: undefined };
111
+ t.deepEqual(myPlugin.getIncrement.firstCall.args[0], incrementBase);
109
112
  t.deepEqual(myPlugin.getIncrementedVersionCI.firstCall.args[0], incrementBase);
110
113
  t.deepEqual(myLocalPlugin.getIncrementedVersionCI.firstCall.args[0], incrementBase);
111
114
  t.is(myPlugin.bump.firstCall.args[0], '0.0.1');
@@ -129,3 +132,69 @@ test.serial('should disable core plugins', async t => {
129
132
  version: undefined
130
133
  });
131
134
  });
135
+
136
+ test.serial('should expose context to execute commands', async t => {
137
+ const { bare } = t.context;
138
+ const latestVersion = '1.0.0';
139
+ const project = path.basename(bare);
140
+ const pkgName = 'plugin-context';
141
+ const owner = path.basename(path.dirname(bare));
142
+ gitAdd(`{"name":"${pkgName}","version":"${latestVersion}"}`, 'package.json', 'Add package.json');
143
+
144
+ class MyPlugin extends Plugin {
145
+ init() {
146
+ this.exec('echo ${version.isPreRelease}');
147
+ }
148
+ beforeBump() {
149
+ const context = this.config.getContext();
150
+ t.is(context.name, pkgName);
151
+ this.exec('echo ${name} ${repo.owner} ${repo.project} ${latestVersion} ${version}');
152
+ }
153
+ bump() {
154
+ const repo = this.config.getContext('repo');
155
+ t.is(repo.owner, owner);
156
+ t.is(repo.project, project);
157
+ t.is(repo.repository, `${owner}/${project}`);
158
+ this.exec('echo ${name} ${repo.owner} ${repo.project} ${latestVersion} ${version}');
159
+ }
160
+ beforeRelease() {
161
+ const context = this.config.getContext();
162
+ t.is(context.name, pkgName);
163
+ this.exec('echo ${name} ${repo.owner} ${repo.project} ${latestVersion} ${version} ${tagName}');
164
+ }
165
+ release() {
166
+ const context = this.config.getContext();
167
+ t.is(context.latestVersion, latestVersion);
168
+ t.is(context.version, '1.0.1');
169
+ this.exec('echo ${name} ${repo.owner} ${repo.project} ${latestVersion} ${version} ${tagName}');
170
+ }
171
+ afterRelease() {
172
+ const context = this.config.getContext();
173
+ t.is(context.tagName, '1.0.1');
174
+ this.exec('echo ${name} ${repo.owner} ${repo.project} ${latestVersion} ${version} ${tagName}');
175
+ }
176
+ }
177
+ const statics = { isEnabled: () => true, disablePlugin: () => null };
178
+ const options = { '@global': true, '@noCallThru': true };
179
+ const runTasks = proxyquire('../lib/tasks', {
180
+ 'my-plugin': Object.assign(MyPlugin, statics, options)
181
+ });
182
+
183
+ const container = getContainer({ plugins: { 'my-plugin': {} } });
184
+ const exec = sinon.spy(container.shell, 'execFormattedCommand');
185
+
186
+ await runTasks({}, container);
187
+
188
+ const pluginExecArgs = exec.args
189
+ .map(args => args[0])
190
+ .filter(arg => typeof arg === 'string' && arg.startsWith('echo'));
191
+
192
+ t.deepEqual(pluginExecArgs, [
193
+ 'echo false',
194
+ `echo ${pkgName} ${owner} ${project} ${latestVersion} 1.0.1`,
195
+ `echo ${pkgName} ${owner} ${project} ${latestVersion} 1.0.1`,
196
+ `echo ${pkgName} ${owner} ${project} ${latestVersion} 1.0.1 1.0.1`,
197
+ `echo ${pkgName} ${owner} ${project} ${latestVersion} 1.0.1 1.0.1`,
198
+ `echo ${pkgName} ${owner} ${project} ${latestVersion} 1.0.1 1.0.1`
199
+ ]);
200
+ });
@@ -1,7 +1,7 @@
1
1
  const debug = require('debug')('release-it:shell-stub');
2
2
  const Shell = require('../../lib/shell');
3
3
 
4
- module.exports = class ShellStub extends Shell {
4
+ class ShellStub extends Shell {
5
5
  exec(command) {
6
6
  if (/^(npm (ping|publish|show)|git fetch)/.test(command)) {
7
7
  debug(command);
@@ -17,4 +17,6 @@ module.exports = class ShellStub extends Shell {
17
17
  }
18
18
  return super.exec(...arguments);
19
19
  }
20
- };
20
+ }
21
+
22
+ module.exports = ShellStub;
package/test/tasks.js CHANGED
@@ -398,22 +398,11 @@ test.serial('should propagate errors', async t => {
398
398
  });
399
399
 
400
400
  {
401
- const myPlugin = sandbox.createStubInstance(Plugin);
402
- myPlugin.namespace = 'my-plugin';
403
- const MyPlugin = sandbox.stub().callsFake(() => myPlugin);
404
- const myLocalPlugin = sandbox.createStubInstance(Plugin);
405
- const MyLocalPlugin = sandbox.stub().callsFake(() => myLocalPlugin);
406
- const replacePlugin = sandbox.createStubInstance(Plugin);
407
- const ReplacePlugin = sandbox.stub().callsFake(() => replacePlugin);
408
-
401
+ class MyPlugin extends Plugin {}
409
402
  const statics = { isEnabled: () => true, disablePlugin: () => null };
410
403
  const options = { '@global': true, '@noCallThru': true };
411
404
  const runTasks = proxyquire('../lib/tasks', {
412
- 'my-plugin': Object.assign(MyPlugin, statics, options),
413
- '/my/plugin': Object.assign(MyLocalPlugin, statics, options),
414
- 'replace-plugin': Object.assign(ReplacePlugin, statics, options, {
415
- disablePlugin: () => ['version', 'git']
416
- })
405
+ 'my-plugin': Object.assign(MyPlugin, statics, options)
417
406
  });
418
407
 
419
408
  test.serial('should run all hooks', async t => {
@@ -421,9 +410,9 @@ test.serial('should propagate errors', async t => {
421
410
  const hooks = {};
422
411
  ['before', 'after'].forEach(prefix => {
423
412
  ['version', 'git', 'npm', 'my-plugin'].forEach(ns => {
424
- ['init', 'beforeBump', 'bump', 'beforeRelease', 'release', 'afterRelease'].forEach(lifecycle => {
425
- hooks[`${prefix}:${lifecycle}`] = `echo ${prefix}:${lifecycle}`;
426
- hooks[`${prefix}:${ns}:${lifecycle}`] = `echo ${prefix}:${ns}:${lifecycle}`;
413
+ ['init', 'beforeBump', 'bump', 'beforeRelease', 'release', 'afterRelease'].forEach(cycle => {
414
+ hooks[`${prefix}:${cycle}`] = `echo ${prefix}:${cycle}`;
415
+ hooks[`${prefix}:${ns}:${cycle}`] = `echo ${prefix}:${ns}:${cycle}`;
427
416
  });
428
417
  });
429
418
  });
@@ -30,19 +30,39 @@ module.exports.factory = (Definition, { namespace, options = {}, container = {}
30
30
  });
31
31
  };
32
32
 
33
+ const getIncrement = (plugin, { latestVersion }) => {
34
+ return (
35
+ plugin.getIncrement({
36
+ latestVersion,
37
+ increment: plugin.options.increment,
38
+ isPreRelease: false,
39
+ preReleaseId: null
40
+ }) ||
41
+ plugin.getContext('increment') ||
42
+ plugin.config.getContext('increment')
43
+ );
44
+ };
45
+
46
+ const getVersion = async (plugin, { latestVersion, increment }) => {
47
+ return (
48
+ (await plugin.getIncrementedVersionCI({ latestVersion, increment })) ||
49
+ (await plugin.getIncrementedVersion({ latestVersion, increment })) ||
50
+ (increment !== false ? semver.inc(latestVersion, increment || 'patch') : latestVersion)
51
+ );
52
+ };
53
+
33
54
  module.exports.runTasks = async plugin => {
34
55
  await plugin.init();
35
56
 
36
57
  const name = (await plugin.getName()) || '__test__';
37
58
  const latestVersion = (await plugin.getLatestVersion()) || '1.0.0';
38
59
  const changelog = (await plugin.getChangelog(latestVersion)) || null;
39
- const increment = plugin.getContext('increment') || plugin.config.getContext('increment');
60
+ const increment = getIncrement(plugin, { latestVersion });
61
+
40
62
  plugin.config.setContext({ name, latestVersion, latestTag: latestVersion, changelog });
41
63
 
42
- const version =
43
- plugin.getIncrementedVersionCI({ latestVersion, increment }) ||
44
- (await plugin.getIncrementedVersion({ latestVersion, increment })) ||
45
- (increment !== false ? semver.inc(latestVersion, increment || 'patch') : latestVersion);
64
+ const version = await getVersion(plugin, { latestVersion, increment });
65
+
46
66
  plugin.config.setContext(parseVersion(version));
47
67
 
48
68
  await plugin.beforeBump();
package/test/version.js CHANGED
@@ -15,11 +15,19 @@ test('isPreRelease', t => {
15
15
  t.is(v.isPreRelease('1.0.0'), false);
16
16
  });
17
17
 
18
+ test('should return the same version in both interactive and ci mode', async t => {
19
+ const v = factory(Version);
20
+ const options = { latestVersion: '2.0.0-beta.1', increment: null, preReleaseId: 'rc', isPreRelease: true };
21
+ const resultInteractiveMode = await v.getIncrementedVersion(options);
22
+ t.is(resultInteractiveMode, '2.0.0-rc.0');
23
+ const resultCiMode = v.getIncrementedVersionCI(options);
24
+ t.is(resultInteractiveMode, resultCiMode);
25
+ });
26
+
18
27
  test('should increment latest version', t => {
19
28
  const v = factory(Version);
20
29
  const latestVersion = '1.0.0';
21
30
  t.is(v.incrementVersion({ latestVersion, increment: false }), '1.0.0');
22
- t.is(v.incrementVersion({ latestVersion, increment: null }), undefined);
23
31
  t.is(v.incrementVersion({ latestVersion, increment: 'foo' }), undefined);
24
32
  t.is(v.incrementVersion({ latestVersion, increment: 'patsj' }), undefined);
25
33
  t.is(v.incrementVersion({ latestVersion, increment: 'a.b.c' }), undefined);
@@ -29,6 +37,22 @@ test('should increment latest version', t => {
29
37
  t.is(v.incrementVersion({ latestVersion, increment: '2.0.0-beta.1' }), '2.0.0-beta.1');
30
38
  });
31
39
 
40
+ test('should not increment latest version in interactive mode', t => {
41
+ const v = factory(Version, { options: { ci: false } });
42
+ const latestVersion = '1.0.0';
43
+ t.is(v.incrementVersion({ latestVersion, increment: null }), undefined);
44
+ t.is(v.incrementVersion({ latestVersion, increment: false }), '1.0.0');
45
+ });
46
+
47
+ test('should always set increment version in CI mode', t => {
48
+ const v = factory(Version, { options: { ci: true } });
49
+ const latestVersion = '1.0.0';
50
+ t.is(v.getIncrementedVersionCI({ latestVersion, increment: false }), '1.0.0');
51
+ t.is(v.getIncrementedVersionCI({ latestVersion, increment: null }), '1.0.1');
52
+ t.is(v.getIncrementedVersionCI({ latestVersion, increment: '1.1.0' }), '1.1.0');
53
+ t.is(v.getIncrementedVersionCI({ latestVersion, increment: 'major' }), '2.0.0');
54
+ });
55
+
32
56
  test('should increment latest version (coerce)', t => {
33
57
  const v = factory(Version);
34
58
  t.is(v.incrementVersion({ increment: '1.2' }), '1.2.0');
@@ -38,7 +62,7 @@ test('should increment latest version (coerce)', t => {
38
62
 
39
63
  test('should increment version (pre-release continuation)', t => {
40
64
  const v = factory(Version);
41
- t.is(v.incrementVersion({ latestVersion: '1.2.3-alpha.0', increment: 'prepatch' }), '1.2.3-alpha.1');
65
+ t.is(v.incrementVersion({ latestVersion: '1.2.3-alpha.0', increment: 'prepatch' }), '1.2.4-0');
42
66
  });
43
67
 
44
68
  test('should increment version (prepatch)', t => {
@@ -54,6 +78,27 @@ test('should increment version (normalized)', t => {
54
78
  );
55
79
  });
56
80
 
81
+ test('should increment version (prepatch on prerelease version)', t => {
82
+ const v = factory(Version);
83
+ t.is(
84
+ v.incrementVersion({ latestVersion: '1.2.3-alpha.5', increment: 'prepatch', preReleaseId: 'next' }),
85
+ '1.2.4-next.0'
86
+ );
87
+ });
88
+
89
+ test('should increment version (normalized on prerelease version)', t => {
90
+ const v = factory(Version);
91
+ t.is(
92
+ v.incrementVersion({
93
+ latestVersion: '1.2.3-alpha.5',
94
+ increment: 'patch',
95
+ preReleaseId: 'next',
96
+ isPreRelease: true
97
+ }),
98
+ '1.2.4-next.0'
99
+ );
100
+ });
101
+
57
102
  test('should increment version (prerelease)', t => {
58
103
  const v = factory(Version);
59
104
  t.is(v.incrementVersion({ latestVersion: '1.2.3', increment: 'prerelease', preReleaseId: 'alpha' }), '1.2.4-alpha.0');
@@ -96,9 +141,19 @@ test('should increment version (patch release after pre-release)', t => {
96
141
  test('should run tasks without errors', async t => {
97
142
  const options = { version: { increment: 'minor' } };
98
143
  const v = factory(Version, { options });
144
+ const getIncrement = sinon.spy(v, 'getIncrement');
99
145
  const getIncrementedVersionCI = sinon.spy(v, 'getIncrementedVersionCI');
100
146
  const incrementVersion = sinon.spy(v, 'incrementVersion');
147
+
101
148
  await runTasks(v);
149
+
150
+ t.is(getIncrement.callCount, 1);
151
+ t.deepEqual(getIncrement.firstCall.args[0], {
152
+ latestVersion: '0.0.0',
153
+ increment: 'minor',
154
+ isPreRelease: false,
155
+ preReleaseId: null
156
+ });
102
157
  t.is(getIncrementedVersionCI.callCount, 1);
103
158
  t.deepEqual(getIncrementedVersionCI.firstCall.args[0], { latestVersion: '0.0.0', increment: 'minor' });
104
159
  t.is(await incrementVersion.firstCall.returnValue, '0.1.0');