release-it 18.0.0-next.3 → 18.0.0

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
@@ -44,7 +44,7 @@ npm install -D release-it
44
44
  "release": "release-it"
45
45
  },
46
46
  "devDependencies": {
47
- "release-it": "^16.1.0"
47
+ "release-it": "^18.0.0"
48
48
  }
49
49
  }
50
50
  ```
@@ -99,7 +99,7 @@ Here's a quick example `.release-it.json`:
99
99
 
100
100
  ```json
101
101
  {
102
- "$schema": "https://unpkg.com/release-it@17/schema/release-it.json",
102
+ "$schema": "https://unpkg.com/release-it@18/schema/release-it.json",
103
103
  "git": {
104
104
  "commitMessage": "chore: release v${version}"
105
105
  },
@@ -181,6 +181,7 @@ message conventions. Plugins are available for:
181
181
  - auto-changelog
182
182
  - Conventional Changelog
183
183
  - Keep A Changelog
184
+ - git-cliff
184
185
 
185
186
  To print the changelog without releasing anything, add the `--changelog` flag.
186
187
 
@@ -328,34 +329,43 @@ release-it programmatically][54] for example code.
328
329
  - [Halo][59]
329
330
  - [hosts][60]
330
331
  - [js-cookie][61]
331
- - [Madge][62]
332
- - [Metalsmith][63]
333
- - [Node-Redis][64]
334
- - [React Native Paper][65]
335
- - [Readability.js][66]
336
- - [Redux][67]
337
- - [Saleor][68]
338
- - [Semantic UI React][69]
339
- - [Shepherd][70]
340
- - [Tabler][71] + [tabler-icons][72]
341
- - Swagger ([swagger-ui][73] + [swagger-editor][74])
342
- - [Repositories that depend on release-it][75]
343
- - GitHub search for [path:\*\*/.release-it.json][76]
344
-
345
- ## Legacy Node.js
346
-
347
- The latest major version is v17, supporting Node.js 18 and up (as Node.js v16 is EOL). The previous major version was
348
- v16, supporting Node.js 16. Use release-it v15 for environments running Node.js v14. Also see [CHANGELOG.md][77].
332
+ - [jQuery][62]
333
+ - [Madge][63]
334
+ - [Metalsmith][64]
335
+ - [Node-Redis][65]
336
+ - [React Native Paper][66]
337
+ - [Readability.js][67]
338
+ - [Redux][68]
339
+ - [Saleor][69]
340
+ - [Semantic UI React][70]
341
+ - [Shepherd][71]
342
+ - [Tabler][72] + [tabler-icons][73]
343
+ - Swagger ([swagger-ui][74] + [swagger-editor][75])
344
+ - [Repositories that depend on release-it][76]
345
+ - GitHub search for [path:\*\*/.release-it.json][77]
346
+
347
+ ## Node.js version support
348
+
349
+ The latest major version is v18, supporting Node.js 20 and up:
350
+
351
+ | release-it | Node.js |
352
+ | :--------: | :-----: |
353
+ | v18 | v20 |
354
+ | v17 | v18 |
355
+ | v16 | v16 |
356
+ | v15 | v14 |
357
+
358
+ Also see [CHANGELOG.md][78] for dates and details.
349
359
 
350
360
  ## Links
351
361
 
352
- - See [CHANGELOG.md][77] for major/breaking updates, and [releases][78] for a detailed version history.
353
- - To **contribute**, please read [CONTRIBUTING.md][79] first.
354
- - Please [open an issue][80] if anything is missing or unclear in this documentation.
362
+ - See [CHANGELOG.md][78] for major/breaking updates, and [releases][79] for a detailed version history.
363
+ - To **contribute**, please read [CONTRIBUTING.md][80] first.
364
+ - Please [open an issue][81] if anything is missing or unclear in this documentation.
355
365
 
356
366
  ## License
357
367
 
358
- [MIT][81]
368
+ [MIT][82]
359
369
 
360
370
  Are you using release-it at work? Please consider [sponsoring me][14]!
361
371
 
@@ -420,23 +430,24 @@ Are you using release-it at work? Please consider [sponsoring me][14]!
420
430
  [59]: https://github.com/halo-dev/halo
421
431
  [60]: https://github.com/StevenBlack/hosts
422
432
  [61]: https://github.com/js-cookie/js-cookie
