-
Notifications
You must be signed in to change notification settings - Fork 6
/
index.js
124 lines (101 loc) · 3.81 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// @ts-check
import { debug, getInput } from '@actions/core'
import { getExecOutput } from '@actions/exec'
import { Octokit } from '@octokit/action'
import { readFileSync } from 'node:fs'
import { env } from 'node:process'
import parseGitDiff from 'parse-git-diff'
const octokit = new Octokit({
userAgent: 'suggest-changes',
})
const [owner, repo] = String(env.GITHUB_REPOSITORY).split('/')
/** @type {import("@octokit/webhooks-types").PullRequestEvent} */
const eventPayload = JSON.parse(
readFileSync(String(env.GITHUB_EVENT_PATH), 'utf8')
)
const pull_number = Number(eventPayload.pull_request.number)
const pullRequestFiles = (
await octokit.pulls.listFiles({ owner, repo, pull_number })
).data.map((file) => file.filename)
// Get the diff between the head branch and the base branch (limit to the files in the pull request)
const diff = await getExecOutput(
'git',
['diff', '--unified=1', '--', ...pullRequestFiles],
{ silent: true }
)
debug(`Diff output: ${diff.stdout}`)
// Create an array of changes from the diff output based on patches
const parsedDiff = parseGitDiff(diff.stdout)
// Get changed files from parsedDiff (changed files have type 'ChangedFile')
const changedFiles = parsedDiff.files.filter(
(file) => file.type === 'ChangedFile'
)
const generateSuggestionBody = (changes) => {
const suggestionBody = changes
.filter(({ type }) => type === 'AddedLine' || type === 'UnchangedLine')
.map(({ content }) => content)
.join('\n')
// Quadruple backticks allow for triple backticks in a fenced code block in the suggestion body
// https://docs.github.com/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#fenced-code-blocks
return `\`\`\`\`suggestion\n${suggestionBody}\n\`\`\`\``
}
function createSingleLineComment(path, fromFileRange, changes) {
return {
path,
line: fromFileRange.start,
body: generateSuggestionBody(changes),
}
}
function createMultiLineComment(path, fromFileRange, changes) {
return {
path,
start_line: fromFileRange.start,
// The last line of the chunk is the start line plus the number of lines in the chunk
// minus 1 to account for the start line being included in fromFileRange.lines
line: fromFileRange.start + fromFileRange.lines - 1,
start_side: 'RIGHT',
side: 'RIGHT',
body: generateSuggestionBody(changes),
}
}
// Fetch existing review comments
const existingComments = (
await octokit.pulls.listReviewComments({ owner, repo, pull_number })
).data
// Function to generate a unique key for a comment
const generateCommentKey = (comment) =>
`${comment.path}:${comment.line ?? ''}:${comment.start_line ?? ''}:${
comment.body
}`
// Create a Set of existing comment keys for faster lookup
const existingCommentKeys = new Set(existingComments.map(generateCommentKey))
// Create an array of comments with suggested changes for each chunk of each changed file
const comments = changedFiles.flatMap(({ path, chunks }) =>
chunks.flatMap(({ fromFileRange, changes }) => {
debug(`Starting line: ${fromFileRange.start}`)
debug(`Number of lines: ${fromFileRange.lines}`)
debug(`Changes: ${JSON.stringify(changes)}`)
const comment =
fromFileRange.lines <= 1
? createSingleLineComment(path, fromFileRange, changes)
: createMultiLineComment(path, fromFileRange, changes)
// Generate key for the new comment
const commentKey = generateCommentKey(comment)
// Check if the new comment already exists
if (existingCommentKeys.has(commentKey)) {
return []
}
return [comment]
})
)
// Create a review with the suggested changes if there are any
if (comments.length > 0) {
await octokit.pulls.createReview({
owner,
repo,
pull_number,
event: getInput('event').toUpperCase(),
body: getInput('comment'),
comments,
})
}