Skip to content

Commit b8e3152

Browse files
authored
Added post comment to PR (#466)
## Feature request * Comment on PR when merged without auto-apply > [!IMPORTANT] > **No Changes Were Applied** > > This Pull Request was merged without using the `auto-apply` label. > Please [check if any issues](....) were created and apply them by adding the `apply` label to the corresponding issue. ## what * Added comment to PR ## why * Improve informative feedback for developers ## references * https://cloudposse.atlassian.net/browse/DEV-1580 * https://cloudposse.atlassian.net/browse/DEV-1124
1 parent cacbb69 commit b8e3152

File tree

18 files changed

+357
-111
lines changed

18 files changed

+357
-111
lines changed

.github/workflows/pr-comment-test.yml

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
name: "Test - PR comments"
2+
3+
on:
4+
pull_request: {}
5+
6+
env:
7+
AWS_REGION: us-east-2
8+
9+
# Permissions required for assuming AWS identity
10+
permissions:
11+
id-token: write
12+
contents: read
13+
issues: write
14+
15+
jobs:
16+
setup:
17+
runs-on: ubuntu-latest
18+
steps:
19+
- name: Setup
20+
run: echo "Do setup"
21+
22+
- uses: actions/checkout@v4
23+
with:
24+
ref: ${{ github.event.pull_request.head.ref }}
25+
26+
- name: Upload Artifacts
27+
uses: actions/upload-artifact@v3
28+
with:
29+
name: metadata
30+
path: ./tests/fixtures/metadata
31+
retention-days: 1
32+
33+
test:
34+
runs-on: ubuntu-latest
35+
continue-on-error: true
36+
needs: [setup]
37+
steps:
38+
- uses: actions/checkout@v4
39+
with:
40+
ref: ${{ github.event.pull_request.head.ref }}
41+
42+
- name: Plan Atmos Component
43+
id: current
44+
uses: ./
45+
with:
46+
max-opened-issues: '4'
47+
labels: "test-pr-comments"
48+
token: ${{ secrets.PUBLIC_REPO_ACCESS_TOKEN }}
49+
50+
outputs:
51+
result: ${{ steps.current.outcome }}
52+
53+
assert:
54+
runs-on: ubuntu-latest
55+
needs: [test]
56+
steps:
57+
- uses: nick-fields/assert-action@v1
58+
with:
59+
expected: 'success'
60+
actual: "${{ needs.test.outputs.result }}"
61+
62+
- name: Find all issues
63+
id: issues
64+
uses: lee-dohm/select-matching-issues@v1
65+
with:
66+
query: 'label:test-pr-comments is:open'
67+
token: ${{ github.token }}
68+
69+
- name: Close found issues
70+
id: test
71+
run: |
72+
echo "count=$(cat ${{ steps.issues.outputs.path }} | xargs -I {} -d '\n' echo "{}" | wc -l )" >> $GITHUB_OUTPUT
73+
74+
- uses: nick-fields/assert-action@v1
75+
with:
76+
expected: '4'
77+
actual: "${{ steps.test.outputs.count }}"
78+
79+
teardown:
80+
runs-on: ubuntu-latest
81+
needs: [assert]
82+
if: ${{ always() }}
83+
steps:
84+
- name: Tear down
85+
run: echo "Do Tear down"
86+
87+
- name: Find all issues
88+
id: issues
89+
uses: lee-dohm/select-matching-issues@v1
90+
with:
91+
query: 'label:test-pr-comments is:open'
92+
token: ${{ github.token }}
93+
94+
- name: Close found issues
95+
run: cat ${{ steps.issues.outputs.path }} | xargs -I {} -d '\n' gh issue close {}
96+
env:
97+
GH_TOKEN: ${{ github.token }}

assets/comment-no-changes.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
> [!IMPORTANT]
2+
> **No Changes Were Applied**
3+
>
4+
> This Pull Request was merged without using the `auto-apply` label; in any case, the pull request did not contain any changes to apply.

assets/comment.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
> [!IMPORTANT]
2+
> **No Changes Were Applied**
3+
>
4+
> This Pull Request was merged without using the `auto-apply` label; therefore, no changes were applied upon merging.
5+
6+
Review the associated issues and, if applicable, tag each with the `apply` label to apply the changes.

assets/summary-no-changes.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
> [!IMPORTANT]
2+
> **No Changes Were Applied**
3+
>
4+
> This Pull Request was merged without using the \`auto-apply\` label; in any case, the pull request did not contain any changes to apply.

src/action.js

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const fs = require('fs');
2+
const path = require('path');
23
const core = require('@actions/core');
34
const artifact = require('@actions/artifact');
45
const {StackFromIssue, getMetadataFromIssueBody} = require("./models/stacks_from_issues");
@@ -9,6 +10,7 @@ const {Remove} = require("./operations/remove");
910
const {Create} = require("./operations/create");
1011
const {Nothing} = require("./operations/nothing");
1112
const {StackFromArchive} = require("./models/stacks_from_archive");
13+
const {readFileSync} = require("fs");
1214

1315

1416
const downloadArtifacts = (artifactName) => {
@@ -75,7 +77,7 @@ const mapArtifactToComponents = (path) => {
7577
return new Map(result)
7678
}
7779

78-
const getOperationsList = async (stacksFromIssues, stacksFromArtifact, users, labels, maxOpenedIssues, processAll) => {
80+
const getOperationsList = (stacksFromIssues, stacksFromArtifact, users, labels, maxOpenedIssues, processAll) => {
7981

8082
const stacks = processAll ?
8183
[...stacksFromIssues.keys(), ...stacksFromArtifact.keys()] :
@@ -159,21 +161,19 @@ const driftDetectionTable = (results) => {
159161
const table = [
160162
`| Component | State | Comments |`,
161163
`|-----------|-------|----------|`
162-
];
164+
]
163165

164-
results.map((result) => {
166+
const tableContent = results.map((result) => {
165167
return result.render()
166168
}).filter((result) => {
167169
return result !== ""
168-
}).forEach((result) => {
169-
table.push(result)
170170
})
171171

172-
if (table.length > 2) {
173-
return ['# Drift Detection Summary', table.join("\n")]
172+
if (tableContent.length > 2) {
173+
return ['# Drift Detection Summary', table.concat(tableContent).join("\n")]
174174
}
175175

176-
return ["No drift detected"]
176+
return []
177177
}
178178

179179
const postSummaries = async (table, components) => {
@@ -215,6 +215,39 @@ const postSummaries = async (table, components) => {
215215
await summary.write();
216216
}
217217

218+
const postComment = async (octokit, context, table) => {
219+
const commentId = "github-action-atmos-terraform-drift-detection-comment"
220+
// Suffix comment with hidden value to check for updating later.
221+
const commentIdSuffix = `\n\n\n<hidden purpose="github-action-atmos-terraform-drift-detection-comment" value="${commentId}"></hidden>`;
222+
223+
const existingCommentId = await octokit.rest.issues.listComments({
224+
...context.repo,
225+
issue_number: context.payload.pull_request.number,
226+
}).then( result => {
227+
return result.data.filter(item => {
228+
return item.body !== ""
229+
}).map(item => { return item.id }).pop()
230+
})
231+
232+
const commentBody = table.join("\n") + commentIdSuffix;
233+
// If comment already exists, get the comment ID.
234+
if (existingCommentId) {
235+
console.log("Update comment")
236+
await octokit.rest.issues.updateComment({
237+
...context.repo,
238+
comment_id: existingCommentId,
239+
body: commentBody
240+
})
241+
} else {
242+
console.log("Create comment")
243+
await octokit.rest.issues.createComment({
244+
...context.repo,
245+
issue_number: context.payload.pull_request.number,
246+
body: commentBody
247+
});
248+
}
249+
}
250+
218251
/**
219252
* @param {Object} octokit
220253
* @param {Object} context
@@ -241,14 +274,21 @@ const runAction = async (octokit, context, parameters) => {
241274
let users = assigneeUsers.concat(usersFromTeams);
242275
users = [...new Set(users)]; // get unique set
243276

244-
const operations = await getOperationsList(stacksFromIssues, stacksFromArtifact, users, labels, maxOpenedIssues, processAll);
277+
const operations = getOperationsList(stacksFromIssues, stacksFromArtifact, users, labels, maxOpenedIssues, processAll)
278+
.filter(item => item.isVisible())
279+
280+
const assets_path = path.resolve(__dirname + '/../assets')
281+
282+
if (context.payload.pull_request != null) {
283+
const fileName = operations.length > 0 ? `${assets_path}/comment.md` : `${assets_path}/comments-no-changes.md`
284+
const title = [readFileSync(fileName, 'utf-8')]
285+
await postComment(octokit, context, title)
286+
}
245287

246-
const results = await Promise.all(operations.map((item) => {
247-
return item.run(octokit, context)
248-
}))
288+
const results = await Promise.all(operations.map(item => { return item.run(octokit, context) }))
249289

250-
const table = driftDetectionTable(results);
251-
await postSummaries(table, operations);
290+
const summaryTable = operations.length > 0 ? driftDetectionTable(results) : [readFileSync(`${assets_path}/summary-no-changes.md`, 'utf-8')]
291+
await postSummaries(summaryTable, results);
252292
}
253293

254294
module.exports = {

src/operations/close.js

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,11 @@ class Close {
3636

3737
core.info(`Issue ${issueNumber} for component ${slug} has been closed with comment: ${comment}`);
3838

39-
return new Recovered(context.runId, repository, issueNumber, this.state)
39+
return new Recovered(context, repository, issueNumber, this.state)
4040
}
4141

42-
summary() {
43-
return "";
44-
}
45-
46-
shortSummary() {
47-
return "";
42+
isVisible() {
43+
return true
4844
}
4945

5046
}

src/operations/create.js

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,31 @@ class Create {
1313
async run(octokit, context) {
1414
const repository = context.repo;
1515

16+
const orgName = repository.owner;
17+
const repo = repository.repo;
18+
const runId = context.runId;
19+
1620
const slug = this.state.slug;
1721
const issueTitle = this.state.error ? `Failure Detected in \`${slug}\`` : `Drift Detected in \`${slug}\``;
1822
const file_name = getFileName(slug);
1923
const issueDescription = readFileSync(`issue-description-${file_name}.md`, 'utf8');
2024

25+
const body = [
26+
issueDescription,
27+
"# Related",
28+
`* [:building_construction: Action Workflow Logs](/${orgName}/${repo}/actions/runs/${runId})`
29+
]
30+
31+
if (context.payload.pull_request != null) {
32+
body.push(`* #${context.payload.pull_request.number}`);
33+
}
34+
2135
const label = this.state.error ? "error" : "drift"
2236

2337
const newIssue = await octokit.rest.issues.create({
2438
...repository,
2539
title: issueTitle,
26-
body: issueDescription,
40+
body: body.join("\n"),
2741
labels: [label].concat(this.labels)
2842
});
2943

@@ -43,23 +57,11 @@ class Create {
4357
}
4458
}
4559

