Skip to content

Commit e330921

Browse files
authored
Only use a single comment when the action fails (#53)
* Update README to require write permission for comments * Update existing comment if found * Delete comment when action passes * Update README to v4
1 parent 9ac1278 commit e330921

File tree

3 files changed

+146
-25
lines changed

3 files changed

+146
-25
lines changed

README.md

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@ This action allows you to fail the build if/unless a certain combination of labe
66

77
This action has three required inputs; `labels`, `mode` and `count`
88

9-
| Name | Description | Required | Default |
10-
| ------------- | ----------------------------------------------------------------------------------------------------------- | -------- | ------------------- |
11-
| `labels` | Comma separated list of labels to match | true |
12-
| `mode` | The mode of comparison to use. One of: exactly, minimum, maximum | true |
13-
| `count` | The required number of labels to match | true |
14-
| `token` | The GitHub token to use when calling the API | false | ${{ github.token }} |
15-
| `message` | The message to log and to add to the PR (if add_comment is true). See the README for available placeholders | false |
16-
| `add_comment` | Add a comment to the PR if required labels are missing | false | false |
17-
| `exit_type` | The exit type of the action. One of: failure, success | false |
9+
| Name | Description | Required | Default |
10+
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- | ------------------- |
11+
| `labels` | Comma separated list of labels to match | true |
12+
| `mode` | The mode of comparison to use. One of: exactly, minimum, maximum | true |
13+
| `count` | The required number of labels to match | true |
14+
| `token` | The GitHub token to use when calling the API | false | ${{ github.token }} |
15+
| `message` | The message to log and to add to the PR (if add_comment is true). See the README for available placeholders | false |
16+
| `add_comment` | Add a comment to the PR if required labels are missing. If a comment already exists, it will be updated. When the action passes, the comment will be deleted | false | false |
17+
| `exit_type` | The exit type of the action. One of: failure, success | false |
1818

1919
This action calls the GitHub API to fetch labels for a PR rather than reading `event.json`. This allows the action to run as intended when an earlier step adds a label. It will use `github.token` by default, and you can set the `token` input to provide alternative authentication.
2020

@@ -31,10 +31,10 @@ jobs:
3131
label:
3232
runs-on: ubuntu-latest
3333
permissions:
34-
issues: read
35-
pull-requests: read
34+
issues: write
35+
pull-requests: write
3636
steps:
37-
- uses: mheap/github-action-required-labels@v3
37+
- uses: mheap/github-action-required-labels@v4
3838
with:
3939
mode: exactly
4040
count: 1
@@ -44,7 +44,7 @@ jobs:
4444
### Prevent merging if a label exists
4545
4646
```yaml
47-
- uses: mheap/github-action-required-labels@v3
47+
- uses: mheap/github-action-required-labels@v4
4848
with:
4949
mode: exactly
5050
count: 0
@@ -58,20 +58,22 @@ You can choose to add a comment to the PR when the action fails. The default for
5858
> Label error. Requires {{ errorString }} {{ count }} of: {{ provided }}. Found: {{ applied }}
5959
6060
```yaml
61-
- uses: mheap/github-action-required-labels@v3
61+
- uses: mheap/github-action-required-labels@v4
6262
with:
6363
mode: exactly
6464
count: 1
6565
labels: "semver:patch, semver:minor, semver:major"
6666
add_comment: true
6767
```
6868
69+
If a comment already exists, it will be updated. When the action passes, the comment will be deleted.
70+
6971
### Customising the failure message / comment
7072
7173
You can also customise the message used by providing the `message` input:
7274

7375
```yaml
74-
- uses: mheap/github-action-required-labels@v3
76+
- uses: mheap/github-action-required-labels@v4
7577
with:
7678
mode: exactly
7779
count: 1
@@ -93,7 +95,7 @@ The following tokens are available for use in custom messages:
9395
### Require multiple labels
9496

9597
```yaml
96-
- uses: mheap/github-action-required-labels@v3
98+
- uses: mheap/github-action-required-labels@v4
9799
with:
98100
mode: minimum
99101
count: 2
@@ -105,7 +107,7 @@ The following tokens are available for use in custom messages:
105107
You can set `exit_type` to success then inspect `outputs.status` to see if the action passed or failed. This is useful when you want to perform additional actions if a label is not present, but not fail the entire build.
106108

107109
```yaml
108-
- uses: mheap/github-action-required-labels@v3
110+
- uses: mheap/github-action-required-labels@v4
109111
with:
110112
mode: minimum
111113
count: 2
@@ -126,13 +128,13 @@ jobs:
126128
label:
127129
runs-on: ubuntu-latest
128130
permissions:
129-
issues: read
130-
pull-requests: read
131+
issues: write
132+
pull-requests: write
131133
outputs:
132134
status: ${{ steps.check-labels.outputs.status }}
133135
steps:
134136
- id: check-labels
135-
uses: mheap/github-action-required-labels@v3
137+
uses: mheap/github-action-required-labels@v4
136138
with:
137139
mode: exactly
138140
count: 1

index.js

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const core = require("@actions/core");
22
const github = require("@actions/github");
33

4+
const matchToken = `<!-- reqlabelmessage -->`;
45
async function action() {
56
try {
67
const token = core.getInput("token", { required: true });
@@ -84,6 +85,24 @@ async function action() {
8485
return;
8586
}
8687

88+
// Remove the comment if it exists
89+
if (shouldAddComment) {
90+
const { data: existing } = await octokit.rest.issues.listComments({
91+
...github.context.repo,
92+
issue_number: github.context.issue.number,
93+
});
94+
95+
const generatedComment = existing.find((c) =>
96+
c.body.includes(matchToken)
97+
);
98+
if (generatedComment) {
99+
await octokit.rest.issues.deleteComment({
100+
...github.context.repo,
101+
comment_id: generatedComment.id,
102+
});
103+
}
104+
}
105+
87106
core.setOutput("status", "success");
88107
} catch (e) {
89108
core.setFailed(e.message);
@@ -98,11 +117,27 @@ function tmpl(t, o) {
98117

99118
async function exitWithError(exitType, octokit, shouldAddComment, message) {
100119
if (shouldAddComment) {
101-
await octokit.rest.issues.createComment({
120+
// Is there an existing comment?
121+
const { data: existing } = await octokit.rest.issues.listComments({
102122
...github.context.repo,
103123
issue_number: github.context.issue.number,
104-
body: message,
105124
});
125+
126+
const generatedComment = existing.find((c) => c.body.includes(matchToken));
127+
128+
const params = {
129+
...github.context.repo,
130+
issue_number: github.context.issue.number,
131+
body: `${matchToken}${message}`,
132+
};
133+
134+
// If so, update it
135+
let method = "createComment";
136+
if (generatedComment) {
137+
method = "updateComment";
138+
params.comment_id = generatedComment.id;
139+
}
140+
await octokit.rest.issues[method](params);
106141
}
107142

108143
core.setOutput("status", "failure");

index.test.js

Lines changed: 87 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ const mockedEnv = require("mocked-env");
66
const nock = require("nock");
77
nock.disableNetConnect();
88

9+
const matchToken = `<!-- reqlabelmessage -->`;
10+
911
describe("Required Labels", () => {
1012
let restore;
1113
let restoreTest;
@@ -90,14 +92,35 @@ describe("Required Labels", () => {
9092

9193
mockLabels(["bug"]);
9294

95+
mockListComments([]);
96+
9397
nock("https://api.github.com")
9498
.post("/repos/mheap/missing-repo/issues/28/comments", {
95-
body: "Label error. Requires exactly 1 of: enhancement. Found: bug",
99+
body: `${matchToken}Label error. Requires exactly 1 of: enhancement. Found: bug`,
96100
})
97101
.reply(201);
98102

99103
await action();
100104
});
105+
106+
it("deletes a comment when passing", async () => {
107+
restoreTest = mockPr({
108+
INPUT_LABELS: "bug",
109+
INPUT_MODE: "exactly",
110+
INPUT_COUNT: "1",
111+
INPUT_ADD_COMMENT: "true",
112+
GITHUB_TOKEN: "mock-token-here-abc",
113+
});
114+
115+
mockLabels(["bug"]);
116+
mockListComments([{ id: "12345", body: `${matchToken}This` }]);
117+
118+
nock("https://api.github.com")
119+
.delete("/repos/mheap/missing-repo/issues/comments/12345")
120+
.reply(200);
121+
122+
await action();
123+
});
101124
});
102125

103126
describe("success", () => {
@@ -369,9 +392,58 @@ describe("Required Labels", () => {
369392

370393
mockLabels(["enhancement", "bug"]);
371394

395+
mockListComments([]);
396+
397+
nock("https://api.github.com")
398+
.post("/repos/mheap/missing-repo/issues/28/comments", {
399+
body: `${matchToken}This is a static comment`,
400+
})
401+
.reply(201);
402+
403+
await action();
404+
});
405+
406+
it("updates an existing comment when one is found", async () => {
407+
restoreTest = mockPr({
408+
GITHUB_TOKEN: "abc123",
409+
INPUT_LABELS: "enhancement,bug",
410+
INPUT_MODE: "exactly",
411+
INPUT_COUNT: "1",
412+
INPUT_ADD_COMMENT: "true",
413+
INPUT_MESSAGE: "This is a static comment",
414+
});
415+
416+
mockLabels(["enhancement", "bug"]);
417+
418+
mockListComments([{ id: "12345", body: `${matchToken}This` }]);
419+
420+
nock("https://api.github.com")
421+
.patch("/repos/mheap/missing-repo/issues/comments/12345", {
422+
issue_number: 28,
423+
body: `${matchToken}This is a static comment`,
424+
})
425+
.reply(200);
426+
427+
await action();
428+
});
429+
430+
it("creates when comments exist but don't match", async () => {
431+
restoreTest = mockPr({
432+
GITHUB_TOKEN: "abc123",
433+
INPUT_LABELS: "enhancement,bug",
434+
INPUT_MODE: "exactly",
435+
INPUT_COUNT: "1",
436+
INPUT_ADD_COMMENT: "true",
437+
INPUT_MESSAGE: "This is a static comment",
438+
});
439+
440+
mockLabels(["enhancement", "bug"]);
441+
442+
mockListComments([{ id: "12345", body: `No Match` }]);
443+
372444
nock("https://api.github.com")
373445
.post("/repos/mheap/missing-repo/issues/28/comments", {
374-
body: "This is a static comment",
446+
body: `${matchToken}This is a static comment`,
375447
})
376448
.reply(201);
377449

@@ -392,9 +464,10 @@ describe("Required Labels", () => {
392464

393465
mockLabels(["enhancement", "bug"]);
394466

467+
mockListComments([]);
395468
nock("https://api.github.com")
396469
.post("/repos/mheap/missing-repo/issues/28/comments", {
397-
body: "Mode: exactly, Count: 1, Error String: exactly, Provided: enhancement, bug, Applied: enhancement, bug",
470+
body: `${matchToken}Mode: exactly, Count: 1, Error String: exactly, Provided: enhancement, bug, Applied: enhancement, bug`,
398471
})
399472
.reply(201);
400473

@@ -428,6 +501,17 @@ function mockLabels(labels) {
428501
);
429502
}
430503

504+
function mockListComments(comments) {
505+
nock("https://api.github.com")
506+
.get("/repos/mheap/missing-repo/issues/28/comments")
507+
.reply(
508+
200,
509+
comments.map((c) => {
510+
return { body: c.body, id: c.id };
511+
})
512+
);
513+
}
514+
431515
function mockEvent(eventName, mockPayload, additionalParams = {}) {
432516
github.context.payload = mockPayload;
433517

0 commit comments

Comments
 (0)