423
- [62]: https://github.com/pahen/madge
424
- [63]: https://github.com/metalsmith/metalsmith
425
- [64]: https://github.com/redis/node-redis
426
- [65]: https://github.com/callstack/react-native-paper
427
- [66]: https://github.com/mozilla/readability
428
- [67]: https://github.com/reduxjs/redux
429
- [68]: https://github.com/saleor/saleor
430
- [69]: https://github.com/Semantic-Org/Semantic-UI-React
431
- [70]: https://github.com/shipshapecode/shepherd
432
- [71]: https://github.com/tabler/tabler
433
- [72]: https://github.com/tabler/tabler-icons
434
- [73]: https://github.com/swagger-api/swagger-ui
435
- [74]: https://github.com/swagger-api/swagger-editor
436
- [75]: https://github.com/release-it/release-it/network/dependents
437
- [76]: https://github.com/search?q=path%3A**%2F.release-it.json&type=code
438
- [77]: ./CHANGELOG.md
439
- [78]: https://github.com/release-it/release-it/releases
440
- [79]: ./.github/CONTRIBUTING.md
441
- [80]: https://github.com/release-it/release-it/issues/new
442
- [81]: ./LICENSE
433
+ [62]: https://github.com/jquery/jquery
434
+ [63]: https://github.com/pahen/madge
435
+ [64]: https://github.com/metalsmith/metalsmith
436
+ [65]: https://github.com/redis/node-redis
437
+ [66]: https://github.com/callstack/react-native-paper
438
+ [67]: https://github.com/mozilla/readability
439
+ [68]: https://github.com/reduxjs/redux
440
+ [69]: https://github.com/saleor/saleor
441
+ [70]: https://github.com/Semantic-Org/Semantic-UI-React
442
+ [71]: https://github.com/shipshapecode/shepherd
443
+ [72]: https://github.com/tabler/tabler
444
+ [73]: https://github.com/tabler/tabler-icons
445
+ [74]: https://github.com/swagger-api/swagger-ui
446
+ [75]: https://github.com/swagger-api/swagger-editor
447
+ [76]: https://github.com/release-it/release-it/network/dependents
448
+ [77]: https://github.com/search?q=path%3A**%2F.release-it.json&type=code
449
+ [78]: ./CHANGELOG.md
450
+ [79]: https://github.com/release-it/release-it/releases
451
+ [80]: ./.github/CONTRIBUTING.md
452
+ [81]: https://github.com/release-it/release-it/issues/new
453
+ [82]: ./LICENSE
@@ -65,6 +65,9 @@
65
65
  "certificateAuthorityFile": null,
66
66
  "secure": null,
67
67
  "assets": null,
68
+ "useIdsForUrls": false,
69
+ "useGenericPackageRepositoryForAssets": false,
70
+ "genericPackageRepositoryName": "release-it",
68
71
  "origin": null,
69
72
  "skipChecks": false
70
73
  }
@@ -27,7 +27,7 @@ class GitBase extends Plugin {
27
27
 
28
28
  getLatestVersion() {
29
29
  const { tagTemplate, latestTag } = this.config.getContext();
30
- const prefix = tagTemplate.replace(/\$\{version\}/, '');
30
+ const prefix = format(tagTemplate.replace(/\$\{version\}/, ''), this.config.getContext());
31
31
  return latestTag ? latestTag.replace(prefix, '').replace(/^v/, '') : null;
32
32
  }
33
33
 
@@ -48,10 +48,13 @@ class GitRelease extends GitBase {
48
48
  }
49
49
 
50
50
  afterRelease() {
51
- const { isReleased, releaseUrl } = this.getContext();
51
+ const { isReleased, releaseUrl, discussionUrl } = this.getContext();
52
52
  if (isReleased) {
53
53
  this.log.log(`🔗 ${releaseUrl}`);
54
54
  }
55
+ if (discussionUrl) {
56
+ this.log.log(`🔗 ${discussionUrl}`);
57
+ }
55
58
  }
56
59
  }
57
60
 
@@ -13,6 +13,10 @@ import Release from '../GitRelease.js';
13
13
  import prompts from './prompts.js';
14
14
  import { getCommitsFromChangelog, getResolvedIssuesFromChangelog, searchQueries } from './util.js';
15
15
 
