Просмотр исходного кода

feat(ci): fail PR Body Check when a Fixes issue is not linked in Development (#5139)

Closing keywords never create a Development link on pull requests into develop, so the link has to be added by hand and was often missed. The check now compares the issues named after Fixes, Closes or Resolves with the pull request's closingIssuesReferences and fails on any issue that is not linked. Related to and no-issue pull requests still only warn.
Daniel Gibbs 5 дней назад
Родитель
Сommit
a3e0960eca
1 измененных файлов с 39 добавлено и 0 удалено
  1. 39 0
      .github/workflows/action-pr-body-check.yml

+ 39 - 0
.github/workflows/action-pr-body-check.yml

@@ -11,6 +11,8 @@ on:
 
 permissions:
   contents: read
+  issues: read
+  pull-requests: read
 
 jobs:
   pr-body-check:
@@ -39,6 +41,43 @@ jobs:
               core.warning('No closing keyword found. Add "Fixes #123" to link the issue, or "Related to #123" if this pull request does not fully resolve it.');
             }
 
+            const repoName = `${context.repo.owner}/${context.repo.repo}`.toLowerCase();
+            const closingRefs = [...body.matchAll(/\b(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?):?\s+([\w.-]+\/[\w.-]+)?#(\d+)/gi)]
+              .filter((m) => !m[1] || m[1].toLowerCase() === repoName)
+              .map((m) => Number(m[2]));
+            const wanted = [...new Set(closingRefs)];
+            if (wanted.length > 0) {
+              const vars = { owner: context.repo.owner, repo: context.repo.repo };
+              const { repository } = await github.graphql(
+                `query($owner: String!, $repo: String!, $number: Int!) {
+                  repository(owner: $owner, name: $repo) {
+                    pullRequest(number: $number) { closingIssuesReferences(first: 50) { nodes { number } } }
+                  }
+                }`,
+                { ...vars, number: context.payload.pull_request.number }
+              );
+              const linked = new Set(repository.pullRequest.closingIssuesReferences.nodes.map((n) => n.number));
+              const unlinked = [];
+              for (const number of wanted.filter((n) => !linked.has(n))) {
+                try {
+                  const { repository: ref } = await github.graphql(
+                    `query($owner: String!, $repo: String!, $number: Int!) {
+                      repository(owner: $owner, name: $repo) { issueOrPullRequest(number: $number) { __typename } }
+                    }`,
+                    { ...vars, number }
+                  );
+                  if (ref.issueOrPullRequest?.__typename === 'Issue') {
+                    unlinked.push(`#${number}`);
+                  }
+                } catch (error) {
+                  core.warning(`Could not look up #${number}: ${error.message}`);
+                }
+              }
+              if (unlinked.length > 0) {
+                errors.push(`Not linked in Development: ${unlinked.join(', ')}. Closing keywords in pull requests to develop do not link issues. Select each issue in the Development section of this pull request's sidebar, then re-run this check.`);
+              }
+            }
+
             if (errors.length > 0) {
               core.setFailed(errors.join('\n'));
             }