46-
return new NewCreated(context.runId, repository, issueNumber, this.state);
47-
}
48-
49-
summary() {
50-
const file_name = getFileName(this.state.slug);
51-
const file = `step-summary-${file_name}.md`;
52-
return readFileSync(file, 'utf-8');
60+
return new NewCreated(context, repository, issueNumber, this.state);
5361
}
5462

55-
shortSummary() {
56-
const component = this.state.metadata.component;
57-
const stack = this.state.metadata.stack;
58-
const title = this.state.error ?
59-
`## Plan Failed for \`${component}\` in \`${stack}\`` :
60-
`## Changes Found for \`${component}\` in \`${stack}\``;
61-
const body = `Summary is unavailable due to [GitHub size limitation](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#step-isolation-and-limits) on job summaries. Please check the GitHub Action run logs for more details.`
62-
return [title, body].join("\n");
63+
isVisible() {
64+
return true
6365
}
6466
}
6567

src/operations/nothing.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,8 @@ class Nothing {
88
return new None();
99
}
1010

11-
summary() {
12-
return "";
13-
}
14-
15-
shortSummary() {
16-
return "";
11+
isVisible() {
12+
return false
1713
}
1814
}
1915

src/operations/remove.js

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
const core = require("@actions/core");
2-
const github = require("@actions/github");
32
const {Removed} = require("../results/removed");
43

