Skip to content

Commit 5f12fbc

Browse files
authored
Add issue triage comments workflow (#4450)
## Description Adds a workflow that posts triage comments based on issue labels. Labels are now applied by @coderabbitai, which only replies when @-mentioned and so cannot post them itself. Removes `needs-repro.yml`, `needs-more-info.yml` and `platforms.yml`, which previously did both the labeling and the commenting. They are already disabled in the repository settings. `close-when-stale.yml` is unchanged. ## Test plan Workflows only run from the default branch, so after merge: open an issue with no reproduction and confirm the comment appears, then remove the label and confirm it is deleted.
1 parent b2bbb49 commit 5f12fbc

2 files changed

Lines changed: 145 additions & 0 deletions

File tree

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// Turns the triage labels applied by CodeRabbit into comments, replacing the
2+
// responses that used to be sent by software-mansion-labs/swmansion-bot.
3+
//
4+
// One comment per label: a run only ever touches the comment belonging to the
5+
// label that triggered it.
6+
//
7+
// Invoked from .github/workflows/issue-triage-comments.yml via actions/github-script.
8+
9+
const greeting = (author) => `Hey @${author}! 👋`;
10+
11+
const RESPONSES = {
12+
'Missing repro': {
13+
marker: '<!-- rngh-triage:missing-repro -->',
14+
body: [
15+
"The issue doesn't seem to contain a [minimal reproduction](https://stackoverflow.com/help/minimal-reproducible-example).",
16+
'',
17+
'Could you provide a [snack](https://snack.expo.dev/), a [gist](https://gist.github.com/) or a link to a GitHub repository that reproduces the problem?',
18+
].join('\n'),
19+
},
20+
'Missing info': {
21+
marker: '<!-- rngh-triage:missing-info -->',
22+
body: [
23+
'Some of the required information in this issue is missing, incomplete or invalid. Could you take another look at the template and make sure each of these is filled in with something we can act on?',
24+
'',
25+
'- **Description** - what happens, and what you expected to happen instead',
26+
'- **Steps to reproduce** - steps somebody else can follow, not just numbering',
27+
'- **Gesture Handler version** - the exact version from your `package.json` or lockfile',
28+
'- **React Native version** - the exact version from your `package.json` or lockfile',
29+
'- **Versions of related libraries** you mention (Reanimated, Worklets, Screens, Expo) - the versions actually installed',
30+
'- **Platforms** - the platforms you actually see the bug on',
31+
'',
32+
'Once the issue is updated we will take another look. Thanks!',
33+
].join('\n'),
34+
},
35+
};
36+
37+
// Mirrors `check-issues-only-created-after` from the old bot.
38+
const IGNORE_ISSUES_CREATED_BEFORE = '2022-02-01';
39+
40+
// Maintainers apply this when opening an issue and are not nagged about it.
41+
const MAINTAINER_LABEL = 'Maintainer issue';
42+
43+
module.exports = async ({ github, context, core }) => {
44+
const { issue, label, action } = context.payload;
45+
const response = RESPONSES[label?.name];
46+
47+
if (!response) {
48+
core.notice(`No response configured for "${label?.name}". Skipping.`);
49+
return;
50+
}
51+
52+
if (issue.pull_request) {
53+
core.notice('Triggered on a pull request. Skipping.');
54+
return;
55+
}
56+
57+
// Retraction runs before the checks below: those decide whether to nag, not
58+
// whether to clean up. A response must still be removed when the issue was
59+
// closed or labelled `Maintainer issue` after it was posted.
60+
if (action === 'unlabeled') {
61+
const posted = await findResponse({ github, context, issue, marker: response.marker });
62+
63+
if (!posted) {
64+
core.notice('No response to retract. Skipping.');
65+
return;
66+
}
67+
68+
await github.rest.issues.deleteComment({ ...context.repo, comment_id: posted.id });
69+
core.notice(`Retracted "${label.name}" response.`);
70+
return;
71+
}
72+
73+
if (issue.state === 'closed') {
74+
core.notice('Triggered on a closed issue. Skipping.');
75+
return;
76+
}
77+
78+
if (new Date(issue.created_at) < new Date(IGNORE_ISSUES_CREATED_BEFORE)) {
79+
core.notice(`Issue predates ${IGNORE_ISSUES_CREATED_BEFORE}. Skipping.`);
80+
return;
81+
}
82+
83+
if (issue.labels.some(({ name }) => name === MAINTAINER_LABEL)) {
84+
core.notice(`Issue is labelled "${MAINTAINER_LABEL}". Skipping.`);
85+
return;
86+
}
87+
88+
const existing = await findResponse({ github, context, issue, marker: response.marker });
89+
90+
if (existing) {
91+
core.notice('Response already posted. Skipping.');
92+
return;
93+
}
94+
95+
await github.rest.issues.createComment({
96+
...context.repo,
97+
issue_number: issue.number,
98+
body: `${response.marker}\n${greeting(issue.user.login)}\n\n${response.body}`,
99+
});
100+
101+
core.notice(`Posted "${label.name}" response.`);
102+
};
103+
104+
async function findResponse({ github, context, issue, marker }) {
105+
const comments = await github.paginate(github.rest.issues.listComments, {
106+
...context.repo,
107+
issue_number: issue.number,
108+
per_page: 100,
109+
});
110+
111+
return comments.find((comment) => comment.user.type === 'Bot' && comment.body.includes(marker));
112+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: Issue triage comments
2+
3+
on:
4+
issues:
5+
types: [labeled, unlabeled]
6+
7+
permissions:
8+
contents: read
9+
issues: write
10+
11+
jobs:
12+
comment:
13+
if: >-
14+
${{ github.repository == 'software-mansion/react-native-gesture-handler'
15+
&& contains(fromJSON('["Missing repro", "Missing info"]'), github.event.label.name) }}
16+
runs-on: ubuntu-latest
17+
concurrency:
18+
group: triage-comment-${{ github.event.issue.number }}
19+
cancel-in-progress: false
20+
21+
steps:
22+
- name: Checkout triage script
23+
uses: actions/checkout@v4
24+
with:
25+
sparse-checkout: .github/scripts
26+
sparse-checkout-cone-mode: false
27+
28+
- name: Post or retract triage response
29+
uses: actions/github-script@v7
30+
with:
31+
script: |
32+
const triage = require(`${process.env.GITHUB_WORKSPACE}/.github/scripts/issue-triage-comments.js`);
33+
await triage({ github, context, core });

0 commit comments

Comments
 (0)