release-it 19.0.0-next.0 → 19.0.0-next.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/lib/cli.js +2 -5
- package/lib/config.js +42 -7
- package/lib/index.js +30 -14
- package/lib/log.js +12 -11
- package/lib/plugin/GitBase.js +2 -1
- package/lib/plugin/GitRelease.js +5 -5
- package/lib/plugin/Plugin.js +6 -4
- package/lib/plugin/factory.js +11 -11
- package/lib/plugin/git/Git.js +5 -6
- package/lib/plugin/github/GitHub.js +13 -16
- package/lib/plugin/github/util.js +1 -1
- package/lib/plugin/gitlab/GitLab.js +6 -4
- package/lib/plugin/npm/npm.js +4 -0
- package/lib/util.js +71 -42
- package/package.json +27 -29
- package/schema/gitlab.json +4 -0
- package/schema/release-it.json +4 -0
- package/test/cli.js +6 -5
- package/test/config.js +232 -122
- package/test/git.init.js +220 -218
- package/test/git.js +359 -364
- package/test/github.js +565 -480
- package/test/gitlab.js +374 -335
- package/test/log.js +141 -138
- package/test/npm.js +315 -373
- package/test/plugin-name.js +7 -6
- package/test/plugins.js +200 -211
- package/test/prompt.js +26 -32
- package/test/shell.js +63 -60
- package/test/spinner.js +20 -24
- package/test/stub/github.js +113 -123
- package/test/stub/gitlab.js +74 -52
- package/test/tasks.interactive.js +217 -165
- package/test/tasks.js +394 -413
- package/test/util/helpers.js +4 -3
- package/test/util/index.js +35 -11
- package/test/utils.js +33 -32
- package/test/version.js +192 -176
- package/test/util/setup.js +0 -3
package/test/github.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import test from '
|
|
2
|
-
import
|
|
1
|
+
import test, { describe, before, after, afterEach } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
3
|
import { RequestError } from '@octokit/request-error';
|
|
4
|
+
import { MockServer, FetchMocker } from 'mentoss';
|
|
4
5
|
import GitHub from '../lib/plugin/github/GitHub.js';
|
|
5
6
|
import { getSearchQueries } from '../lib/plugin/github/util.js';
|
|
6
7
|
import { factory, runTasks } from './util/index.js';
|
|
@@ -13,535 +14,619 @@ import {
|
|
|
13
14
|
interceptAsset
|
|
14
15
|
} from './stub/github.js';
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const
|
|
17
|
+
describe('github', () => {
|
|
18
|
+
const tokenRef = 'GITHUB_TOKEN';
|
|
19
|
+
const pushRepo = 'git://github.com/user/repo';
|
|
20
|
+
const host = 'github.com';
|
|
21
|
+
const git = { changelog: '' };
|
|
22
|
+
const requestErrorOptions = { request: { url: '', headers: {} }, response: { headers: {} } };
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
const
|
|
24
|
+
const api = new MockServer('https://api.github.com');
|
|
25
|
+
const assets = new MockServer('https://uploads.github.com');
|
|
26
|
+
const example = new MockServer('https://github.example.org/api/v3');
|
|
27
|
+
const custom = new MockServer('https://custom.example.org/api/v3');
|
|
26
28
|
|
|
27
|
-
|
|
29
|
+
const mocker = new FetchMocker({
|
|
30
|
+
servers: [api, assets, example, custom]
|
|
31
|
+
});
|
|
28
32
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
});
|
|
33
|
+
before(() => {
|
|
34
|
+
mocker.mockGlobal();
|
|
35
|
+
});
|
|
33
36
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const github = factory(GitHub, { options });
|
|
38
|
-
delete process.env[tokenRef];
|
|
37
|
+
afterEach(() => {
|
|
38
|
+
mocker.clearAll();
|
|
39
|
+
});
|
|
39
40
|
|
|
40
|
-
|
|
41
|
+
after(() => {
|
|
42
|
+
mocker.unmockGlobal();
|
|
43
|
+
});
|
|
41
44
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
+
test('should check token and perform checks', async () => {
|
|
46
|
+
const tokenRef = 'MY_GITHUB_TOKEN';
|
|
47
|
+
const options = { github: { release: true, tokenRef, pushRepo } };
|
|
48
|
+
const github = factory(GitHub, { options });
|
|
45
49
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
pushRepo,
|
|
51
|
-
tokenRef,
|
|
52
|
-
release: true,
|
|
53
|
-
releaseName: 'Release ${tagName}',
|
|
54
|
-
releaseNotes: 'echo Custom notes',
|
|
55
|
-
assets: 'test/resources/file-v${version}.txt'
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
const github = factory(GitHub, { options });
|
|
59
|
-
const exec = sinon.stub(github.shell, 'exec').callThrough();
|
|
60
|
-
exec.withArgs('git log --pretty=format:"* %s (%h)" ${from}...${to}').resolves('');
|
|
61
|
-
exec.withArgs('git describe --tags --match=* --abbrev=0').resolves('2.0.1');
|
|
62
|
-
|
|
63
|
-
interceptAuthentication();
|
|
64
|
-
interceptCollaborator();
|
|
65
|
-
interceptCreate({ body: { tag_name: '2.0.2', name: 'Release 2.0.2', body: 'Custom notes' } });
|
|
66
|
-
interceptAsset({ body: '*' });
|
|
67
|
-
|
|
68
|
-
await runTasks(github);
|
|
69
|
-
|
|
70
|
-
const { isReleased, releaseUrl } = github.getContext();
|
|
71
|
-
t.true(isReleased);
|
|
72
|
-
t.is(releaseUrl, 'https://github.com/user/repo/releases/tag/2.0.2');
|
|
73
|
-
exec.restore();
|
|
74
|
-
});
|
|
50
|
+
interceptAuthentication(api);
|
|
51
|
+
interceptCollaborator(api);
|
|
52
|
+
await assert.doesNotReject(github.init());
|
|
53
|
+
});
|
|
75
54
|
|
|
76
|
-
test('should
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
github
|
|
80
|
-
|
|
81
|
-
tokenRef,
|
|
82
|
-
release: true,
|
|
83
|
-
releaseName: 'Release ${tagName}',
|
|
84
|
-
preRelease: true,
|
|
85
|
-
draft: true
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
const github = factory(GitHub, { options });
|
|
89
|
-
const exec = sinon.stub(github.shell, 'exec').callThrough();
|
|
90
|
-
exec.withArgs('git log --pretty=format:"* %s (%h)" ${from}...${to}').resolves('');
|
|
91
|
-
exec.withArgs('git describe --tags --match=* --abbrev=0').resolves('2.0.1');
|
|
92
|
-
|
|
93
|
-
interceptAuthentication();
|
|
94
|
-
interceptCollaborator();
|
|
95
|
-
interceptCreate({ body: { tag_name: '2.0.2', name: 'Release 2.0.2', prerelease: true, draft: true } });
|
|
96
|
-
|
|
97
|
-
await runTasks(github);
|
|
98
|
-
|
|
99
|
-
const { isReleased, releaseUrl } = github.getContext();
|
|
100
|
-
t.true(isReleased);
|
|
101
|
-
t.is(releaseUrl, 'https://github.com/user/repo/releases/tag/2.0.2');
|
|
102
|
-
exec.restore();
|
|
103
|
-
});
|
|
55
|
+
test('should check token and warn', async () => {
|
|
56
|
+
const tokenRef = 'MY_GITHUB_TOKEN';
|
|
57
|
+
const options = { github: { release: true, tokenRef, pushRepo } };
|
|
58
|
+
const github = factory(GitHub, { options });
|
|
59
|
+
delete process.env[tokenRef];
|
|
104
60
|
|
|
105
|
-
|
|
106
|
-
const options = {
|
|
107
|
-
git,
|
|
108
|
-
github: {
|
|
109
|
-
pushRepo,
|
|
110
|
-
tokenRef,
|
|
111
|
-
release: true,
|
|
112
|
-
releaseName: 'Release ${tagName}',
|
|
113
|
-
autoGenerate: true
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
|
-
const github = factory(GitHub, { options });
|
|
117
|
-
const exec = sinon.stub(github.shell, 'exec').callThrough();
|
|
118
|
-
exec.withArgs('git describe --tags --match=* --abbrev=0').resolves('2.0.1');
|
|
61
|
+
await assert.doesNotReject(github.init());
|
|
119
62
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
63
|
+
assert.equal(
|
|
64
|
+
github.log.warn.mock.calls[0].arguments[0],
|
|
65
|
+
'Environment variable "MY_GITHUB_TOKEN" is required for automated GitHub Releases.'
|
|
66
|
+
);
|
|
67
|
+
assert.equal(github.log.warn.mock.calls[1].arguments[0], 'Falling back to web-based GitHub Release.');
|
|
68
|
+
});
|
|
123
69
|
|
|
124
|
-
|
|
70
|
+
test('should release and upload assets', async t => {
|
|
71
|
+
const options = {
|
|
72
|
+
git,
|
|
73
|
+
github: {
|
|
74
|
+
pushRepo,
|
|
75
|
+
tokenRef,
|
|
76
|
+
release: true,
|
|
77
|
+
releaseName: 'Release ${tagName}',
|
|
78
|
+
releaseNotes: 'echo Custom notes',
|
|
79
|
+
assets: 'test/resources/file-v${version}.txt'
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
const github = factory(GitHub, { options });
|
|
83
|
+
|
|
84
|
+
const original = github.shell.exec.bind(github.shell);
|
|
85
|
+
t.mock.method(github.shell, 'exec', (...args) => {
|
|
86
|
+
if (args[0] === 'git log --pretty=format:"* %s (%h)" ${from}...${to}') return Promise.resolve('');
|
|
87
|
+
if (args[0] === 'git describe --tags --match=* --abbrev=0') return Promise.resolve('2.0.1');
|
|
88
|
+
return original(...args);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
interceptAuthentication(api);
|
|
92
|
+
interceptCollaborator(api);
|
|
93
|
+
interceptCreate(api, { body: { tag_name: '2.0.2', name: 'Release 2.0.2', body: 'Custom notes' } });
|
|
94
|
+
interceptAsset(assets, { body: '*' });
|
|
95
|
+
|
|
96
|
+
await runTasks(github);
|
|
97
|
+
|
|
98
|
+
const { isReleased, releaseUrl } = github.getContext();
|
|
99
|
+
assert(isReleased);
|
|
100
|
+
assert.equal(releaseUrl, 'https://github.com/user/repo/releases/tag/2.0.2');
|
|
101
|
+
});
|
|
125
102
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
103
|
+
test('should create a pre-release and draft release notes', async t => {
|
|
104
|
+
const options = {
|
|
105
|
+
git,
|
|
106
|
+
github: {
|
|
107
|
+
pushRepo,
|
|
108
|
+
tokenRef,
|
|
109
|
+
release: true,
|
|
110
|
+
releaseName: 'Release ${tagName}',
|
|
111
|
+
preRelease: true,
|
|
112
|
+
draft: true
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
const github = factory(GitHub, { options });
|
|
131
116
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
exec.withArgs('git log --pretty=format:"* %s (%h)" ${from}...${to}').resolves('');
|
|
150
|
-
exec.withArgs('git describe --tags --match=* --abbrev=0').resolves('2.0.1');
|
|
151
|
-
exec.withArgs('git rev-list 2.0.1 --tags --max-count=1').resolves('a123456');
|
|
152
|
-
exec.withArgs('git describe --tags --abbrev=0 "a123456^"').resolves('2.0.1');
|
|
153
|
-
|
|
154
|
-
interceptAuthentication();
|
|
155
|
-
interceptCollaborator();
|
|
156
|
-
interceptListReleases({ tag_name: '2.0.1' });
|
|
157
|
-
interceptUpdate({ body: { tag_name: '2.0.1', name: 'Release 2.0.1', body: 'Custom notes' } });
|
|
158
|
-
interceptAsset({ body: asset });
|
|
159
|
-
|
|
160
|
-
await runTasks(github);
|
|
161
|
-
|
|
162
|
-
const { isReleased, releaseUrl } = github.getContext();
|
|
163
|
-
t.true(isReleased);
|
|
164
|
-
t.is(releaseUrl, 'https://github.com/user/repo/releases/tag/2.0.1');
|
|
165
|
-
exec.restore();
|
|
166
|
-
});
|
|
117
|
+
const original = github.shell.exec.bind(github.shell);
|
|
118
|
+
t.mock.method(github.shell, 'exec', (...args) => {
|
|
119
|
+
if (args[0] === 'git log --pretty=format:"* %s (%h)" ${from}...${to}') return Promise.resolve('');
|
|
120
|
+
if (args[0] === 'git describe --tags --match=* --abbrev=0') return Promise.resolve('2.0.1');
|
|
121
|
+
return original(...args);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
interceptAuthentication(api);
|
|
125
|
+
interceptCollaborator(api);
|
|
126
|
+
interceptCreate(api, { body: { tag_name: '2.0.2', name: 'Release 2.0.2', prerelease: true, draft: true } });
|
|
127
|
+
|
|
128
|
+
await runTasks(github);
|
|
129
|
+
|
|
130
|
+
const { isReleased, releaseUrl } = github.getContext();
|
|
131
|
+
assert(isReleased);
|
|
132
|
+
assert.equal(releaseUrl, 'https://github.com/user/repo/releases/tag/2.0.2');
|
|
133
|
+
});
|
|
167
134
|
|
|
168
|
-
test('should create
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
return `Custom notes for tag ${context.tagName}`;
|
|
135
|
+
test('should create auto-generated release notes', async t => {
|
|
136
|
+
const options = {
|
|
137
|
+
git,
|
|
138
|
+
github: {
|
|
139
|
+
pushRepo,
|
|
140
|
+
tokenRef,
|
|
141
|
+
release: true,
|
|
142
|
+
releaseName: 'Release ${tagName}',
|
|
143
|
+
autoGenerate: true
|
|
178
144
|
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
145
|
+
};
|
|
146
|
+
const github = factory(GitHub, { options });
|
|
147
|
+
|
|
148
|
+
const original = github.shell.exec.bind(github.shell);
|
|
149
|
+
t.mock.method(github.shell, 'exec', (...args) => {
|
|
150
|
+
if (args[0] === 'git describe --tags --match=* --abbrev=0') return Promise.resolve('2.0.1');
|
|
151
|
+
return original(...args);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
interceptAuthentication(api);
|
|
155
|
+
interceptCollaborator(api);
|
|
156
|
+
interceptCreate(api, {
|
|
157
|
+
body: { tag_name: '2.0.2', name: 'Release 2.0.2', generate_release_notes: true, body: '' }
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
await runTasks(github);
|
|
161
|
+
|
|
162
|
+
const { isReleased, releaseUrl } = github.getContext();
|
|
163
|
+
assert(isReleased);
|
|
164
|
+
assert.equal(releaseUrl, 'https://github.com/user/repo/releases/tag/2.0.2');
|
|
165
|
+
});
|
|
184
166
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
167
|
+
test('should update release and upload assets', async t => {
|
|
168
|
+
const asset = 'file1';
|
|
169
|
+
const options = {
|
|
170
|
+
increment: false,
|
|
171
|
+
git,
|
|
172
|
+
github: {
|
|
173
|
+
update: true,
|
|
174
|
+
pushRepo,
|
|
175
|
+
tokenRef,
|
|
176
|
+
release: true,
|
|
177
|
+
releaseName: 'Release ${tagName}',
|
|
178
|
+
releaseNotes: 'echo Custom notes',
|
|
179
|
+
assets: `test/resources/${asset}`
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
const github = factory(GitHub, { options });
|
|
183
|
+
|
|
184
|
+
const original = github.shell.exec.bind(github.shell);
|
|
185
|
+
t.mock.method(github.shell, 'exec', (...args) => {
|
|
186
|
+
if (args[0] === 'git log --pretty=format:"* %s (%h)" ${from}...${to}') return Promise.resolve('');
|
|
187
|
+
if (args[0] === 'git describe --tags --match=* --abbrev=0') return Promise.resolve('2.0.1');
|
|
188
|
+
if (args[0] === 'git rev-list 2.0.1 --tags --max-count=1') return Promise.resolve('a123456');
|
|
189
|
+
if (args[0] === 'git describe --tags --match=* --abbrev=0') return Promise.resolve('2.0.1');
|
|
190
|
+
return original(...args);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
interceptAuthentication(api);
|
|
194
|
+
interceptCollaborator(api);
|
|
195
|
+
interceptListReleases(api, { tag_name: '2.0.1' });
|
|
196
|
+
interceptUpdate(api, { body: { tag_name: '2.0.1', name: 'Release 2.0.1', body: 'Custom notes' } });
|
|
197
|
+
interceptAsset(assets, { body: asset });
|
|
198
|
+
|
|
199
|
+
await runTasks(github);
|
|
200
|
+
|
|
201
|
+
const { isReleased, releaseUrl } = github.getContext();
|
|
202
|
+
assert(isReleased);
|
|
203
|
+
assert.equal(releaseUrl, 'https://github.com/user/repo/releases/tag/2.0.1');
|
|
204
|
+
});
|
|
188
205
|
|
|
189
|
-
|
|
206
|
+
test('should create custom release notes using releaseNotes function', async t => {
|
|
207
|
+
const options = {
|
|
208
|
+
git,
|
|
209
|
+
github: {
|
|
210
|
+
pushRepo,
|
|
211
|
+
tokenRef,
|
|
212
|
+
release: true,
|
|
213
|
+
releaseName: 'Release ${tagName}',
|
|
214
|
+
releaseNotes(context) {
|
|
215
|
+
return `Custom notes for tag ${context.tagName}`;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
const github = factory(GitHub, { options });
|
|
220
|
+
|
|
221
|
+
const original = github.shell.exec.bind(github.shell);
|
|
222
|
+
t.mock.method(github.shell, 'exec', (...args) => {
|
|
223
|
+
if (args[0] === 'git describe --tags --match=* --abbrev=0') return Promise.resolve('2.0.1');
|
|
224
|
+
return original(...args);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
interceptAuthentication(api);
|
|
228
|
+
interceptCollaborator(api);
|
|
229
|
+
interceptCreate(api, {
|
|
230
|
+
body: { tag_name: '2.0.2', name: 'Release 2.0.2', body: 'Custom notes for tag 2.0.2' }
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
await runTasks(github);
|
|
234
|
+
|
|
235
|
+
const { isReleased, releaseUrl } = github.getContext();
|
|
236
|
+
assert(isReleased);
|
|
237
|
+
assert.equal(releaseUrl, 'https://github.com/user/repo/releases/tag/2.0.2');
|
|
238
|
+
});
|
|
190
239
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
240
|
+
test('should create new release for unreleased tag', async t => {
|
|
241
|
+
const options = {
|
|
242
|
+
increment: false,
|
|
243
|
+
git,
|
|
244
|
+
github: {
|
|
245
|
+
update: true,
|
|
246
|
+
pushRepo,
|
|
247
|
+
tokenRef,
|
|
248
|
+
release: true,
|
|
249
|
+
releaseName: 'Release ${tagName}',
|
|
250
|
+
releaseNotes: 'echo Custom notes'
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
const github = factory(GitHub, { options });
|
|
254
|
+
|
|
255
|
+
const original = github.shell.exec.bind(github.shell);
|
|
256
|
+
t.mock.method(github.shell, 'exec', (...args) => {
|
|
257
|
+
if (args[0] === 'git log --pretty=format:"* %s (%h)" ${from}...${to}') return Promise.resolve('');
|
|
258
|
+
if (args[0] === 'git describe --tags --match=* --abbrev=0') return Promise.resolve('2.0.1');
|
|
259
|
+
if (args[0] === 'git rev-list 2.0.1 --tags --max-count=1') return Promise.resolve('b123456');
|
|
260
|
+
if (args[0] === 'git describe --tags --abbrev=0 "b123456^"') return Promise.resolve('2.0.1');
|
|
261
|
+
return original(...args);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
interceptAuthentication(api);
|
|
265
|
+
interceptCollaborator(api);
|
|
266
|
+
interceptListReleases(api, { tag_name: '2.0.0' });
|
|
267
|
+
interceptCreate(api, { body: { tag_name: '2.0.1', name: 'Release 2.0.1', body: 'Custom notes' } });
|
|
268
|
+
|
|
269
|
+
await runTasks(github);
|
|
270
|
+
|
|
271
|
+
const { isReleased, releaseUrl } = github.getContext();
|
|
272
|
+
assert(isReleased);
|
|
273
|
+
assert.equal(releaseUrl, 'https://github.com/user/repo/releases/tag/2.0.1');
|
|
274
|
+
});
|
|
196
275
|
|
|
197
|
-
test('should
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
github
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
const { isReleased, releaseUrl } = github.getContext();
|
|
225
|
-
t.true(isReleased);
|
|
226
|
-
t.is(releaseUrl, 'https://github.com/user/repo/releases/tag/2.0.1');
|
|
227
|
-
exec.restore();
|
|
228
|
-
});
|
|
276
|
+
test('should release to enterprise host', async t => {
|
|
277
|
+
const options = { git, github: { tokenRef, pushRepo: 'git://github.example.org/user/repo' } };
|
|
278
|
+
const github = factory(GitHub, { options });
|
|
279
|
+
|
|
280
|
+
const original = github.shell.exec.bind(github.shell);
|
|
281
|
+
t.mock.method(github.shell, 'exec', (...args) => {
|
|
282
|
+
if (args[0] === 'git remote get-url origin') return Promise.resolve(`https://github.example.org/user/repo`);
|
|
283
|
+
if (args[0] === 'git config --get remote.origin.url')
|
|
284
|
+
return Promise.resolve('https://github.example.org/user/repo');
|
|
285
|
+
if (args[0] === 'git describe --tags --match=* --abbrev=0') return Promise.resolve('1.0.0');
|
|
286
|
+
return original(...args);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
interceptAuthentication(example);
|
|
290
|
+
interceptCollaborator(example);
|
|
291
|
+
interceptCreate(example, {
|
|
292
|
+
api: 'https://github.example.org/api/v3',
|
|
293
|
+
host: 'github.example.org',
|
|
294
|
+
body: { tag_name: '1.0.1' }
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
await runTasks(github);
|
|
298
|
+
|
|
299
|
+
const { isReleased, releaseUrl } = github.getContext();
|
|
300
|
+
assert(isReleased);
|
|
301
|
+
assert.equal(releaseUrl, 'https://github.example.org/user/repo/releases/tag/1.0.1');
|
|
302
|
+
});
|
|
229
303
|
|
|
230
|
-
test('should release to
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
304
|
+
test('should release to alternative host and proxy', async t => {
|
|
305
|
+
const options = {
|
|
306
|
+
git,
|
|
307
|
+
github: {
|
|
308
|
+
tokenRef,
|
|
309
|
+
pushRepo: `git://custom.example.org/user/repo`,
|
|
310
|
+
host: 'custom.example.org',
|
|
311
|
+
proxy: 'http://proxy:8080'
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
const github = factory(GitHub, { options });
|
|
315
|
+
|
|
316
|
+
const original = github.shell.exec.bind(github.shell);
|
|
317
|
+
t.mock.method(github.shell, 'exec', (...args) => {
|
|
318
|
+
if (args[0] === 'git log --pretty=format:"* %s (%h)" ${from}...${to}') return Promise.resolve('');
|
|
319
|
+
if (args[0] === 'git describe --tags --match=* --abbrev=0') return Promise.resolve('1.0.0');
|
|
320
|
+
return original(...args);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
interceptAuthentication(custom);
|
|
324
|
+
interceptCollaborator(custom);
|
|
325
|
+
interceptCreate(custom, {
|
|
326
|
+
api: 'https://custom.example.org/api/v3',
|
|
327
|
+
host: 'custom.example.org',
|
|
328
|
+
body: { tag_name: '1.0.1' }
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
await runTasks(github);
|
|
250
332
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
github: {
|
|
259
|
-
|
|
260
|
-
|
|
333
|
+
const { isReleased, releaseUrl } = github.getContext();
|
|
334
|
+
assert(isReleased);
|
|
335
|
+
assert.equal(releaseUrl, 'https://custom.example.org/user/repo/releases/tag/1.0.1');
|
|
336
|
+
assert.equal(github.options.proxy, 'http://proxy:8080');
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
test('should release to git.pushRepo', async t => {
|
|
340
|
+
const options = { git: { pushRepo: 'upstream', changelog: '' }, github: { tokenRef, skipChecks: true } };
|
|
341
|
+
const github = factory(GitHub, { options });
|
|
342
|
+
|
|
343
|
+
const original = github.shell.exec.bind(github.shell);
|
|
344
|
+
t.mock.method(github.shell, 'exec', (...args) => {
|
|
345
|
+
if (args[0] === 'git log --pretty=format:"* %s (%h)" ${from}...${to}') return Promise.resolve('');
|
|
346
|
+
if (args[0] === 'git describe --tags --match=* --abbrev=0') return Promise.resolve('1.0.0');
|
|
347
|
+
if (args[0] === 'git remote get-url upstream') return Promise.resolve('https://custom.example.org/user/repo');
|
|
348
|
+
return original(...args);
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
interceptCreate(custom, {
|
|
352
|
+
api: 'https://custom.example.org/api/v3',
|
|
261
353
|
host: 'custom.example.org',
|
|
262
|
-
|
|
263
|
-
}
|
|
264
|
-
};
|
|
265
|
-
const github = factory(GitHub, { options });
|
|
266
|
-
const exec = sinon.stub(github.shell, 'exec').callThrough();
|
|
267
|
-
exec.withArgs('git log --pretty=format:"* %s (%h)" ${from}...${to}').resolves('');
|
|
268
|
-
exec.withArgs('git describe --tags --match=* --abbrev=0').resolves('1.0.0');
|
|
269
|
-
|
|
270
|
-
await runTasks(github);
|
|
271
|
-
|
|
272
|
-
const { isReleased, releaseUrl } = github.getContext();
|
|
273
|
-
t.true(isReleased);
|
|
274
|
-
t.is(releaseUrl, `https://custom.example.org/user/repo/releases/tag/1.0.1`);
|
|
275
|
-
t.is(github.options.proxy, 'http://proxy:8080');
|
|
276
|
-
exec.restore();
|
|
277
|
-
});
|
|
354
|
+
body: { tag_name: '1.0.1' }
|
|
355
|
+
});
|
|
278
356
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
const { isReleased, releaseUrl } = github.getContext();
|
|
292
|
-
t.true(isReleased);
|
|
293
|
-
t.is(releaseUrl, 'https://custom.example.org/user/repo/releases/tag/1.0.1');
|
|
294
|
-
exec.restore();
|
|
295
|
-
});
|
|
357
|
+
await runTasks(github);
|
|
358
|
+
|
|
359
|
+
const { isReleased, releaseUrl } = github.getContext();
|
|
360
|
+
assert(isReleased);
|
|
361
|
+
assert.equal(releaseUrl, 'https://custom.example.org/user/repo/releases/tag/1.0.1');
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
const testSkipOnActions = process.env.GITHUB_ACTIONS ? test.skip : test;
|
|
365
|
+
|
|
366
|
+
testSkipOnActions('should throw for unauthenticated user', async t => {
|
|
367
|
+
const options = { github: { tokenRef, pushRepo, host } };
|
|
368
|
+
const github = factory(GitHub, { options });
|
|
296
369
|
|
|
297
|
-
const
|
|
370
|
+
const getAuthenticated = t.mock.method(github.client.users, 'getAuthenticated', () => {
|
|
371
|
+
throw new RequestError('Bad credentials', 401, requestErrorOptions);
|
|
372
|
+
});
|
|
298
373
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
const stub = sinon.stub(github.client.users, 'getAuthenticated');
|
|
303
|
-
stub.throws(new RequestError('Bad credentials', 401, requestErrorOptions));
|
|
374
|
+
await assert.rejects(runTasks(github), {
|
|
375
|
+
message: /Could not authenticate with GitHub using environment variable "GITHUB_TOKEN"/
|
|
376
|
+
});
|
|
304
377
|
|
|
305
|
-
|
|
306
|
-
message: /^Could not authenticate with GitHub using environment variable "GITHUB_TOKEN"/
|
|
378
|
+
assert.equal(getAuthenticated.mock.callCount(), 1);
|
|
307
379
|
});
|
|
308
380
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
});
|
|
381
|
+
testSkipOnActions('should throw for non-collaborator', async t => {
|
|
382
|
+
const options = { github: { tokenRef, pushRepo, host } };
|
|
383
|
+
const github = factory(GitHub, { options });
|
|
312
384
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
const github = factory(GitHub, { options });
|
|
317
|
-
const stub = sinon.stub(github.client.repos, 'checkCollaborator');
|
|
318
|
-
stub.throws(new RequestError('HttpError', 401, requestErrorOptions));
|
|
385
|
+
t.mock.method(github.client.repos, 'checkCollaborator', () => {
|
|
386
|
+
throw new RequestError('HttpError', 401, requestErrorOptions);
|
|
387
|
+
});
|
|
319
388
|
|
|
320
|
-
|
|
389
|
+
interceptAuthentication(api, { username: 'john' });
|
|
321
390
|
|
|
322
|
-
|
|
323
|
-
});
|
|
391
|
+
await assert.rejects(runTasks(github), /User john is not a collaborator for user\/repo/);
|
|
392
|
+
});
|
|
324
393
|
|
|
325
|
-
test
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
394
|
+
test('should skip authentication and collaborator checks when running on GitHub Actions', async t => {
|
|
395
|
+
const { GITHUB_ACTIONS, GITHUB_ACTOR } = process.env;
|
|
396
|
+
if (!GITHUB_ACTIONS) {
|
|
397
|
+
process.env.GITHUB_ACTIONS = 1;
|
|
398
|
+
process.env.GITHUB_ACTOR = 'webpro';
|
|
399
|
+
}
|
|
331
400
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
401
|
+
const options = { github: { tokenRef } };
|
|
402
|
+
const github = factory(GitHub, { options });
|
|
403
|
+
const authStub = t.mock.method(github, 'isAuthenticated');
|
|
404
|
+
const collaboratorStub = t.mock.method(github, 'isCollaborator');
|
|
336
405
|
|
|
337
|
-
|
|
406
|
+
await assert.doesNotReject(github.init());
|
|
338
407
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
408
|
+
assert.equal(authStub.mock.callCount(), 0);
|
|
409
|
+
assert.equal(collaboratorStub.mock.callCount(), 0);
|
|
410
|
+
assert.equal(github.getContext('username'), process.env.GITHUB_ACTOR);
|
|
342
411
|
|
|
343
|
-
|
|
344
|
-
|
|
412
|
+
if (!GITHUB_ACTIONS) {
|
|
413
|
+
process.env.GITHUB_ACTIONS = GITHUB_ACTIONS || '';
|
|
414
|
+
process.env.GITHUB_ACTOR = GITHUB_ACTOR || '';
|
|
415
|
+
}
|
|
416
|
+
});
|
|
345
417
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
});
|
|
418
|
+
test('should handle octokit client error (without retries)', async t => {
|
|
419
|
+
const github = factory(GitHub, { options: { github: { tokenRef, pushRepo, host } } });
|
|
420
|
+
const createRelease = t.mock.method(github.client.repos, 'createRelease', () => {
|
|
421
|
+
throw new RequestError('Not found', 404, requestErrorOptions);
|
|
422
|
+
});
|
|
351
423
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
const stub = sinon.stub(github.client.repos, 'createRelease');
|
|
355
|
-
stub.throws(new RequestError('Not found', 404, requestErrorOptions));
|
|
356
|
-
interceptAuthentication();
|
|
357
|
-
interceptCollaborator();
|
|
424
|
+
interceptAuthentication(api);
|
|
425
|
+
interceptCollaborator(api);
|
|
358
426
|
|
|
359
|
-
|
|
427
|
+
await assert.rejects(runTasks(github), /404 \(Not found\)/);
|
|
360
428
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
});
|
|
429
|
+
assert.equal(createRelease.mock.callCount(), 1);
|
|
430
|
+
});
|
|
364
431
|
|
|
365
|
-
test('should handle octokit client error (with retries)', async t => {
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
const stub = sinon.stub(github.client.repos, 'createRelease');
|
|
369
|
-
stub.throws(new RequestError('Request failed', 500, requestErrorOptions));
|
|
370
|
-
interceptAuthentication();
|
|
371
|
-
interceptCollaborator();
|
|
432
|
+
test('should handle octokit client error (with retries)', async t => {
|
|
433
|
+
const options = { github: { tokenRef, pushRepo, host, retryMinTimeout: 0 } };
|
|
434
|
+
const github = factory(GitHub, { options });
|
|
372
435
|
|
|
373
|
-
|
|
436
|
+
const createRelease = t.mock.method(github.client.repos, 'createRelease', () => {
|
|
437
|
+
throw new RequestError('Request failed', 500, requestErrorOptions);
|
|
438
|
+
});
|
|
374
439
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
});
|
|
440
|
+
interceptAuthentication(api);
|
|
441
|
+
interceptCollaborator(api);
|
|
378
442
|
|
|
379
|
-
|
|
380
|
-
const options = { 'dry-run': true, git, github: { tokenRef, pushRepo, releaseName: 'R ${version}', assets: ['*'] } };
|
|
381
|
-
const github = factory(GitHub, { options });
|
|
382
|
-
const spy = sinon.spy(github, 'client', ['get']);
|
|
383
|
-
const exec = sinon.stub(github.shell, 'exec').callThrough();
|
|
384
|
-
exec.withArgs('git log --pretty=format:"* %s (%h)" ${from}...${to}').resolves('');
|
|
385
|
-
exec.withArgs('git describe --tags --match=* --abbrev=0').resolves('v1.0.0');
|
|
386
|
-
|
|
387
|
-
await runTasks(github);
|
|
388
|
-
|
|
389
|
-
t.is(spy.get.callCount, 0);
|
|
390
|
-
t.is(github.log.exec.args[1][0], 'octokit repos.createRelease "R 1.0.1" (v1.0.1)');
|
|
391
|
-
t.is(github.log.exec.lastCall.args[0], 'octokit repos.uploadReleaseAssets');
|
|
392
|
-
const { isReleased, releaseUrl } = github.getContext();
|
|
393
|
-
t.true(isReleased);
|
|
394
|
-
t.is(releaseUrl, 'https://github.com/user/repo/releases/tag/v1.0.1');
|
|
395
|
-
spy.get.restore();
|
|
396
|
-
exec.restore();
|
|
397
|
-
});
|
|
443
|
+
await assert.rejects(runTasks(github), /500 \(Request failed\)/);
|
|
398
444
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
const github = factory(GitHub, { options });
|
|
402
|
-
await t.notThrowsAsync(github.init());
|
|
403
|
-
});
|
|
445
|
+
assert.equal(createRelease.mock.callCount(), 3);
|
|
446
|
+
});
|
|
404
447
|
|
|
405
|
-
test('should
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
});
|
|
448
|
+
test('should not call octokit client in dry run', async t => {
|
|
449
|
+
const options = {
|
|
450
|
+
'dry-run': true,
|
|
451
|
+
git,
|
|
452
|
+
github: { tokenRef, pushRepo, releaseName: 'R ${version}', assets: ['*'] }
|
|
453
|
+
};
|
|
454
|
+
const github = factory(GitHub, { options });
|
|
455
|
+
|
|
456
|
+
const get = t.mock.getter(github, 'client');
|
|
457
|
+
const original = github.shell.exec.bind(github.shell);
|
|
458
|
+
t.mock.method(github.shell, 'exec', (...args) => {
|
|
459
|
+
if (args[0] === 'git log --pretty=format:"* %s (%h)" ${from}...${to}') return Promise.resolve('');
|
|
460
|
+
if (args[0] === 'git describe --tags --match=* --abbrev=0') return Promise.resolve('v1.0.0');
|
|
461
|
+
return original(...args);
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
await runTasks(github);
|
|
465
|
+
|
|
466
|
+
assert.equal(get.mock.callCount(), 0);
|
|
467
|
+
assert.equal(github.log.exec.mock.calls[1].arguments[0], 'octokit repos.createRelease "R 1.0.1" (v1.0.1)');
|
|
468
|
+
assert.equal(github.log.exec.mock.calls.at(-1).arguments[0], 'octokit repos.uploadReleaseAssets');
|
|
469
|
+
const { isReleased, releaseUrl } = github.getContext();
|
|
470
|
+
assert(isReleased);
|
|
471
|
+
assert.equal(releaseUrl, 'https://github.com/user/repo/releases/tag/v1.0.1');
|
|
472
|
+
});
|
|
430
473
|
|
|
431
|
-
test('should generate GitHub web release url
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
474
|
+
test('should generate GitHub web release url', async t => {
|
|
475
|
+
const options = {
|
|
476
|
+
github: {
|
|
477
|
+
pushRepo,
|
|
478
|
+
release: true,
|
|
479
|
+
web: true,
|
|
480
|
+
releaseName: 'Release ${tagName}',
|
|
481
|
+
releaseNotes: 'echo Custom notes'
|
|
482
|
+
}
|
|
483
|
+
};
|
|
484
|
+
const github = factory(GitHub, { options });
|
|
485
|
+
|
|
486
|
+
const original = github.shell.exec.bind(github.shell);
|
|
487
|
+
t.mock.method(github.shell, 'exec', (...args) => {
|
|
488
|
+
if (args[0] === 'git log --pretty=format:"* %s (%h)" ${from}...${to}') return Promise.resolve('');
|
|
489
|
+
if (args[0] === 'git describe --tags --match=* --abbrev=0') return Promise.resolve('2.0.1');
|
|
490
|
+
return original(...args);
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
await runTasks(github);
|
|
494
|
+
|
|
495
|
+
const { isReleased, releaseUrl } = github.getContext();
|
|
496
|
+
assert(isReleased);
|
|
497
|
+
assert.equal(
|
|
498
|
+
releaseUrl,
|
|
499
|
+
'https://github.com/user/repo/releases/new?tag=2.0.2&title=Release+2.0.2&body=Custom+notes&prerelease=false'
|
|
500
|
+
);
|
|
501
|
+
});
|
|
458
502
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
});
|
|
503
|
+
test('should generate GitHub web release url for enterprise host', async t => {
|
|
504
|
+
const options = {
|
|
505
|
+
git,
|
|
506
|
+
github: {
|
|
507
|
+
pushRepo: 'git@custom.example.org:user/repo',
|
|
508
|
+
release: true,
|
|
509
|
+
web: true,
|
|
510
|
+
host: 'custom.example.org',
|
|
511
|
+
releaseName: 'The Launch',
|
|
512
|
+
releaseNotes: 'echo It happened'
|
|
513
|
+
}
|
|
514
|
+
};
|
|
515
|
+
const github = factory(GitHub, { options });
|
|
516
|
+
|
|
517
|
+
const original = github.shell.exec.bind(github.shell);
|
|
518
|
+
t.mock.method(github.shell, 'exec', (...args) => {
|
|
519
|
+
if (args[0] === 'git log --pretty=format:"* %s (%h)" ${from}...${to}') return Promise.resolve('');
|
|
520
|
+
if (args[0] === 'git describe --tags --match=* --abbrev=0') return Promise.resolve('2.0.1');
|
|
521
|
+
return original(...args);
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
await runTasks(github);
|
|
525
|
+
|
|
526
|
+
const { isReleased, releaseUrl } = github.getContext();
|
|
527
|
+
assert(isReleased);
|
|
528
|
+
assert.equal(
|
|
529
|
+
releaseUrl,
|
|
530
|
+
'https://custom.example.org/user/repo/releases/new?tag=2.0.2&title=The+Launch&body=It+happened&prerelease=false'
|
|
531
|
+
);
|
|
532
|
+
});
|
|
489
533
|
|
|
490
|
-
test('should
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
const longResult = getSearchQueries(base, manyCommits, separator);
|
|
505
|
-
t.true(longResult.length > 1, 'Many commits should be split into multiple queries');
|
|
506
|
-
t.true(
|
|
507
|
-
longResult.every(query => encodeURIComponent(query).length <= 256),
|
|
508
|
-
'Each query should not exceed 256 characters after encoding'
|
|
509
|
-
);
|
|
510
|
-
});
|
|
534
|
+
test.skip('should truncate long body', async t => {
|
|
535
|
+
const releaseNotes = 'a'.repeat(125001);
|
|
536
|
+
const body = 'a'.repeat(124000) + '...';
|
|
537
|
+
const options = {
|
|
538
|
+
git,
|
|
539
|
+
github: {
|
|
540
|
+
pushRepo,
|
|
541
|
+
tokenRef,
|
|
542
|
+
release: true,
|
|
543
|
+
releaseName: 'Release ${tagName}',
|
|
544
|
+
releaseNotes: 'echo ' + releaseNotes
|
|
545
|
+
}
|
|
546
|
+
};
|
|
547
|
+
const github = factory(GitHub, { options });
|
|
511
548
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
interceptAuthentication();
|
|
529
|
-
interceptCollaborator();
|
|
530
|
-
interceptCreate({
|
|
531
|
-
body: {
|
|
532
|
-
tag_name: '2.0.2',
|
|
533
|
-
name: 'Release 2.0.2',
|
|
534
|
-
generate_release_notes: false,
|
|
535
|
-
body: null,
|
|
536
|
-
discussion_category_name: 'Announcement'
|
|
537
|
-
}
|
|
549
|
+
const original = github.shell.exec.bind(github.shell);
|
|
550
|
+
t.mock.method(github.shell, 'exec', (...args) => {
|
|
551
|
+
if (args[0] === 'git log --pretty=format:"* %s (%h)" ${from}...${to}') return Promise.resolve('');
|
|
552
|
+
if (args[0] === 'git describe --tags --match=* --abbrev=0') return Promise.resolve('2.0.1');
|
|
553
|
+
return original(...args);
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
interceptAuthentication(api);
|
|
557
|
+
interceptCollaborator(api);
|
|
558
|
+
interceptCreate(api, { body: { tag_name: '2.0.2', name: 'Release 2.0.2', body } });
|
|
559
|
+
|
|
560
|
+
await runTasks(github);
|
|
561
|
+
|
|
562
|
+
const { isReleased, releaseUrl } = github.getContext();
|
|
563
|
+
assert(isReleased);
|
|
564
|
+
assert.equal(releaseUrl, 'https://github.com/user/repo/releases/tag/2.0.2');
|
|
538
565
|
});
|
|
539
566
|
|
|
540
|
-
|
|
567
|
+
test('should generate search queries correctly', () => {
|
|
568
|
+
const generateCommit = () => Math.random().toString(36).substring(2, 9);
|
|
569
|
+
const base = 'repo:owner/repo+type:pr+is:merged';
|
|
570
|
+
const commits = Array.from({ length: 5 }, generateCommit);
|
|
571
|
+
const separator = '+';
|
|
572
|
+
|
|
573
|
+
const result = getSearchQueries(base, commits, separator);
|
|
574
|
+
|
|
575
|
+
// Test case 1: Check if all commits are included in the search queries
|
|
576
|
+
const allCommitsIncluded = commits.every(commit => result.some(query => query.includes(commit)));
|
|
577
|
+
assert(allCommitsIncluded, 'All commits should be included in the search queries');
|
|
578
|
+
|
|
579
|
+
assert.equal(
|
|
580
|
+
commits.every(commit => result.some(query => query.includes(commit))),
|
|
581
|
+
true
|
|
582
|
+
);
|
|
583
|
+
|
|
584
|
+
// Test case 2: Check if the function respects the 256 character limit
|
|
585
|
+
const manyCommits = Array.from({ length: 100 }, generateCommit);
|
|
586
|
+
const longResult = getSearchQueries(base, manyCommits, separator);
|
|
587
|
+
assert(longResult.length > 1, 'Many commits should be split into multiple queries');
|
|
588
|
+
assert(
|
|
589
|
+
longResult.every(query => encodeURIComponent(query).length <= 256),
|
|
590
|
+
'Each query should not exceed 256 characters after encoding'
|
|
591
|
+
);
|
|
592
|
+
});
|
|
541
593
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
594
|
+
test('should create auto-generated discussion', async t => {
|
|
595
|
+
const options = {
|
|
596
|
+
git,
|
|
597
|
+
github: {
|
|
598
|
+
pushRepo,
|
|
599
|
+
tokenRef,
|
|
600
|
+
release: true,
|
|
601
|
+
releaseName: 'Release ${tagName}',
|
|
602
|
+
autoGenerate: false,
|
|
603
|
+
discussionCategoryName: 'Announcement'
|
|
604
|
+
}
|
|
605
|
+
};
|
|
606
|
+
const github = factory(GitHub, { options });
|
|
607
|
+
const original = github.shell.exec.bind(github.shell);
|
|
608
|
+
t.mock.method(github.shell, 'exec', (...args) => {
|
|
609
|
+
if (args[0] === 'git describe --tags --match=* --abbrev=0') return Promise.resolve('2.0.1');
|
|
610
|
+
return original(...args);
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
interceptAuthentication(api);
|
|
614
|
+
interceptCollaborator(api);
|
|
615
|
+
interceptCreate(api, {
|
|
616
|
+
body: {
|
|
617
|
+
tag_name: '2.0.2',
|
|
618
|
+
name: 'Release 2.0.2',
|
|
619
|
+
generate_release_notes: false,
|
|
620
|
+
body: null,
|
|
621
|
+
discussion_category_name: 'Announcement'
|
|
622
|
+
}
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
await runTasks(github);
|
|
626
|
+
|
|
627
|
+
const { isReleased, releaseUrl, discussionUrl } = github.getContext();
|
|
628
|
+
assert(isReleased);
|
|
629
|
+
assert.equal(releaseUrl, 'https://github.com/user/repo/releases/tag/2.0.2');
|
|
630
|
+
assert.equal(discussionUrl, 'https://github.com/user/repo/discussions/1');
|
|
631
|
+
});
|
|
547
632
|
});
|