release-it 15.0.0-esm.2 → 15.0.0-esm.5

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.
Files changed (50) hide show
  1. package/README.md +22 -16
  2. package/config/release-it.json +5 -1
  3. package/lib/config.js +7 -15
  4. package/lib/index.js +13 -21
  5. package/lib/log.js +1 -1
  6. package/lib/plugin/GitBase.js +2 -2
  7. package/lib/plugin/GitRelease.js +1 -1
  8. package/lib/plugin/Plugin.js +1 -1
  9. package/lib/plugin/factory.js +5 -5
  10. package/lib/plugin/git/Git.js +11 -4
  11. package/lib/plugin/github/GitHub.js +59 -15
  12. package/lib/plugin/gitlab/GitLab.js +69 -15
  13. package/lib/plugin/npm/npm.js +3 -2
  14. package/lib/plugin/version/Version.js +5 -5
  15. package/lib/shell.js +3 -3
  16. package/lib/spinner.js +3 -3
  17. package/lib/util.js +2 -2
  18. package/package.json +36 -42
  19. package/test/config.js +21 -28
  20. package/test/git.init.js +32 -7
  21. package/test/git.js +5 -2
  22. package/test/github.js +116 -19
  23. package/test/gitlab.js +69 -18
  24. package/test/log.js +1 -1
  25. package/test/npm.js +12 -3
  26. package/test/plugins.js +34 -22
  27. package/test/spinner.js +8 -11
  28. package/test/stub/config/default/.release-it.json +5 -0
  29. package/test/stub/config/invalid-config-rc +1 -0
  30. package/test/stub/config/invalid-config-txt +2 -0
  31. package/test/stub/config/merge/.release-it.json +5 -0
  32. package/test/stub/config/merge/package.json +7 -0
  33. package/test/stub/config/toml/.release-it.toml +2 -0
  34. package/test/stub/config/yaml/.release-it.yaml +2 -0
  35. package/test/stub/config/yml/.release-it.yml +2 -0
  36. package/test/stub/github.js +26 -10
  37. package/test/stub/gitlab.js +15 -7
  38. package/test/stub/shell.js +2 -2
  39. package/test/tasks.interactive.js +2 -3
  40. package/test/tasks.js +44 -4
  41. package/test/util/helpers.js +6 -6
  42. package/test/util/index.js +13 -18
  43. package/test/utils.js +1 -1
  44. package/test/version.js +14 -9
  45. package/config/.codecov.yml +0 -5
  46. package/config/deprecated.json +0 -1
  47. package/lib/deprecated.js +0 -22
  48. package/lib/metrics.js +0 -76
  49. package/test/deprecated.js +0 -11
  50. package/test/metrics.js +0 -17
@@ -53,10 +53,6 @@ class Version extends Plugin {
53
53
  this.registerPrompts(prompts);
54
54
  }
55
55
 
56
- getLatestVersion() {
57
- return '0.0.0';
58
- }
59
-
60
56
  getIncrement(options) {
61
57
  return options.increment;
62
58
  }
@@ -110,7 +106,11 @@ class Version extends Plugin {
110
106
  }
111
107
 
112
108
  if (this.config.isCI && !increment) {
113
- return semver.inc(latestVersion, 'patch');
109
+ if (isPreRelease) {
110
+ return semver.inc(latestVersion, 'prepatch', preReleaseId);
111
+ } else {
112
+ return semver.inc(latestVersion, 'patch');
113
+ }
114
114
  }
115
115
 
116
116
  const normalizedType = RELEASE_TYPES.includes(increment) && isPreRelease ? `pre${increment}` : increment;
package/lib/shell.js CHANGED
@@ -1,9 +1,9 @@
1
+ import util from 'node:util';
1
2
  import sh from 'shelljs';
2
- import execa from 'execa';
3
- import _debug from 'debug';
3
+ import { execa } from 'execa';
4
4
  import { format } from './util.js';
5
5
 
6
- const debug = _debug('release-it:shell');
6
+ const debug = util.debug('release-it:shell');
7
7
 
8
8
  sh.config.silent = !debug.enabled;
9
9
 