54
class Remove {
@@ -34,14 +33,11 @@ class Remove {
3433

3534
core.info(`Issue ${issueNumber} for component ${slug} has been closed with comment: ${comment}`);
3635

37-
return new Removed(github.context.runId, repository, issueNumber, this.issue);
36+
return new Removed(context, repository, issueNumber, this.issue);
3837
}
3938

40-
summary() {
41-
return "";
42-
}
43-
shortSummary() {
44-
return "";
39+
isVisible() {
40+
return true
4541
}
4642
}
4743

src/operations/skip.js

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
const {NewSkipped} = require("../results/new-skipped");
2-
const {readFileSync} = require("fs");
3-
const {getFileName} = require("../utils");
42

53
class Skip {
64
constructor(issue, state, maxNumberOpenedIssues) {
@@ -11,23 +9,11 @@ class Skip {
119

1210
async run(octokit, context) {
1311
const repository = context.repo;
14-
return new NewSkipped(context.runId, repository, this.maxNumberOpenedIssues, this.state);
12+
return new NewSkipped(context, repository, this.maxNumberOpenedIssues, this.state);
1513
}
1614

17-
summary() {
18-
const file_name = getFileName(this.state.slug);
19-
const file = `step-summary-${file_name}.md`;
20-
return readFileSync(file, 'utf-8');
21-
}
22-
23-
shortSummary() {
24-
const component = this.state.metadata.component;
25-
const stack = this.state.metadata.stack;
26-
const title = this.state.error ?
27-
`## Plan Failed for \`${component}\` in \`${stack}\`` :
28-
`## Changes Found for \`${component}\` in \`${stack}\``;
29-
const body = `Summary is unavailable due to [GitHub size limitation](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#step-isolation-and-limits) on job summaries. Please check the GitHub Action run logs for more details.`
30-
return [title, body].join("\n");
15+
isVisible() {
16+
return true
3117
}
3218
}
3319

0 commit comments

Comments
 (0)