@semantic-release/github 10.2.0 → 10.3.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
@@ -80,24 +80,26 @@ When using the _GITHUB_TOKEN_, the **minimum required permissions** are:
80
80
 
81
81
  ### Options
82
82
 
83
- | Option | Description | Default |
84
- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
85
- | `githubUrl` | The GitHub server endpoint. | `GH_URL` or `GITHUB_URL` environment variable. |
86
- | `githubApiPathPrefix` | The GitHub API prefix, relative to `githubUrl`. | `GH_PREFIX` or `GITHUB_PREFIX` environment variable. |
87
- | `githubApiUrl` | The GitHub API endpoint. Note that this overwrites `githubApiPathPrefix`. | `GITHUB_API_URL` environment variable. |
88
- | `proxy` | The proxy to use to access the GitHub API. Set to `false` to disable usage of proxy. See [proxy](#proxy). | `HTTP_PROXY` environment variable. |
89
- | `assets` | An array of files to upload to the release. See [assets](#assets). | - |
90
- | `successComment` | The comment to add to each issue and pull request resolved by the release. Set to `false` to disable commenting on issues and pull requests. See [successComment](#successcomment). | `:tada: This issue has been resolved in version ${nextRelease.version} :tada:\n\nThe release is available on [GitHub release](<github_release_url>)` |
91
- | `failComment` | The content of the issue created when a release fails. Set to `false` to disable opening an issue when a release fails. See [failComment](#failcomment). | Friendly message with links to **semantic-release** documentation and support, with the list of errors that caused the release to fail. |
92
- | `failTitle` | The title of the issue created when a release fails. Set to `false` to disable opening an issue when a release fails. | `The automated release is failing 🚨` |
93
- | `labels` | The [labels](https://help.github.com/articles/about-labels) to add to the issue created when a release fails. Set to `false` to not add any label. | `['semantic-release']` |
94
- | `assignees` | The [assignees](https://help.github.com/articles/assigning-issues-and-pull-requests-to-other-github-users) to add to the issue created when a release fails. | - |
95
- | `releasedLabels` | The [labels](https://help.github.com/articles/about-labels) to add to each issue and pull request resolved by the release. Set to `false` to not add any label. See [releasedLabels](#releasedlabels). | `['released<%= nextRelease.channel ? \` on @\${nextRelease.channel}\` : "" %>']- |
96
- | `addReleases` | Will add release links to the GitHub Release. Can be `false`, `"bottom"` or `"top"`. See [addReleases](#addReleases). | `false` |
97
- | `draftRelease` | A boolean indicating if a GitHub Draft Release should be created instead of publishing an actual GitHub Release. | `false` |
98
- | `releaseNameTemplate` | A [Lodash template](https://lodash.com/docs#template) to customize the github release's name | `<%= nextRelease.name %>` |
99
- | `releaseBodyTemplate` | A [Lodash template](https://lodash.com/docs#template) to customize the github release's body | `<%= nextRelease.notes %>` |
100
- | `discussionCategoryName` | The category name in which to create a linked discussion to the release. Set to `false` to disable creating discussion for a release. | `false` |
83
+ | Option | Description | Default |
84
+ | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
85
+ | `githubUrl` | The GitHub server endpoint. | `GH_URL` or `GITHUB_URL` environment variable. |
86
+ | `githubApiPathPrefix` | The GitHub API prefix, relative to `githubUrl`. | `GH_PREFIX` or `GITHUB_PREFIX` environment variable. |
87
+ | `githubApiUrl` | The GitHub API endpoint. Note that this overwrites `githubApiPathPrefix`. | `GITHUB_API_URL` environment variable. |
88
+ | `proxy` | The proxy to use to access the GitHub API. Set to `false` to disable usage of proxy. See [proxy](#proxy). | `HTTP_PROXY` environment variable. |
89
+ | `assets` | An array of files to upload to the release. See [assets](#assets). | - |
90
+ | `successComment` | The comment to add to each issue and pull request resolved by the release. Set to `false` to disable commenting on issues and pull requests. See [successComment](#successcomment). | `:tada: This issue has been resolved in version ${nextRelease.version} :tada:\n\nThe release is available on [GitHub release](<github_release_url>)` |
91
+ | `successCommentCondition` | Use this as condition, when to comment on issues or pull requests. See [successCommentCondition](#successCommentCondition) | - |
92
+ | `failComment` | The content of the issue created when a release fails. Set to `false` to disable opening an issue when a release fails. See [failComment](#failcomment). | Friendly message with links to **semantic-release** documentation and support, with the list of errors that caused the release to fail. |
93
+ | `failTitle` | The title of the issue created when a release fails. Set to `false` to disable opening an issue when a release fails. | `The automated release is failing 🚨` |
94
+ | `failCommentCondition` | Use this as condition, when to comment on or create an issues in case of failures. See [failCommentCondition](#failCommentCondition). | - |
95
+ | `labels` | The [labels](https://help.github.com/articles/about-labels) to add to the issue created when a release fails. Set to `false` to not add any label. | `['semantic-release']` |
96
+ | `assignees` | The [assignees](https://help.github.com/articles/assigning-issues-and-pull-requests-to-other-github-users) to add to the issue created when a release fails. | - |
97
+ | `releasedLabels` | The [labels](https://help.github.com/articles/about-labels) to add to each issue and pull request resolved by the release. Set to `false` to not add any label. See [releasedLabels](#releasedlabels). | `['released<%= nextRelease.channel ? \` on @\${nextRelease.channel}\` : "" %>']- |
98
+ | `addReleases` | Will add release links to the GitHub Release. Can be `false`, `"bottom"` or `"top"`. See [addReleases](#addReleases). | `false` |
99
+ | `draftRelease` | A boolean indicating if a GitHub Draft Release should be created instead of publishing an actual GitHub Release. | `false` |
100
+ | `releaseNameTemplate` | A [Lodash template](https://lodash.com/docs#template) to customize the github release's name | `<%= nextverison.name %>` |
101
+ | `releaseBodyTemplate` | A [Lodash template](https://lodash.com/docs#template) to customize the github release's body | `<%= nextverison.notes %>` |
102
+ | `discussionCategoryName` | The category name in which to create a linked discussion to the release. Set to `false` to disable creating discussion for a release. | `false` |
101
103
 
102
104
  #### proxy
103
105
 
@@ -181,6 +183,29 @@ The `successComment` `This ${issue.pull_request ? 'pull request' : 'issue'} is i
181
183
 
182
184
  > This pull request is included in version 1.0.0
183
185
 
186
+ #### successCommentCondition
187
+
188
+ The message for the issue comments is generated with [Lodash template](https://lodash.com/docs#template). The following variables are available:
189
+
190
+ | Parameter | Description |
191
+ | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
192
+ | `branch` | `Object` with `name`, `type`, `channel`, `range` and `prerelease` properties of the branch from which the release is done. |
193
+ | `lastRelease` | `Object` with `version`, `channel`, `gitTag` and `gitHead` of the last release. |
194
+ | `nextRelease` | `Object` with `version`, `channel`, `gitTag`, `gitHead` and `notes` of the release being done. |
195
+ | `commits` | `Array` of commit `Object`s with `hash`, `subject`, `body` `message` and `author`. |
196
+ | `releases` | `Array` with a release `Object`s for each release published, with optional release data such as `name` and `url`. |
197
+ | `issue` | A [GitHub API Pull Request object](https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#get-a-pull-request) for pull requests related to a commit, or an `Object` with the `number` property for issues resolved via [keywords](https://help.github.com/articles/closing-issues-using-keywords) |
198
+
199
+ ##### successCommentCondition example
200
+
201
+ - do not create any comments at all: set to `false` or templating: `"<% return false; %>"`
202
+ - to only comment on issues: `"<% return !issue.pull_request; %>"`
203
+ - to only comment on pull requests: `"<% return issue.pull_request; %>"`
204
+ - to avoid comment on PRs or issues created by Bots: `"<% return issue.user.type !== 'Bot'; %>"`
205
+ - you can use labels to filter issues: `"<% return issue.labels?.includes('semantic-release-relevant'); %>"`
206
+
207
+ > check the [GitHub API issue object](https://docs.github.com/en/rest/issues/issues?apiVersion=2022-11-28#get-an-issue) for properties which can be used for the filter
208
+
184
209
  #### failComment
185
210
 
186
211
  The message for the issue content is generated with [Lodash template](https://lodash.com/docs#template). The following variables are available:
@@ -199,6 +224,27 @@ The `failComment` `This release from branch ${branch.name} had failed due to the
199
224
  > - Error message 1
200
225
  > - Error message 2
201
226
 
227
+ #### failCommentCondition
228
+
229
+ The message for the issue content is generated with [Lodash template](https://lodash.com/docs#template). The following variables are available:
230
+
231
+ | Parameter | Description |
232
+ | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
233
+ | `branch` | `Object` with `name`, `type`, `channel`, `range` and `prerelease` properties of the branch from which the release is done. |
234
+ | `lastRelease` | `Object` with `version`, `channel`, `gitTag` and `gitHead` of the last release. |
235
+ | `nextRelease` | `Object` with `version`, `channel`, `gitTag`, `gitHead` and `notes` of the release being done. |
236
+ | `commits` | `Array` of commit `Object`s with `hash`, `subject`, `body` `message` and `author`. |
237
+ | `releases` | `Array` with a release `Object`s for each release published, with optional release data such as `name` and `url`. |
238
+ | `issue` | A [GitHub API pull request object](https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#get-a-pull-request) for pull requests related to a commit, or an `Object` with the `number` property for issues resolved via [keywords](https://help.github.com/articles/closing-issues-using-keywords) |
239
+
240
+ ##### failCommentCondition example
241
+
242
+ - do not create any comments at all: set to `false` or templating: `"<% return false; %>"`
243
+ - to only comment on main branch: `"<% return branch.name === 'main' %>"`
244
+ - you can use labels to filter issues, i.e. to not comment if the issue is labeled with `wip`: `"<% return !issue.labels?.includes('wip') %>"`
245
+
246
+ > check the [GitHub API Pull Request Object](https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#get-a-pull-request) for properties which can be used for the filter
247
+
202
248
  #### releasedLabels
203
249
 
204
250
  Each label name is generated with [Lodash template](https://lodash.com/docs#template). The following variables are available:
package/lib/fail.js CHANGED
@@ -23,14 +23,21 @@ export default async function fail(pluginConfig, context, { Octokit }) {
23
23
  githubApiPathPrefix,
24
24
  githubApiUrl,
25
25
  proxy,
26
- failComment,
27
26
  failTitle,
27
+ failComment,
28
+ failCommentCondition,
28
29
  labels,
29
30
  assignees,
30
31
  } = resolveConfig(pluginConfig, context);
31
32
 
32
33
  if (failComment === false || failTitle === false) {
33
34
  logger.log("Skip issue creation.");
35
+ // TODO: use logger.warn() instead of logger.log()
36
+ logger.log(
37
+ `DEPRECATION: 'false' for 'failComment' or 'failTitle' is deprecated and will be removed in a future major version. Use 'failCommentCondition' instead.`,
38
+ );
39
+ } else if (failCommentCondition === false) {
40
+ logger.log("Skip issue creation.");
34
41
  } else {
35
42
  const octokit = new Octokit(
36
43
  toOctokitOptions({
@@ -52,6 +59,15 @@ export default async function fail(pluginConfig, context, { Octokit }) {
52
59
  : getFailComment(branch, errors);
53
60
  const [srIssue] = await findSRIssues(octokit, failTitle, owner, repo);
54
61
 
62
+ const canCommentOnOrCreateIssue = failCommentCondition
63
+ ? template(failCommentCondition)({ ...context, issue: srIssue })
64
+ : true;
65
+
66
+ if (!canCommentOnOrCreateIssue) {
67
+ logger.log("Skip commenting on or creating an issue.");
68
+ return;
69
+ }
70
+
55
71
  if (srIssue) {
56
72
  logger.log("Found existing semantic-release issue #%d.", srIssue.number);
57
73
  const comment = { owner, repo, issue_number: srIssue.number, body };
@@ -8,8 +8,10 @@ export default function resolveConfig(
8
8
  proxy,
9
9
  assets,
10
10
  successComment,
11
+ successCommentCondition,
11
12
  failTitle,
12
13
  failComment,
14
+ failCommentCondition,
13
15
  labels,
14
16
  assignees,
15
17
  releasedLabels,
@@ -30,10 +32,12 @@ export default function resolveConfig(
30
32
  proxy: isNil(proxy) ? env.http_proxy || env.HTTP_PROXY || false : proxy,
31
33
  assets: assets ? castArray(assets) : assets,
32
34
  successComment,
35
+ successCommentCondition,
33
36
  failTitle: isNil(failTitle)
34
37
  ? "The automated release is failing 🚨"
35
38
  : failTitle,
36
39
  failComment,
40
+ failCommentCondition,
37
41
  labels: isNil(labels)
38
42
  ? ["semantic-release"]
39
43
  : labels === false
package/lib/success.js CHANGED
@@ -1,4 +1,4 @@
1
- import { isNil, uniqBy, template, flatten, isEmpty } from "lodash-es";
1
+ import { isNil, uniqBy, template, flatten, isEmpty, merge } from "lodash-es";
2
2
  import pFilter from "p-filter";
3
3
  import AggregateError from "aggregate-error";
4
4
  import issueParser from "issue-parser";
@@ -29,8 +29,10 @@ export default async function success(pluginConfig, context, { Octokit }) {
29
29
  githubApiUrl,
30
30
  proxy,
31
31
  successComment,
32
- failComment,
32
+ successCommentCondition,
33
33
  failTitle,
34
+ failComment,
35
+ failCommentCondition,
34
36
  releasedLabels,
35
37
  addReleases,
36
38
  } = resolveConfig(pluginConfig, context);
@@ -59,6 +61,12 @@ export default async function success(pluginConfig, context, { Octokit }) {
59
61
  logger.log("No commits found in release");
60
62
  }
61
63
  logger.log("Skip commenting on issues and pull requests.");
64
+ // TODO: use logger.warn() instead of logger.log()
65
+ logger.log(
66
+ `DEPRECATION: 'false' for 'successComment' is deprecated and will be removed in a future major version. Use 'successCommentCondition' instead.`,
67
+ );
68
+ } else if (successCommentCondition === false) {
69
+ logger.log("Skip commenting on issues and pull requests.");
62
70
  } else {
63
71
  const parser = issueParser(
64
72
  "github",
@@ -67,16 +75,9 @@ export default async function success(pluginConfig, context, { Octokit }) {
67
75
  const releaseInfos = releases.filter((release) => Boolean(release.name));
68
76
  const shas = commits.map(({ hash }) => hash);
69
77
 
70
- const associatedPRs = [];
71
-
72
- // Split commit shas into chunks of 100 shas
73
- const chunkSize = 100;
74
- const shasChunks = [];
75
- for (let i = 0; i < shas.length; i += chunkSize) {
76
- const chunk = shas.slice(i, i + chunkSize);
77
- shasChunks.push(chunk);
78
- }
79
- for (const chunk of shasChunks) {
78
+ // Get associatedPRs
79
+ const associatedPRs = await inChunks(shas, 100, async (chunk) => {
80
+ const responsePRs = [];
80
81
  const { repository } = await octokit.graphql(
81
82
  buildAssociatedPRsQuery(chunk),
82
83
  { owner, repo },
@@ -85,7 +86,9 @@ export default async function success(pluginConfig, context, { Octokit }) {
85
86
  (item) => item.associatedPullRequests,
86
87
  );
87
88
  for (const { nodes, pageInfo } of responseAssociatedPRs) {
88
- associatedPRs.push(nodes);
89
+ if (nodes.length === 0) continue;
90
+
91
+ responsePRs.push(...buildIssuesOrPRsFromResponseNode(nodes, "PR"));
89
92
  if (pageInfo.hasNextPage) {
90
93
  let cursor = pageInfo.endCursor;
91
94
  let hasNextPage = true;
@@ -95,7 +98,12 @@ export default async function success(pluginConfig, context, { Octokit }) {
95
98
  { owner, repo, sha: response.commit.oid, cursor },
96
99
  );
97
100
  const { associatedPullRequests } = repository.commit;
98
- associatedPRs.push(associatedPullRequests.nodes);
101
+ responsePRs.push(
102
+ ...buildIssuesOrPRsFromResponseNode(
103
+ associatedPullRequests.nodes,
104
+ "PR",
105
+ ),
106
+ );
99
107
  if (associatedPullRequests.pageInfo.hasNextPage) {
100
108
  cursor = associatedPullRequests.pageInfo.endCursor;
101
109
  } else {
@@ -104,7 +112,8 @@ export default async function success(pluginConfig, context, { Octokit }) {
104
112
  }
105
113
  }
106
114
  }
107
- }
115
+ return responsePRs;
116
+ });
108
117
 
109
118
  const uniqueAssociatedPRs = uniqBy(flatten(associatedPRs), "number");
110
119
 
@@ -137,7 +146,7 @@ export default async function success(pluginConfig, context, { Octokit }) {
137
146
  );
138
147
 
139
148
  // Parse the release commits message and PRs body to find resolved issues/PRs via comment keyworkds
140
- const issues = [
149
+ const parsedIssues = [
141
150
  ...prs.map((pr) => pr.body),
142
151
  ...commits.map((commit) => commit.message),
143
152
  ].reduce(
@@ -157,10 +166,42 @@ export default async function success(pluginConfig, context, { Octokit }) {
157
166
  [],
158
167
  );
159
168
 
160
- debug("found issues via comments: %O", issues);
169
+ let issues = [];
170
+
171
+ if (!isEmpty(parsedIssues)) {
172
+ const uniqueParsedIssues = uniqBy(flatten(parsedIssues), "number");
173
+
174
+ // Get relatedIssues
175
+ issues = await inChunks(uniqueParsedIssues, 100, async (chunk) => {
176
+ const { repository } = await octokit.graphql(
177
+ buildRelatedIssuesQuery(chunk.map((issue) => issue.number)),
178
+ { owner, repo },
179
+ );
180
+ const responseRelatedIssues = Object.values(repository).map(
181
+ (issue) => issue,
182
+ );
183
+ return buildIssuesOrPRsFromResponseNode(responseRelatedIssues);
184
+ });
185
+
186
+ debug(
187
+ "found related issues via PRs and Commits: %O",
188
+ issues.map((issue) => issue.number),
189
+ );
190
+ }
161
191
 
162
192
  await Promise.all(
163
193
  uniqBy([...prs, ...issues], "number").map(async (issue) => {
194
+ const issueOrPR = issue.pull_request ? "PR" : "issue";
195
+
196
+ const canCommentOnIssue = successCommentCondition
197
+ ? template(successCommentCondition)({ ...context, issue })
198
+ : true;
199
+
200
+ if (!canCommentOnIssue) {
201
+ logger.log(`Skip commenting on ${issueOrPR} #%d.`, issue.number);
202
+ return;
203
+ }
204
+
164
205
  const body = successComment
165
206
  ? template(successComment)({ ...context, issue })
166
207
  : getSuccessComment(issue, releaseInfos, nextRelease);
@@ -173,7 +214,11 @@ export default async function success(pluginConfig, context, { Octokit }) {
173
214
  "POST /repos/{owner}/{repo}/issues/{issue_number}/comments",
174
215
  comment,
175
216
  );
176
- logger.log("Added comment to issue #%d: %s", issue.number, url);
217
+ logger.log(
218
+ `Added comment to ${issueOrPR} #%d: %s`,
219
+ issue.number,
220
+ url,
221
+ );
177
222
 
178
223
  if (releasedLabels) {
179
224
  const labels = releasedLabels.map((label) =>
@@ -188,23 +233,27 @@ export default async function success(pluginConfig, context, { Octokit }) {
188
233
  data: labels,
189
234
  },
190
235
  );
191
- logger.log("Added labels %O to issue #%d", labels, issue.number);
236
+ logger.log(
237
+ `Added labels %O to ${issueOrPR} #%d`,
238
+ labels,
239
+ issue.number,
240
+ );
192
241
  }
193
242
  } catch (error) {
194
243
  if (error.status === 403) {
195
244
  logger.error(
196
- "Not allowed to add a comment to the issue #%d.",
245
+ `Not allowed to add a comment to the issue/PR #%d.`,
197
246
  issue.number,
198
247
  );
199
248
  } else if (error.status === 404) {
200
249
  logger.error(
201
- "Failed to add a comment to the issue #%d as it doesn't exist.",
250
+ `Failed to add a comment to the issue/PR #%d as it doesn't exist.`,
202
251
  issue.number,
203
252
  );
204
253
  } else {
205
254
  errors.push(error);
206
255
  logger.error(
207
- "Failed to add a comment to the issue #%d.",
256
+ `Failed to add a comment to the issue/PR #%d.`,
208
257
  issue.number,
209
258
  );
210
259
  // Don't throw right away and continue to update other issues
@@ -278,6 +327,100 @@ export default async function success(pluginConfig, context, { Octokit }) {
278
327
  }
279
328
  }
280
329
 
330
+ /**
331
+ * In order to speed up a function call that handles a big array of items, we split up the
332
+ * array in chunks and call the function for each chunk in parallel. At the end we combine the
333
+ * results again.
334
+ *
335
+ * @template TItem
336
+ * @template TCallbackResult
337
+ * @param {TItem[]} items
338
+ * @param {number} chunkSize
339
+ * @param {(items: TItem[]) => TCallbackResult} callback
340
+ * @returns TCallbackResult
341
+ */
342
+ async function inChunks(items, chunkSize, callback) {
343
+ const chunkCalls = [];
344
+ for (let i = 0; i < items.length; i += chunkSize) {
345
+ chunkCalls.push(callback(items.slice(i, i + chunkSize)));
346
+ }
347
+ const results = await Promise.all(chunkCalls);
348
+
349
+ return results.flat();
350
+ }
351
+
352
+ /**
353
+ * Fields common accross PRs and Issue
354
+ */
355
+ const baseFields = `
356
+ id
357
+ title
358
+ body
359
+ url
360
+ number
361
+ createdAt
362
+ updatedAt
363
+ closedAt
364
+ comments {
365
+ totalCount
366
+ }
367
+ state
368
+ author {
369
+ login
370
+ url
371
+ avatarUrl
372
+ __typename
373
+ }
374
+ authorAssociation
375
+ activeLockReason
376
+ labels(first: 100) {
377
+ nodes {
378
+ id
379
+ url
380
+ name
381
+ color
382
+ }
383
+ }
384
+ milestone {
385
+ url
386
+ id
387
+ number
388
+ state
389
+ title
390
+ description
391
+ creator {
392
+ login
393
+ url
394
+ avatarUrl
395
+ }
396
+ createdAt
397
+ closedAt
398
+ updatedAt
399
+ }
400
+ locked
401
+ `;
402
+
403
+ /**
404
+ * Builds GraphQL query for fetching PRs/Commits related Issues to a list of commit hash (sha)
405
+ * @param {Array<number>} numbers
406
+ * @returns {string}
407
+ */
408
+ function buildRelatedIssuesQuery(numbers) {
409
+ return `#graphql
410
+ query getRelatedIssues($owner: String!, $repo: String!) {
411
+ repository(owner: $owner, name: $repo) {
412
+ ${numbers
413
+ .map((num) => {
414
+ return `issue${num}: issue(number: ${num}) {
415
+ ${baseFields}
416
+ }`;
417
+ })
418
+ .join("")}
419
+ }
420
+ }
421
+ `;
422
+ }
423
+
281
424
  /**
282
425
  * Builds GraphQL query for fetching associated PRs to a list of commit hash (sha)
283
426
  * @param {Array<string>} shas
@@ -298,9 +441,20 @@ function buildAssociatedPRsQuery(shas) {
298
441
  hasNextPage
299
442
  }
300
443
  nodes {
301
- url
302
- number
303
- body
444
+ ${baseFields}
445
+ mergeable
446
+ canBeRebased
447
+ changedFiles
448
+ mergedAt
449
+ isDraft
450
+ mergedBy {
451
+ login
452
+ avatarUrl
453
+ url
454
+ }
455
+ commits {
456
+ totalCount
457
+ }
304
458
  }
305
459
  }
306
460
  }
@@ -320,15 +474,27 @@ const loadSingleCommitAssociatedPRs = `#graphql
320
474
  repository(owner: $owner, name: $repo) {
321
475
  commit: object(oid: $sha) {
322
476
  ...on Commit {
477
+ oid
323
478
  associatedPullRequests(after: $cursor, first: 100) {
324
479
  pageInfo {
325
480
  endCursor
326
481
  hasNextPage
327
482
  }
328
483
  nodes {
329
- url
330
- number
331
- body
484
+ ${baseFields}
485
+ mergeable
486
+ canBeRebased
487
+ changedFiles
488
+ mergedAt
489
+ isDraft
490
+ mergedBy {
491
+ login
492
+ avatarUrl
493
+ url
494
+ }
495
+ commits {
496
+ totalCount
497
+ }
332
498
  }
333
499
  }
334
500
  }
@@ -336,3 +502,76 @@ const loadSingleCommitAssociatedPRs = `#graphql
336
502
  }
337
503
  }
338
504
  `;
505
+
506
+ /**
507
+ * Build associatedPRs or RelatedIssues object (into issue-like object with `pull_request` property) from the GraphQL repository response
508
+ * @param {object} responseNodes
509
+ * @param {"ISSUE" | "PR"} type
510
+ * @returns {object[]}
511
+ */
512
+ function buildIssuesOrPRsFromResponseNode(responseNodes, type = "ISSUE") {
513
+ const resultArray = [];
514
+ for (const node of responseNodes) {
515
+ let baseProps = {
516
+ number: node.number,
517
+ title: node.title,
518
+ body: node.body,
519
+ labels: node.labels?.nodes.map((label) => label.name),
520
+ html_url: node.url,
521
+ created_at: node.createdAt,
522
+ updated_at: node.updatedAt,
523
+ user: {
524
+ login: node.author?.login,
525
+ html_url: node.author?.url,
526
+ avatar_url: node.author?.avatarUrl,
527
+ type: node.author?.__typename,
528
+ },
529
+ comments: node.comments?.totalCount,
530
+ state: node.state,
531
+ milestone: node.milestone
532
+ ? {
533
+ url: node.milestone.url,
534
+ id: node.milestone.id,
535
+ number: node.milestone.number,
536
+ state: node.milestone.state,
537
+ title: node.milestone.title,
538
+ description: node.milestone.description,
539
+ creator: {
540
+ login: node.milestone.creator.login,
541
+ html_url: node.milestone.creator.url,
542
+ avatar_url: node.milestone.creator.avatarUrl,
543
+ },
544
+ created_at: node.milestone.createdAt,
545
+ closed_at: node.milestone.closedAt,
546
+ updated_at: node.milestone.updatedAt,
547
+ }
548
+ : null,
549
+ locked: node.locked,
550
+ active_lock_reason: node.activeLockReason,
551
+ closed_at: node.closedAt,
552
+ };
553
+
554
+ let result = baseProps;
555
+
556
+ if (type === "PR") {
557
+ const prProps = {
558
+ pull_request: true,
559
+ mergeable: node.mergeable,
560
+ rebaseable: node.canBeRebased,
561
+ changed_files: node.changedFiles,
562
+ commits: node.commits?.totalCount,
563
+ merged_at: node.mergedAt,
564
+ draft: node.isDraft,
565
+ merged_by: {
566
+ login: node.mergedBy?.login,
567
+ avatar_url: node.mergedBy?.avatarUrl,
568
+ html_url: node.mergedBy?.url,
569
+ },
570
+ };
571
+ result = merge(baseProps, prProps);
572
+ }
573
+
574
+ resultArray.push(result);
575
+ }
576
+ return resultArray;
577
+ }
package/lib/verify.js CHANGED
@@ -38,8 +38,10 @@ const VALIDATORS = {
38
38
  (isPlainObject(asset) && isStringOrStringArray(asset.path)),
39
39
  ),
40
40
  successComment: canBeDisabled(isNonEmptyString),
41
+ successCommentCondition: canBeDisabled(isNonEmptyString),
41
42
  failTitle: canBeDisabled(isNonEmptyString),
42
43
  failComment: canBeDisabled(isNonEmptyString),
44
+ failCommentCondition: canBeDisabled(isNonEmptyString),
43
45
  labels: canBeDisabled(isArrayOf(isNonEmptyString)),
44
46
  assignees: isArrayOf(isNonEmptyString),
45
47
  releasedLabels: canBeDisabled(isArrayOf(isNonEmptyString)),
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@semantic-release/github",
3
3
  "description": "semantic-release plugin to publish a GitHub release and comment on released Pull Requests/Issues",
4
- "version": "10.2.0",
4
+ "version": "10.3.0",
5
5
  "type": "module",
6
6
  "author": "Pierre Vanduynslager (https://twitter.com/@pvdlg_)",
7
7
  "ava": {