package/lib/spinner.js CHANGED
@@ -1,4 +1,4 @@
1
- import ora from 'ora';
1
+ import { oraPromise } from 'ora';
2
2
  import { format } from './util.js';
3
3
 
4
4
  const noop = Promise.resolve();
@@ -6,7 +6,7 @@ const noop = Promise.resolve();
6
6
  class Spinner {
7
7
  constructor({ container = {} } = {}) {
8
8
  this.config = container.config;
9
- this.ora = container.ora || ora;
9
+ this.ora = container.ora || oraPromise;
10
10
  }
11
11
  show({ enabled = true, task, label, external = false, context }) {
12
12
  if (!enabled) return noop;
@@ -19,7 +19,7 @@ class Spinner {
19
19
 
20
20
  if (!this.isSpinnerDisabled || (external && this.canForce)) {
21
21
  const text = format(label, context);
22
- this.ora.promise(awaitTask, text);
22
+ this.ora(awaitTask, text);
23
23
  }
24
24
 
25
25
  return awaitTask;
package/lib/util.js CHANGED
@@ -1,5 +1,5 @@
1
- import fs from 'fs';
2
- import { EOL } from 'os';
1
+ import fs from 'node:fs';
2
+ import { EOL } from 'node:os';
3
3
  import _ from 'lodash';
4
4
  import gitUrlParse from 'git-url-parse';
5
5
  import semver from 'semver';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "release-it",
3
- "version": "15.0.0-esm.2",
3
+ "version": "15.0.0-esm.5",
4
4
  "description": "Generic CLI tool to automate versioning and package publishing related tasks.",
5
5
  "keywords": [
6
6
  "build",
@@ -49,10 +49,7 @@
49
49
  "scripts": {
50
50
  "lint": "eslint lib test",
51
51
  "format": "prettier --write \"{lib,test}/**/*.js\"",
52
- "test": "ava --timeout=20m",
53
- "coverage": "nyc --reporter=lcov --reporter=html --config=config/.codecov.yml npm test",
54
- "codecov": "nyc --reporter=json --config=config/.codecov.yml npm test && codecov -f coverage/coverage-final.json",
55
- "readme": "markdown-toc README.md -i --maxdepth=2 --bullets=-",
52
+ "test": "ava --no-worker-threads",
56
53
  "release": "./bin/release-it.js"
57
54
  },
58
55
  "author": {
@@ -62,52 +59,49 @@
62
59
  "license": "MIT",
63
60
  "dependencies": {
64
61
  "@iarna/toml": "2.2.5",
65
- "@octokit/rest": "18.6.7",
66
- "async-retry": "1.3.1",
67
- "chalk": "4.1.1",
68
- "cosmiconfig": "7.0.0",
69
- "debug": "4.3.2",
70
- "deprecated-obj": "2.0.0",
71
- "execa": "5.1.1",
72
- "find-up": "5.0.0",
62
+ "@octokit/rest": "18.12.0",
63
+ "async-retry": "1.3.3",
64
+ "chalk": "5.0.1",
65
+ "cosmiconfig": "7.0.1",
66
+ "execa": "6.1.0",
73
67
  "form-data": "4.0.0",
74
- "git-url-parse": "11.5.0",
75
- "globby": "11.0.4",
76
- "got": "11.8.2",
77
- "inquirer": "8.1.1",
78
- "is-ci": "3.0.0",
68
+ "git-url-parse": "11.6.0",
69
+ "globby": "13.1.1",
70
+ "got": "12.0.4",
71
+ "inquirer": "8.2.3",
72
+ "is-ci": "3.0.1",
79
73
  "lodash": "4.17.21",
80
- "mime-types": "2.1.31",
81
- "ora": "5.4.1",
82
- "os-name": "4.0.0",
83
- "parse-json": "5.2.0",
84
- "semver": "7.3.5",
85
- "shelljs": "0.8.4",
74
+ "mime-types": "2.1.35",
75
+ "new-github-release-url": "2.0.0",
76
+ "open": "8.4.0",
77
+ "ora": "6.1.0",
78
+ "os-name": "5.0.1",
79
+ "promise.allsettled": "1.0.5",
80
+ "proxy-agent": "5.0.0",
81
+ "semver": "7.3.7",
82
+ "shelljs": "0.8.5",
86
83
  "update-notifier": "5.1.0",
87
- "url-join": "4.0.1",
88
- "uuid": "8.3.2",
89
- "yaml": "1.10.2",
90
- "yargs-parser": "20.2.9"
84
+ "url-join": "5.0.0",
85
+ "wildcard-match": "5.1.2",
86
+ "yargs-parser": "21.0.1"
91
87
  },
92
88
  "devDependencies": {
93
89
  "@octokit/request-error": "2.1.0",
94
- "ava": "3.15.0",
95
- "codecov": "3.8.2",
96
- "eslint": "7.30.0",
97
- "eslint-config-prettier": "8.3.0",
98
- "eslint-plugin-ava": "12.0.0",
99
- "eslint-plugin-import": "2.23.4",
100
- "eslint-plugin-prettier": "3.4.0",
101
- "markdown-toc": "1.2.0",
102
- "mock-fs": "5.0.0",
90
+ "ava": "4.2.0",
91
+ "eslint": "8.14.0",
92
+ "eslint-config-prettier": "8.5.0",
93
+ "eslint-plugin-ava": "13.2.0",
94
+ "eslint-plugin-import": "2.26.0",
95
+ "eslint-plugin-prettier": "4.0.0",
96
+ "mock-fs": "5.1.2",
103
97
  "mock-stdio": "1.0.3",
104
- "nock": "13.1.1",
98
+ "nock": "13.2.4",
105
99
  "nyc": "15.1.0",
106
- "prettier": "2.3.2",
107
- "sinon": "11.1.1",
108
- "strip-ansi": "6.0.0"
100
+ "prettier": "2.6.2",
101
+ "sinon": "13.0.2",
102
+ "strip-ansi": "7.0.1"
109
103
  },
110
104
  "engines": {
111
- "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
105
+ "node": ">=14.9"
112
106
  }
113
107
  }
package/test/config.js CHANGED
@@ -1,29 +1,30 @@
1
1
  import test from 'ava';
2
- import mock from 'mock-fs';
2
+ import isCI from 'is-ci';
3
3
  import Config from '../lib/config.js';
4
4
  import { readJSON } from '../lib/util.js';
5
5
 
6
6
  const defaultConfig = readJSON(new URL('../config/release-it.json', import.meta.url));
7
+ const projectConfig = readJSON(new URL('../.release-it.json', import.meta.url));
7
8
 
8
9
  const localConfig = { github: { release: true } };
9
10
 
10
- test.afterEach(() => mock.restore()); // eslint-disable-line ava/no-inline-assertions
11
-
12
- test('should contain default values', t => {
13
- mock({ '../.release-it.json': JSON.stringify(localConfig) });
11
+ test("should read this project's own configuration", t => {
14
12
  const config = new Config();
15
13
  t.deepEqual(config.constructorConfig, {});
14
+ t.deepEqual(config.localConfig, projectConfig);
15
+ t.deepEqual(config.defaultConfig, defaultConfig);
16
+ });
17
+
18
+ test('should contain default values', t => {
19
+ const config = new Config({ configDir: './test/stub/config/default' });
20
+ t.deepEqual(config.constructorConfig, { configDir: './test/stub/config/default' });
16
21
  t.deepEqual(config.localConfig, localConfig);
17
22
  t.deepEqual(config.defaultConfig, defaultConfig);
18
- mock.restore();
19
23
  });
20
24
 
21
25
  test('should merge provided options', t => {
22
- mock({
23
- 'package.json': JSON.stringify({ 'release-it': { git: { push: false } } }),
24
- '../.release-it.json': JSON.stringify(localConfig)
25
- });
26
26
  const config = new Config({
27
+ configDir: './test/stub/config/merge',
27
28
  increment: '1.0.0',
28
29
  verbose: true,
29
30
  github: {
@@ -36,7 +37,6 @@ test('should merge provided options', t => {
36
37
  t.is(options.increment, '1.0.0');
37
38
  t.is(options.git.push, false);
38
39
  t.is(options.github.release, true);
39
- mock.restore();
40
40
  });
41
41
 
42
42
  test('should set CI mode', t => {
@@ -44,8 +44,7 @@ test('should set CI mode', t => {
44
44
  t.is(config.isCI, true);
45
45
  });
46
46
 
47
- test('should detect CI mode', async t => {
48
- const { default: isCI } = await import('is-ci');
47
+ test('should detect CI mode', t => {
49
48
  const config = new Config();
50
49
  t.is(config.options.ci, isCI);
51
50
  t.is(config.isCI, isCI);
@@ -57,24 +56,18 @@ test('should override --no-npm.publish', t => {
57
56
  });
58
57
 
59
58
  test('should read YAML config', t => {
60
- mock({ '.release-it.yaml': 'foo:\n bar: 1' });
61
- const config = new Config({ config: '.release-it.yaml' });
59
+ const config = new Config({ configDir: './test/stub/config/yaml' });
62
60
  t.deepEqual(config.options.foo, { bar: 1 });
63
- mock.restore();
64
61
  });
65
62
 
66
63
  test('should read YML config', t => {
67
- mock({ '.release-it.yml': 'foo:\n bar: 1' });
68
- const config = new Config({ config: '.release-it.yml' });
64
+ const config = new Config({ configDir: './test/stub/config/yml' });
69
65
  t.deepEqual(config.options.foo, { bar: 1 });
70
- mock.restore();
71
66
  });
72
67
 
73
68
  test('should read TOML config', t => {
74
- mock({ '.release-it.toml': '[foo]\nbar=1' });
75
- const config = new Config({ config: '.release-it.toml' });
69
+ const config = new Config({ configDir: './test/stub/config/toml' });
76
70
  t.deepEqual(config.options.foo, { bar: 1 });
77
- mock.restore();
78
71
  });
79
72
 
80
73
  test('should throw if provided config file is not found', t => {
@@ -82,15 +75,15 @@ test('should throw if provided config file is not found', t => {
82
75
  });
83
76
 
84
77
  test('should throw if provided config file is invalid (cosmiconfig exception)', t => {
85
- mock({ 'invalid-config-txt': 'foo\nbar\baz' });
86
- t.throws(() => new Config({ config: 'invalid-config-txt' }), { message: /Invalid configuration file at/ });
87
- mock.restore();
78
+ t.throws(() => new Config({ config: './test/stub/config/invalid-config-txt' }), {
79
+ message: /Invalid configuration file at/
80
+ });
88
81
  });
89
82
 
90
83
  test('should throw if provided config file is invalid (no object)', t => {
91
- mock({ 'invalid-config-rc': 'foo=bar' });
92
- t.throws(() => new Config({ config: 'invalid-config-rc' }), { message: /Invalid configuration file at/ });
93
- mock.restore();
84
+ t.throws(() => new Config({ config: './test/stub/config/invalid-config-rc' }), {
85
+ message: /Invalid configuration file at/
86
+ });
94
87
  });
95
88
 
96
89
  test('should not set default increment (for CI mode)', t => {
package/test/git.init.js CHANGED
@@ -26,6 +26,19 @@ test.serial('should throw if on wrong branch', async t => {
26
26
  await t.throwsAsync(gitClient.init(), { message: /^Must be on branch dev/ });
27
27
  });
28
28
 
29
+ test.serial('should not throw if required branch matches', async t => {
30
+ const options = { git: { requireBranch: 'ma?*' } };
31
+ const gitClient = factory(Git, { options });
32
+ await t.notThrowsAsync(gitClient.init());
33
+ });
34
+
35
+ test.serial('should not throw if one of required branch matches', async t => {
36
+ const options = { git: { requireBranch: ['release/*', 'hotfix/*'] } };
37
+ const gitClient = factory(Git, { options });
38
+ sh.exec('git checkout -b release/v1');
39
+ await t.notThrowsAsync(gitClient.init());
40
+ });
41
+
29
42
  test.serial('should throw if there is no remote Git url', async t => {
30
43
  const gitClient = factory(Git, { options: { git } });
31
44
  sh.exec('git remote remove origin');
@@ -143,19 +156,13 @@ test.serial('should get the latest custom tag after fetch when tagName is config
143
156
  test.serial('should get the latest tag based on tagMatch', async t => {
144
157
  const shell = factory(Shell);
145
158
  const gitClient = factory(Git, {
146
- options: { git: { tagMatch: '[0-9][0-9].[0-1][0-9].[0-9]*' } },
159
+ options: { git: { tagMatch: '[0-9][0-9]\\.[0-1][0-9]\\.[0-9]*' } },
147
160
  container: { shell }
148
161
  });
149
- const { bare, target } = t.context;
150
- const other = mkTmpDir();
151
- sh.exec('git push');
152
- sh.exec(`git clone ${bare} ${other}`);
153
- sh.pushd('-q', other);
154
162
  sh.exec('git tag 1.0.0');
155
163
  sh.exec('git tag 21.04.3');
156
164
  sh.exec('git tag 1.0.1');
157
165
  sh.exec('git push --tags');
158
- sh.pushd('-q', target);
159
166
  await gitClient.init();
160
167
  t.is(gitClient.config.getContext('latestTag'), '21.04.3');
161
168
  });
@@ -169,3 +176,21 @@ test.serial('should generate correct changelog', async t => {
169
176
  const changelog = await gitClient.getChangelog();
170
177
  t.regex(changelog, /\* Add file \(\w{7}\)\n\* Add file \(\w{7}\)/);
171
178
  });
179
+
180
+ test.serial('should get the full changelog since latest major tag', async t => {
181
+ const shell = factory(Shell);
182
+ const gitClient = factory(Git, {
183
+ options: { git: { tagMatch: '[0-9]\\.[0-9]\\.[0-9]', changelog: git.changelog } },
184
+ container: { shell }
185
+ });
186
+ sh.exec('git tag 1.0.0');
187
+ gitAdd('line', 'file', 'Add file');
188
+ sh.exec('git tag 2.0.0-rc.0');
189
+ gitAdd('line', 'file', 'Add file');
190
+ sh.exec('git tag 2.0.0-rc.1');
191
+ gitAdd('line', 'file', 'Add file');
192
+ await gitClient.init();
193
+ t.is(gitClient.config.getContext('latestTag'), '1.0.0');
194
+ const changelog = await gitClient.getChangelog();
195
+ t.regex(changelog, /\* Add file \(\w{7}\)\n\* Add file \(\w{7}\)\n\* Add file \(\w{7}\)/);
196
+ });
package/test/git.js CHANGED
@@ -1,4 +1,4 @@
1
- import { EOL } from 'os';
1
+ import { EOL } from 'node:os';
2
2
  import test from 'ava';
3
3
  import sinon from 'sinon';
4
4
  import sh from 'shelljs';
@@ -228,7 +228,10 @@ test.serial('should push to remote name (not "origin")', async t => {
228
228
  gitAdd('line', 'file', 'Add file');
229
229
  await gitClient.push();
230
230
  t.deepEqual(spy.lastCall.args[0], ['git', 'push', '--set-upstream', 'upstream', 'foo']);
231
- t.regex(await spy.lastCall.returnValue, /Branch .?foo.? set up to track remote branch .?foo.? from .?upstream.?/);
231
+ t.regex(
232
+ await spy.lastCall.returnValue,
233
+ /branch .?foo.? set up to track (remote branch .?foo.? from .?upstream.?|.?upstream\/foo.?)/i
234
+ );
232
235
  }
233
236
  spy.restore();
234
237
  });
package/test/github.js CHANGED
@@ -15,18 +15,14 @@ import {
15
15
  const tokenRef = 'GITHUB_TOKEN';
16
16
  const pushRepo = 'git://github.com:user/repo';
17
17
  const host = 'github.com';
18
- const git = { changelog: null };
18
+ const git = { changelog: '' };
19
19
  const requestErrorOptions = { request: { url: '', headers: {} }, response: { headers: {} } };
20
20
 
21
- test.serial('should validate token', async t => {
21
+ test.serial('should check token and perform checks', async t => {
22
22
  const tokenRef = 'MY_GITHUB_TOKEN';
23
23
  const options = { github: { release: true, tokenRef, pushRepo } };
24
24
  const github = factory(GitHub, { options });
25
- delete process.env[tokenRef];
26
25
 
27
- await t.throwsAsync(github.init(), {
28
- message: /^Environment variable "MY_GITHUB_TOKEN" is required for GitHub releases/
29
- });
30
26
  process.env[tokenRef] = '123'; // eslint-disable-line require-atomic-updates
31
27
 
32
28
  interceptAuthentication();
@@ -34,6 +30,18 @@ test.serial('should validate token', async t => {
34
30
  await t.notThrowsAsync(github.init());
35
31
  });
36
32
 
33
+ test.serial('should check token and warn', async t => {
34
+ const tokenRef = 'MY_GITHUB_TOKEN';
35
+ const options = { github: { release: true, tokenRef, pushRepo } };
36
+ const github = factory(GitHub, { options });
37
+ delete process.env[tokenRef];
38
+
39
+ await t.notThrowsAsync(github.init());
40
+
41
+ t.is(github.log.warn.args[0][0], 'Environment variable "MY_GITHUB_TOKEN" is required for automated GitHub Releases.');
42
+ t.is(github.log.warn.args[1][0], 'Falling back to web-based GitHub Release.');
43
+ });
44
+
37
45
  test('should release and upload assets', async t => {
38
46
  const options = {
39
47
  git,
@@ -48,6 +56,7 @@ test('should release and upload assets', async t => {
48
56
  };
49
57
  const github = factory(GitHub, { options });
50
58
  const exec = sinon.stub(github.shell, 'exec').callThrough();
59
+ exec.withArgs('git log --pretty=format:"* %s (%h)" ${from}...${to}').resolves('');
51
60
  exec.withArgs('git describe --tags --match=* --abbrev=0').resolves('2.0.1');
52
61
 
53
62
  interceptAuthentication();
@@ -77,11 +86,39 @@ test('should create a pre-release and draft release notes', async t => {
77
86
  };
78
87
  const github = factory(GitHub, { options });
79
88
  const exec = sinon.stub(github.shell, 'exec').callThrough();
89
+ exec.withArgs('git log --pretty=format:"* %s (%h)" ${from}...${to}').resolves('');
90
+ exec.withArgs('git describe --tags --match=* --abbrev=0').resolves('2.0.1');
91
+
92
+ interceptAuthentication();
93
+ interceptCollaborator();
94
+ interceptCreate({ body: { tag_name: '2.0.2', name: 'Release 2.0.2', prerelease: true, draft: true } });
95
+
96
+ await runTasks(github);
97
+
98
+ const { isReleased, releaseUrl } = github.getContext();
99
+ t.true(isReleased);
100
+ t.is(releaseUrl, 'https://github.com/user/repo/releases/tag/2.0.2');
101
+ exec.restore();
102
+ });
103
+
104
+ test('should create auto-generated release notes', async t => {
105
+ const options = {
106
+ git,
107
+ github: {
108
+ pushRepo,
109
+ tokenRef,
110
+ release: true,
111
+ releaseName: 'Release ${tagName}',
112
+ autoGenerate: true
113
+ }
114
+ };
115
+ const github = factory(GitHub, { options });
116
+ const exec = sinon.stub(github.shell, 'exec').callThrough();
80
117
  exec.withArgs('git describe --tags --match=* --abbrev=0').resolves('2.0.1');
81
118
 
82
119
  interceptAuthentication();
83
120
  interceptCollaborator();
84
- interceptCreate({ body: { tag_name: '2.0.2', name: 'Release 2.0.2', body: null, prerelease: true, draft: true } });
121
+ interceptCreate({ body: { tag_name: '2.0.2', name: 'Release 2.0.2', generate_release_notes: true, body: '' } });
85
122
 
86
123
  await runTasks(github);
87
124
 
@@ -108,9 +145,10 @@ test('should update release and upload assets', async t => {
108
145
  };
109
146
  const github = factory(GitHub, { options });
110
147
  const exec = sinon.stub(github.shell, 'exec').callThrough();
148
+ exec.withArgs('git log --pretty=format:"* %s (%h)" ${from}...${to}').resolves('');
111
149
  exec.withArgs('git describe --tags --match=* --abbrev=0').resolves('2.0.1');
112
- exec.withArgs('git rev-list 2.0.1 --tags --max-count=1').resolves('71f1812');
113
- exec.withArgs('git describe --tags --match=* --abbrev=0 71f1812').resolves('2.0.1');
150
+ exec.withArgs('git rev-list 2.0.1 --tags --max-count=1').resolves('a123456');
151
+ exec.withArgs('git describe --tags --abbrev=0 "a123456^"').resolves('2.0.1');
114
152
 
115
153
  interceptAuthentication();
116
154
  interceptCollaborator();
@@ -141,9 +179,10 @@ test('should create new release for unreleased tag', async t => {
141
179
  };
142
180
  const github = factory(GitHub, { options });
143
181
  const exec = sinon.stub(github.shell, 'exec').callThrough();
182
+ exec.withArgs('git log --pretty=format:"* %s (%h)" ${from}...${to}').resolves('');
144
183
  exec.withArgs('git describe --tags --match=* --abbrev=0').resolves('2.0.1');
145
- exec.withArgs('git rev-list 2.0.1 --tags --max-count=1').resolves('71f1812');
146
- exec.withArgs('git describe --tags --match=* --abbrev=0 71f1812').resolves('2.0.1');
184
+ exec.withArgs('git rev-list 2.0.1 --tags --max-count=1').resolves('b123456');
185
+ exec.withArgs('git describe --tags --abbrev=0 "b123456^"').resolves('2.0.1');
147
186
 
148
187
  interceptAuthentication();
149
188
  interceptCollaborator();
@@ -179,7 +218,7 @@ test('should release to enterprise host', async t => {
179
218
  });
180
219
 
181
220
  test('should release to alternative host and proxy', async t => {
182
- const remote = { api: 'https://my-custom-host.org/api/v3', host: 'my-custom-host.org' };
221
+ const remote = { api: 'https://custom.example.org/api/v3', host: 'custom.example.org' };
183
222
  interceptAuthentication(remote);
184
223
  interceptCollaborator(remote);
185
224
  interceptCreate(Object.assign({ body: { tag_name: '1.0.1' } }, remote));
@@ -187,37 +226,40 @@ test('should release to alternative host and proxy', async t => {
187
226
  git,
188
227
  github: {
189
228
  tokenRef,
190
- pushRepo: `git://my-custom-host.org:user/repo`,
191
- host: 'my-custom-host.org',
229
+ pushRepo: `git://custom.example.org:user/repo`,
230
+ host: 'custom.example.org',
192
231
  proxy: 'http://proxy:8080'
193
232
  }
194
233
  };
195
234
  const github = factory(GitHub, { options });
196
235
  const exec = sinon.stub(github.shell, 'exec').callThrough();
236
+ exec.withArgs('git log --pretty=format:"* %s (%h)" ${from}...${to}').resolves('');
197
237
  exec.withArgs('git describe --tags --match=* --abbrev=0').resolves('1.0.0');
198
238
 
199
239
  await runTasks(github);
200
240
 
201
241
  const { isReleased, releaseUrl } = github.getContext();
202
242
  t.true(isReleased);
203
- t.is(releaseUrl, `https://my-custom-host.org/user/repo/releases/tag/1.0.1`);
243
+ t.is(releaseUrl, `https://custom.example.org/user/repo/releases/tag/1.0.1`);
244
+ t.is(github.options.proxy, 'http://proxy:8080');
204
245
  exec.restore();
205
246
  });
206
247
 
207
248
  test('should release to git.pushRepo', async t => {
208
- const remote = { api: 'https://my-custom-host.org/api/v3', host: 'my-custom-host.org' };
249
+ const remote = { api: 'https://custom.example.org/api/v3', host: 'custom.example.org' };
209
250
  interceptCreate(Object.assign({ body: { tag_name: '1.0.1' } }, remote));
210
- const options = { git: { pushRepo: 'upstream', changelog: null }, github: { tokenRef, skipChecks: true } };
251
+ const options = { git: { pushRepo: 'upstream', changelog: '' }, github: { tokenRef, skipChecks: true } };
211
252
  const github = factory(GitHub, { options });
212
253
  const exec = sinon.stub(github.shell, 'exec').callThrough();
254
+ exec.withArgs('git log --pretty=format:"* %s (%h)" ${from}...${to}').resolves('');
213
255
  exec.withArgs('git describe --tags --match=* --abbrev=0').resolves('1.0.0');
214
- exec.withArgs('git remote get-url upstream').resolves('https://my-custom-host.org/user/repo');
256
+ exec.withArgs('git remote get-url upstream').resolves('https://custom.example.org/user/repo');
215
257
 
216
258
  await runTasks(github);
217
259
 
218
260
  const { isReleased, releaseUrl } = github.getContext();
219
261
  t.true(isReleased);
220
- t.is(releaseUrl, 'https://my-custom-host.org/user/repo/releases/tag/1.0.1');
262
+ t.is(releaseUrl, 'https://custom.example.org/user/repo/releases/tag/1.0.1');
221
263
  exec.restore();
222
264
  });
223
265
 
@@ -308,6 +350,7 @@ test('should not call octokit client in dry run', async t => {
308
350
  const github = factory(GitHub, { options });
309
351
  const spy = sinon.spy(github, 'client', ['get']);
310
352
  const exec = sinon.stub(github.shell, 'exec').callThrough();
353
+ exec.withArgs('git log --pretty=format:"* %s (%h)" ${from}...${to}').resolves('');
311
354
  exec.withArgs('git describe --tags --match=* --abbrev=0').resolves('v1.0.0');
312
355
 
313
356
  await runTasks(github);
@@ -327,3 +370,57 @@ test('should skip checks', async t => {
327
370
  const github = factory(GitHub, { options });
328
371
  await t.notThrowsAsync(github.init());
329
372
  });
373
+
374
+ test('should generate GitHub web release url', async t => {
375
+ const options = {
376
+ github: {
377
+ pushRepo,
378
+ release: true,
379
+ web: true,
380
+ releaseName: 'Release ${tagName}',
381
+ releaseNotes: 'echo Custom notes'
382
+ }
383
+ };
384
+ const github = factory(GitHub, { options });
385
+ const exec = sinon.stub(github.shell, 'exec').callThrough();
386
+ exec.withArgs('git log --pretty=format:"* %s (%h)" ${from}...${to}').resolves('');
387
+ exec.withArgs('git describe --tags --match=* --abbrev=0').resolves('2.0.1');
388
+
389
+ await runTasks(github);
390
+
391
+ const { isReleased, releaseUrl } = github.getContext();
392
+ t.true(isReleased);
393
+ t.is(
394
+ releaseUrl,
395
+ 'https://github.com/user/repo/releases/new?tag=2.0.2&title=Release+2.0.2&body=Custom+notes&prerelease=false'
396
+ );
397
+ exec.restore();
398
+ });
399
+
400
+ test('should generate GitHub web release url for enterprise host', async t => {
401
+ const options = {
402
+ git,
403
+ github: {
404
+ pushRepo: 'git://custom.example.org:user/repo',
405
+ release: true,
406
+ web: true,
407
+ host: 'custom.example.org',
408
+ releaseName: 'The Launch',
409
+ releaseNotes: 'echo It happened'
410
+ }
411
+ };
412
+ const github = factory(GitHub, { options });
413
+ const exec = sinon.stub(github.shell, 'exec').callThrough();
414
+ exec.withArgs('git log --pretty=format:"* %s (%h)" ${from}...${to}').resolves('');
415
+ exec.withArgs('git describe --tags --match=* --abbrev=0').resolves('2.0.1');
416
+
417
+ await runTasks(github);
418
+
419
+ const { isReleased, releaseUrl } = github.getContext();
420
+ t.true(isReleased);
421
+ t.is(
422
+ releaseUrl,
423
+ 'https://custom.example.org/user/repo/releases/new?tag=2.0.2&title=The+Launch&body=It+happened&prerelease=false'
424
+ );
425
+ exec.restore();
426
+ });