@@ -3,13 +3,14 @@ name: Check Linked Issue
3
3
on :
4
4
pull_request :
5
5
types : [opened, edited, synchronize, reopened]
6
+ workflow_dispatch :
6
7
7
8
jobs :
8
9
check-linked-issue :
9
10
runs-on : ubuntu-latest
10
11
11
12
steps :
12
- - name : Check for linked issue
13
+ - name : Ensure PR is linked to an issue
13
14
uses : actions/github-script@v7
14
15
with :
15
16
script : |
@@ -22,22 +23,20 @@ jobs:
22
23
closingIssuesReferences(first: 10) {
23
24
nodes {
24
25
number
26
+ state
25
27
}
26
28
}
27
29
}
28
30
}
29
31
}
30
32
`;
31
33
32
- const variables = {
34
+ const { repository: { pullRequest: pr } } = await github.graphql(query, {
33
35
owner: context.repo.owner,
34
36
repo: context.repo.repo,
35
- number: context.issue.number,
36
- };
37
+ number: context.issue.number
38
+ }) ;
37
39
38
- const result = await github.graphql(query, variables);
39
-
40
- const pr = result.repository.pullRequest;
41
40
const textToCheck = `${pr.title} ${pr.body || ''}`;
42
41
43
42
const issuePatterns = [
@@ -46,26 +45,98 @@ jobs:
46
45
/(?:issue|issues)\s+#?(\d+)/gi,
47
46
];
48
47
49
- let linkedIssues = new Set();
48
+ const linkedIssues = new Set();
50
49
50
+ // Find linked issues from patterns
51
51
for (const pattern of issuePatterns) {
52
52
for (const match of textToCheck.matchAll(pattern)) {
53
53
if (match[1]) linkedIssues.add(match[1]);
54
54
}
55
55
}
56
56
57
- if (pr.closingIssuesReferences.nodes.length > 0) {
58
- pr.closingIssuesReferences.nodes.forEach(issue => linkedIssues.add(issue.number.toString()));
59
- }
57
+ // Add issues from GitHub's closing references
58
+ pr.closingIssuesReferences.nodes.forEach(node => {
59
+ linkedIssues.add(node.number.toString());
60
+ });
60
61
62
+ // Check if no issues are linked
61
63
if (linkedIssues.size === 0) {
62
64
await github.rest.issues.createComment({
63
65
owner: context.repo.owner,
64
66
repo: context.repo.repo,
65
67
issue_number: context.issue.number,
66
- body: `❌ This PR needs to be linked to an issue.\nAdd \`Fixes #123\` or link via the UI.`
68
+ body: `
69
+ # # 🚫 Missing Linked Issue
70
+
71
+ Hi 👋 This pull request does not appear to be linked to any open issue yet.
72
+
73
+ Linking your PR to an issue helps keep the project tidy and ensures the issue is closed automatically.
74
+
75
+ # ## ✔️ How to fix this
76
+
77
+ - Add a keyword like \`Fixes # 123\` or \`Closes #456\` to your PR **description** or a **commit message**.
78
+ - Or link it manually using the **"Linked issues"** panel in the PR sidebar.
79
+
80
+ > ✅ **Tip:** You can link multiple issues.
81
+ > 🚫 **Note:** If only one issue is linked, it must be open for this check to pass.
82
+
83
+ Once linked, this check will pass automatically on your next push or when you re-run the workflow.
84
+
85
+ Thanks for helping maintainers! 🙌
86
+ `
67
87
});
68
- core.setFailed('No linked issue found.');
69
- } else {
70
- console.log(`✅ Linked to issues: ${Array.from(linkedIssues).join(', ')}`);
88
+ core.setFailed('❌ No linked issue found.');
89
+ return;
90
+ }
91
+
92
+ let openIssues = 0;
93
+
94
+ // Check status of each linked issue
95
+ for (const issueNumber of linkedIssues) {
96
+ const issueQuery = `
97
+ query($owner : String!, $repo: String!, $number: Int!) {
98
+ repository(owner : $owner, name: $repo) {
99
+ issue(number : $number) {
100
+ state
101
+ }
102
+ }
103
+ }
104
+ ` ;
105
+
106
+ const { repository: { issue } } = await github.graphql(issueQuery, {
107
+ owner: context.repo.owner,
108
+ repo: context.repo.repo,
109
+ number: parseInt(issueNumber, 10)
110
+ });
111
+
112
+ if (issue.state === 'OPEN') {
113
+ openIssues++;
114
+ }
71
115
}
116
+
117
+ // If only one issue is linked and it's closed, fail the check
118
+ if (linkedIssues.size === 1 && openIssues === 0) {
119
+ await github.rest.issues.createComment({
120
+ owner: context.repo.owner,
121
+ repo: context.repo.repo,
122
+ issue_number: context.issue.number,
123
+ body: `
124
+ # # 🚫 Linked Issue is Closed
125
+
126
+ Hi 👋 This pull request links only **one issue**, but that issue is **closed**.
127
+
128
+ To pass this check, the linked issue must be **open** — or link an additional open issue.
129
+
130
+ # ## ✔️ How to fix this
131
+
132
+ - Reopen the linked issue if appropriate.
133
+ - Or link another relevant **open** issue via the PR description or the **"Linked issues"** panel.
134
+
135
+ Thanks for keeping the project healthy! 🚀
136
+ `
137
+ });
138
+ core.setFailed('❌ Linked issue is closed.');
139
+ } else {
140
+ console.log(` ✅ PR is linked to issue(s): ${Array.from(linkedIssues).join(', ')}`);
141
+ console.log(`✅ Number of open issues : ${openIssues}`);
142
+ }
0 commit comments