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
|
// @ts-check
const { promisify } = require('node:util')
const execFile = promisify(require('node:child_process').execFile)
/**
* @param {{
* args: string[]
* core: import('@actions/core'),
* quiet?: boolean,
* repoPath?: string,
* }} RunGitProps
*/
async function runGit({ args, repoPath, core, quiet }) {
if (repoPath) {
args = ['-C', repoPath, ...args]
}
if (!quiet) {
core.info(`About to run \`git ${args.map((s) => `'${s}'`).join(' ')}\``)
}
return await execFile('git', args)
}
/**
* Gets the SHA, subject and changed files for each commit in the given PR.
*
* Don't use GitHub API at all: the "list commits on PR" endpoint has a limit
* of 250 commits and doesn't return the changed files.
*
* @param {{
* core: import('@actions/core'),
* pr: Awaited<ReturnType<InstanceType<import('@actions/github/lib/utils').GitHub>["rest"]["pulls"]["get"]>>["data"]
* repoPath?: string,
* }} GetCommitMessagesForPRProps
*
* @returns {Promise<{
* subject: string,
* sha: string,
* changedPaths: string[],
* changedPathSegments: Set<string>,
* }[]>}
*/
async function getCommitDetailsForPR({ core, pr, repoPath }) {
await runGit({
args: ['fetch', `--depth=1`, 'origin', pr.base.sha],
repoPath,
core,
})
await runGit({
args: ['fetch', `--depth=${pr.commits + 1}`, 'origin', pr.head.sha],
repoPath,
core,
})
const shas = (
await runGit({
args: [
'rev-list',
`--max-count=${pr.commits}`,
`${pr.base.sha}..${pr.head.sha}`,
],
repoPath,
core,
})
).stdout
.split('\n')
.map((s) => s.trim())
.filter(Boolean)
return Promise.all(
shas.map(async (sha) => {
// Subject first, then a blank line, then filenames.
const result = (
await runGit({
args: ['log', '--format=%s', '--name-only', '-1', sha],
repoPath,
core,
quiet: true,
})
).stdout.split('\n')
const subject = result[0]
const changedPaths = result.slice(2, -1)
const changedPathSegments = new Set(
changedPaths.flatMap((path) => path.split('/')),
)
return {
sha,
subject,
changedPaths,
changedPathSegments,
}
}),
)
}
module.exports = { getCommitDetailsForPR }
|