16
+ /**
17
+ * @typedef {import('@octokit/rest').RestEndpointMethodTypes['repos']['createRelease']['parameters']} CreateReleaseOptions
18
+ */
19
+
16
20
  /**
17
21
  * @typedef {import('@octokit/rest').RestEndpointMethodTypes['repos']['createRelease']['parameters']} CreateReleaseOptions
18
22
  */
@@ -209,7 +213,14 @@ class GitHub extends Release {
209
213
 
210
214
  getOctokitReleaseOptions(options = {}) {
211
215
  const { owner, project: repo } = this.getContext('repo');
212
- const { releaseName, draft = false, preRelease = false, autoGenerate = false, makeLatest = true } = this.options;
216
+ const {
217
+ releaseName,
218
+ draft = false,
219
+ preRelease = false,
220
+ autoGenerate = false,
221
+ makeLatest = true,
222
+ discussionCategoryName = undefined
223
+ } = this.options;
213
224
  const { tagName } = this.config.getContext();
214
225
  const { version, releaseNotes, isUpdate } = this.getContext();
215
226
  const { isPreRelease } = parseVersion(version);
@@ -228,7 +239,8 @@ class GitHub extends Release {
228
239
  body,
229
240
  draft,
230
241
  prerelease: isPreRelease || preRelease,
231
- generate_release_notes: autoGenerate
242
+ generate_release_notes: autoGenerate,
243
+ discussion_category_name: discussionCategoryName
232
244
  };
233
245
  return Object.assign(options, contextOptions);
234
246
  }
@@ -257,9 +269,21 @@ class GitHub extends Release {
257
269
  this.debug(options);
258
270
  const response = await this.client.repos.createRelease(options);
259
271
  this.debug(response.data);
260
- const { html_url, upload_url, id } = response.data;
261
- this.setContext({ isReleased: true, releaseId: id, releaseUrl: html_url, upload_url });
262
- this.config.setContext({ isReleased: true, releaseId: id, releaseUrl: html_url, upload_url });
272
+ const { html_url, upload_url, id, discussion_url } = response.data;
273
+ this.setContext({
274
+ isReleased: true,
275
+ releaseId: id,
276
+ releaseUrl: html_url,
277
+ upload_url,
278
+ discussionUrl: discussion_url
279
+ });
280
+ this.config.setContext({
281
+ isReleased: true,
282
+ releaseId: id,
283
+ releaseUrl: html_url,
284
+ upload_url,
285
+ discussionUrl: discussion_url
286
+ });
263
287
  this.log.verbose(`octokit repos.createRelease: done (${response.headers.location})`);
264
288
  return response.data;
265
289
  } catch (err) {
@@ -2,6 +2,7 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { globby } from 'globby';
4
4
  import _ from 'lodash';
5
+ import { Agent } from 'undici';
5
6
  import Release from '../GitRelease.js';
6
7
  import { format, e } from '../../util.js';
7
8
  import prompts from './prompts.js';
@@ -16,16 +17,18 @@ class GitLab extends Release {
16
17
  this.registerPrompts(prompts);
17
18
  this.assets = [];
18
19
  const { certificateAuthorityFile, secure } = this.options;
20
+ this.certificateAuthorityOption = {};
19
21
 
20
- const httpsOptions = {
21
- certificateAuthority: certificateAuthorityFile ? fs.readFileSync(certificateAuthorityFile) : undefined,
22
- rejectUnauthorized: typeof secure === 'boolean' ? secure : undefined
23
- };
24
-
25
- // Remove keys with undefined values
26
- const https = _.pickBy(httpsOptions, value => value !== undefined);
22
+ const needsCustomAgent = Boolean(secure === false || certificateAuthorityFile);
27
23
 
28
- this.certificateAuthorityOption = _.isEmpty(https) ? {} : { https };
24
+ if (needsCustomAgent) {
25
+ this.certificateAuthorityOption.dispatcher = new Agent({
26
+ connect: {
27
+ rejectUnauthorized: secure,
28
+ ca: certificateAuthorityFile ? fs.readFileSync(certificateAuthorityFile) : undefined
29
+ }
30
+ });
31
+ }
29
32
  }
30
33
 
31
34
  async init() {
@@ -167,34 +170,51 @@ class GitLab extends Release {
167
170
  'user-agent': 'webpro/release-it',
168
171
  [tokenHeader]: this.token
169
172
  };
173
+ // When using fetch() with FormData bodies, we should not set the Content-Type header.
174
+ // See: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest_API/Using_FormData_Objects#sending_files_using_a_formdata_object
175
+ if (!(options.body instanceof FormData)) {
176
+ headers['Content-Type'] = typeof options.json !== 'undefined' ? 'application/json' : 'text/plain';
177
+ }
170
178
  const requestOptions = {
171
179
  method,
172
180
  headers,
173
181
  ...this.certificateAuthorityOption
174
182
  };
175
183
 
176
- const response = await fetch(
177
- url,
178
- options.json || options.body
179
- ? {
180
- ...requestOptions,
181
- body: options.json ? JSON.stringify(options.json) : options.body
182
- }
183
- : requestOptions
184
- );
185
- const body = await response.json();
186
- this.debug(body);
187
- return body;
184
+ try {
185
+ const response = await fetch(
186
+ url,
187
+ options.json || options.body
188
+ ? {
189
+ ...requestOptions,
190
+ body: options.json ? JSON.stringify(options.json) : options.body
191
+ }
192
+ : requestOptions
193
+ );
194
+
195
+ if (!response.ok) {
196
+ const body = await response.json();
197
+ throw new Error(body.error);
198
+ }
199
+
200
+ const body = await response.json();
201
+ this.debug(body);
202
+ return body;
203
+ } catch (err) {
204
+ this.debug(err);
205
+ throw err;
206
+ }
188
207
  }
189
208
 
190
209
  async createRelease() {
191
210
  const { releaseName } = this.options;
192
- const { tagName } = this.config.getContext();
211
+ const { tagName, branchName, git: { tagAnnotation } = {} } = this.config.getContext();
193
212
  const { id, releaseNotes, repo, origin } = this.getContext();
194
213
  const { isDryRun } = this.config;
195
214
  const name = format(releaseName, this.config.getContext());
215
+ const tagMessage = format(tagAnnotation, this.config.getContext());
196
216
  const description = releaseNotes || '-';
197
- const releaseUrl = `${origin}/${repo.repository}/-/releases`;
217
+ const releaseUrl = `${origin}/${repo.repository}/-/releases/${tagName}`;
198
218
  const releaseMilestones = this.getReleaseMilestones();
199
219
 
200
220
  this.log.exec(`gitlab releases#createRelease "${name}" (${tagName})`, { isDryRun });
@@ -208,7 +228,9 @@ class GitLab extends Release {
208
228
  const options = {
209
229
  json: {
210
230
  name,
231
+ ref: branchName,
211
232
  tag_name: tagName,
233
+ tag_message: tagMessage,
212
234
  description
213
235
  }
214
236
  };
@@ -224,10 +246,11 @@ class GitLab extends Release {
224
246
  }
225
247
 
226
248
  try {
227
- await this.request(endpoint, options);
249
+ const body = await this.request(endpoint, options);
250
+ const releaseUrlSelf = body._links?.self ?? releaseUrl;
228
251
  this.log.verbose('gitlab releases#createRelease: done');
229
- this.setContext({ isReleased: true, releaseUrl });
230
- this.config.setContext({ isReleased: true, releaseUrl });
252
+ this.setContext({ isReleased: true, releaseUrl: releaseUrlSelf });
253
+ this.config.setContext({ isReleased: true, releaseUrl: releaseUrlSelf });
231
254
  return true;
232
255
  } catch (err) {
233
256
  this.debug(err);
@@ -237,24 +260,47 @@ class GitLab extends Release {
237
260
 
238
261
  async uploadAsset(filePath) {
239
262
  const name = path.basename(filePath);
240
- const { id, origin, repo } = this.getContext();
241
- const endpoint = `projects/${id}/uploads`;
242
-
243
- const body = new FormData();
244
- const rawData = await fs.promises.readFile(filePath);
245
- body.set('file', new Blob([rawData]), name);
246
- const options = { body };
247
-
248
- try {
249
- const body = await this.request(endpoint, options);
250
- this.log.verbose(`gitlab releases#uploadAsset: done (${body.url})`);
251
- this.assets.push({
252
- name,
253
- url: `${origin}/${repo.repository}${body.url}`
254
- });
255
- } catch (err) {
256
- this.debug(err);
257
- throw err;
263
+ const { useIdsForUrls, useGenericPackageRepositoryForAssets, genericPackageRepositoryName } = this.options;
264
+ const { id, origin, repo, version, baseUrl } = this.getContext();
265
+ const endpoint = useGenericPackageRepositoryForAssets
266
+ ? `projects/${id}/packages/generic/${genericPackageRepositoryName}/${version}/${name}`
267
+ : `projects/${id}/uploads`;
268
+ if (useGenericPackageRepositoryForAssets) {
269
+ const options = {
270
+ method: 'PUT',
271
+ body: await fs.promises.readFile(filePath)
272
+ };
273
+ try {
274
+ const body = await this.request(endpoint, options);
275
+ if (!(body.message && body.message == '201 Created')) {
276
+ throw new Error(`GitLab asset upload failed`);
277
+ }
278
+ this.log.verbose(`gitlab releases#uploadAsset: done (${endpoint})`);
279
+ this.assets.push({
280
+ name,
281
+ url: `${baseUrl}/${endpoint}`
282
+ });
283
+ } catch (err) {
284
+ this.debug(err);
285
+ throw err;
286
+ }
287
+ } else {
288
+ const body = new FormData();
289
+ const rawData = await fs.promises.readFile(filePath);
290
+ body.set('file', new Blob([rawData]), name);
291
+ const options = { body };
292
+
293
+ try {
294
+ const body = await this.request(endpoint, options);
295
+ this.log.verbose(`gitlab releases#uploadAsset: done (${body.url})`);
296
+ this.assets.push({
297
+ name,
298
+ url: useIdsForUrls ? `${origin}${body.full_path}` : `${origin}/${repo.repository}${body.url}`
299
+ });
300
+ } catch (err) {
301
+ this.debug(err);
302
+ throw err;
303
+ }
258
304
  }
259
305
  }
260
306
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "release-it",
3
- "version": "18.0.0-next.3",
3
+ "version": "18.0.0",
4
4
  "description": "Generic CLI tool to automate versioning and package publishing-related tasks.",
5
5
  "keywords": [
6
6
  "build",
@@ -69,7 +69,7 @@
69
69
  "lint": "eslint lib test",
70
70
  "format": "prettier --write eslint.config.mjs \"{lib,test}/**/*.js\"",
71
71
  "docs": "remark README.md 'docs/**/*.md' '.github/*.md' -o",
72
- "test": "ava --no-worker-threads && installed-check",
72
+ "test": "ava --no-worker-threads && installed-check --ignore ava",
73
73
  "release": "./bin/release-it.js"
74
74
  },
75
75
  "author": {
@@ -81,60 +81,61 @@
81
81
  "@iarna/toml": "2.2.5",
82
82
  "@octokit/rest": "21.0.2",
83
83
  "async-retry": "1.3.3",
84
- "chalk": "5.3.0",
85
- "ci-info": "^4.0.0",
84
+ "chalk": "5.4.1",
85
+ "ci-info": "^4.1.0",
86
86
  "cosmiconfig": "9.0.0",
87
- "execa": "9.4.0",
88
- "git-url-parse": "15.0.0",
87
+ "execa": "9.5.2",
88
+ "git-url-parse": "16.0.0",
89
89
  "globby": "14.0.2",
90
- "inquirer": "11.0.2",
90
+ "inquirer": "12.3.0",
91
91
  "issue-parser": "7.0.1",
92
92
  "lodash": "4.17.21",
93
93
  "mime-types": "2.1.35",
94
94
  "new-github-release-url": "2.0.0",
95
95
  "open": "10.1.0",
96
- "ora": "8.1.0",
96
+ "ora": "8.1.1",
97
97
  "os-name": "6.0.0",
98
- "proxy-agent": "6.4.0",
98
+ "proxy-agent": "6.5.0",
99
99
  "semver": "7.6.3",
100
100
  "shelljs": "0.8.5",
101
+ "undici": "6.21.0",
101
102
  "update-notifier": "7.3.1",
102
103
  "url-join": "5.0.0",
103
- "wildcard-match": "5.1.3",
104
+ "wildcard-match": "5.1.4",
104
105
  "yargs-parser": "21.1.1"
105
106
  },
106
107
  "devDependencies": {
107
- "@eslint/compat": "1.1.1",
108
- "@eslint/eslintrc": "3.1.0",
109
- "@eslint/js": "9.11.0",
110
- "@octokit/request-error": "6.1.4",
111
- "@types/node": "22.5.5",
112
- "ava": "6.1.3",
113
- "eslint": "9.11.0",
108
+ "@eslint/compat": "1.2.4",
109
+ "@eslint/eslintrc": "3.2.0",
110
+ "@eslint/js": "9.17.0",
111
+ "@octokit/request-error": "6.1.6",
112
+ "@types/node": "20.17.11",
113
+ "ava": "6.2.0",
114
+ "eslint": "9.17.0",
114
115
  "eslint-config-prettier": "9.1.0",
115
116
  "eslint-plugin-ava": "15.0.1",
116
- "eslint-plugin-import-x": "4.2.1",
117
+ "eslint-plugin-import-x": "4.6.1",
117
118
  "eslint-plugin-prettier": "5.2.1",
118
119
  "fs-monkey": "1.0.6",
119
- "globals": "15.9.0",
120
+ "globals": "15.14.0",
120
121
  "installed-check": "9.3.0",
121
- "knip": "5.30.2",
122
- "memfs": "4.12.0",
122
+ "knip": "5.41.1",
123
+ "memfs": "4.15.3",
123
124
  "mock-stdio": "1.0.3",
124
125
  "nock": "14.0.0-beta.8",
125
- "prettier": "3.3.3",
126
+ "prettier": "3.4.2",
126
127
  "remark-cli": "12.0.1",
127
- "remark-preset-webpro": "1.1.0",
128
+ "remark-preset-webpro": "1.1.1",
128
129
  "sinon": "19.0.2",
129
130
  "strip-ansi": "7.1.0",
130
- "typescript": "5.6.2"
131
+ "typescript": "5.7.2"
131
132
  },
132
133
  "overrides": {
133
134
  "pac-resolver": "7.0.1",
134
135
  "socks": "2.8.3"
135
136
  },
136
137
  "engines": {
137
- "node": "^20.9.0 || ^22.0.0"
138
+ "node": "^20.9.0 || >=22.0.0"
138
139
  },
139
140
  "remarkConfig": {
140
141
  "plugins": [
@@ -32,6 +32,10 @@
32
32
  "type": "boolean",
33
33
  "default": false
34
34
  },
35
+ "discussionCategoryName": {
36
+ "$comment": "Create discussion of the specified category and link to the GitHub Release",
37
+ "type": "string"
38
+ },
35
39
  "tokenRef": {
36
40
  "type": "string",
37
41
  "default": "GITHUB_TOKEN"
@@ -28,7 +28,7 @@
28
28
  "type": "boolean",
29
29
  "default": false
30
30
  },
31
- "milesstones": {
31
+ "milestones": {
32
32
  "type": "array",
33
33
  "items": {
34
34
  "type": "string"
@@ -52,6 +52,18 @@
52
52
  "assets": {
53
53
  "default": null
54
54
  },
55
+ "useIdsForUrls": {
56
+ "type": "boolean",
57
+ "default": false
58
+ },
59
+ "useGenericPackageRepositoryForAssets": {
60
+ "type": "boolean",
61
+ "default": false
62
+ },
63
+ "genericPackageRepositoryName": {
64
+ "type": "string",
65
+ "default": "release-it"
66
+ },
55
67
  "origin": {
56
68
  "type": "string",
57
69
  "default": null
package/test/github.js CHANGED
@@ -508,3 +508,40 @@ test('should generate search queries correctly', t => {
508
508
  'Each query should not exceed 256 characters after encoding'
509
509
  );
510
510
  });
511
+
512
+ test('should create auto-generated discussion', async t => {
513
+ const options = {
514
+ git,
515
+ github: {
516
+ pushRepo,
517
+ tokenRef,
518
+ release: true,
519
+ releaseName: 'Release ${tagName}',
520
+ autoGenerate: false,
521
+ discussionCategoryName: 'Announcement'
522
+ }
523
+ };
524
+ const github = factory(GitHub, { options });
525
+ const exec = sinon.stub(github.shell, 'exec').callThrough();
526
+ exec.withArgs('git describe --tags --match=* --abbrev=0').resolves('2.0.1');
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
+ }
538
+ });
539
+
540
+ await runTasks(github);
541
+
542
+ const { isReleased, releaseUrl, discussionUrl } = github.getContext();
543
+ t.true(isReleased);
544
+ t.is(releaseUrl, 'https://github.com/user/repo/releases/tag/2.0.2');
545
+ t.is(discussionUrl, 'https://github.com/user/repo/discussions/1');
546
+ exec.restore();
547
+ });