release-it 18.0.0-next.2 → 18.0.0-next.4

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
@@ -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,35 @@ 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]
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]
344
346
 
345
347
  ## Legacy Node.js
346
348
 
347
349
  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].
350
+ v16, supporting Node.js 16. Use release-it v15 for environments running Node.js v14. Also see [CHANGELOG.md][78].
349
351
 
350
352
  ## Links
351
353
 
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.
354
+ - See [CHANGELOG.md][78] for major/breaking updates, and [releases][79] for a detailed version history.
355
+ - To **contribute**, please read [CONTRIBUTING.md][80] first.
356
+ - Please [open an issue][81] if anything is missing or unclear in this documentation.
355
357
 
356
358
  ## License
357
359
 
358
- [MIT][81]
360
+ [MIT][82]
359
361
 
360
362
  Are you using release-it at work? Please consider [sponsoring me][14]!
361
363
 
@@ -420,23 +422,24 @@ Are you using release-it at work? Please consider [sponsoring me][14]!
420
422
  [59]: https://github.com/halo-dev/halo
421
423
  [60]: https://github.com/StevenBlack/hosts
422
424
  [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
425
+ [62]: https://github.com/jquery/jquery
426
+ [63]: https://github.com/pahen/madge
427
+ [64]: https://github.com/metalsmith/metalsmith
428
+ [65]: https://github.com/redis/node-redis
429
+ [66]: https://github.com/callstack/react-native-paper
430
+ [67]: https://github.com/mozilla/readability
431
+ [68]: https://github.com/reduxjs/redux
432
+ [69]: https://github.com/saleor/saleor
433
+ [70]: https://github.com/Semantic-Org/Semantic-UI-React
434
+ [71]: https://github.com/shipshapecode/shepherd
435
+ [72]: https://github.com/tabler/tabler
436
+ [73]: https://github.com/tabler/tabler-icons
437
+ [74]: https://github.com/swagger-api/swagger-ui
438
+ [75]: https://github.com/swagger-api/swagger-editor
439
+ [76]: https://github.com/release-it/release-it/network/dependents
440
+ [77]: https://github.com/search?q=path%3A**%2F.release-it.json&type=code
441
+ [78]: ./CHANGELOG.md
442
+ [79]: https://github.com/release-it/release-it/releases
443
+ [80]: ./.github/CONTRIBUTING.md
444
+ [81]: https://github.com/release-it/release-it/issues/new
445
+ [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,14 @@ 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
+
20
+ /**
21
+ * @typedef {import('@octokit/rest').RestEndpointMethodTypes['repos']['createRelease']['parameters']} CreateReleaseOptions
22
+ */
23
+
16
24
  const pkg = readJSON(new URL('../../../package.json', import.meta.url));
17
25
 
18
26
  const docs = 'https://git.io/release-it-github';
@@ -205,23 +213,36 @@ class GitHub extends Release {
205
213
 
206
214
  getOctokitReleaseOptions(options = {}) {
207
215
  const { owner, project: repo } = this.getContext('repo');
208
- const { releaseName, draft = false, preRelease = false, autoGenerate = false } = this.options;
216
+ const {
217
+ releaseName,
218
+ draft = false,
219
+ preRelease = false,
220
+ autoGenerate = false,
221
+ makeLatest = true,
222
+ discussionCategoryName = undefined
223
+ } = this.options;
209
224
  const { tagName } = this.config.getContext();
210
225
  const { version, releaseNotes, isUpdate } = this.getContext();
211
226
  const { isPreRelease } = parseVersion(version);
212
227
  const name = format(releaseName, this.config.getContext());
213
228
  const body = autoGenerate ? (isUpdate ? null : '') : truncateBody(releaseNotes);
214
229
 
215
- return Object.assign(options, {
230
+ /**
231
+ * @type {CreateReleaseOptions}
232
+ */
233
+ const contextOptions = {
216
234
  owner,
235
+ make_latest: makeLatest.toString(),
217
236
  repo,
218
237
  tag_name: tagName,
219
238
  name,
220
239
  body,
221
240
  draft,
222
241
  prerelease: isPreRelease || preRelease,
223
- generate_release_notes: autoGenerate
224
- });
242
+ generate_release_notes: autoGenerate,
243
+ discussion_category_name: discussionCategoryName
244
+ };
245
+ return Object.assign(options, contextOptions);
225
246
  }
226
247
 
227
248
  retry(fn) {
@@ -248,9 +269,21 @@ class GitHub extends Release {
248
269
  this.debug(options);
249
270
  const response = await this.client.repos.createRelease(options);
250
271
  this.debug(response.data);
251
- const { html_url, upload_url, id } = response.data;
252
- this.setContext({ isReleased: true, releaseId: id, releaseUrl: html_url, upload_url });
253
- 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
+ });
254
287
  this.log.verbose(`octokit repos.createRelease: done (${response.headers.location})`);
255
288
  return response.data;
256
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.2",
3
+ "version": "18.0.0-next.4",
4
4
  "description": "Generic CLI tool to automate versioning and package publishing-related tasks.",
5
5
  "keywords": [
6
6
  "build",
@@ -81,53 +81,54 @@
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.3.1",
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": "10.2.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.10.0",
110
- "@octokit/request-error": "6.1.4",
111
- "@types/node": "22.5.4",
112
- "ava": "6.1.3",
113
- "eslint": "9.10.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.5",
112
+ "@types/node": "20.16.10",
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.1",
122
- "memfs": "4.11.1",
122
+ "knip": "5.41.1",
123
+ "memfs": "4.15.1",
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
- "sinon": "19.0.0",
128
+ "remark-preset-webpro": "1.1.1",
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",
@@ -20,6 +20,10 @@
20
20
  "type": "boolean",
21
21
  "default": false
22
22
  },
23
+ "makeLatest": {
24
+ "anyOf": [{ "type": "boolean" }, { "const": "legacy" }],
25
+ "default": true
26
+ },
23
27
  "preRelease": {
24
28
  "type": "boolean",
25
29
  "default": false
@@ -28,6 +32,10 @@
28
32
  "type": "boolean",
29
33
  "default": false
30
34
  },
35
+ "discussionCategoryName": {
36
+ "$comment": "Create discussion of the specified category and link to the GitHub Release",
37
+ "type": "string"
38
+ },
31
39
  "tokenRef": {
32
40
  "type": "string",
33
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
+ });