-
Notifications
You must be signed in to change notification settings - Fork 26
140 lines (116 loc) · 5.03 KB
/
Copy pathadd-trailer.yml
File metadata and controls
140 lines (116 loc) · 5.03 KB
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) 2025 Robin Jarry
---
name: Add Trailer
on:
issue_comment:
types: [created]
workflow_run:
workflows: ["Review"]
types: [completed]
permissions:
contents: read
pull-requests: read
actions: read
jobs:
add-trailer:
if: >-
${{ (github.event_name == 'issue_comment' && github.event.issue.pull_request
&& (contains(github.event.comment.body, 'Acked-by:') || contains(github.event.comment.body, 'Tested-by:')))
|| (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success') }}
runs-on: ubuntu-latest
steps:
- name: Get PR info and trailer
id: info
uses: actions/github-script@v7
with:
github-token: ${{ secrets.TRAILER_BOT_TOKEN }}
script: |
let pr, trailer;
if (context.eventName === 'issue_comment') {
const body = context.payload.comment.body;
const regexp = /(acked|tested)-by:\s+(\w[^<]+\w)(?:\s+<([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})>)?/i;
const match = body.match(regexp);
if (!match) {
throw new Error(`No valid trailer found in comment: ${body}`)
}
const { data: prData } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.issue.number
});
pr = prData;
trailer = `${match[1]}-by: ${match[2]} <${match[3] || 'UNKNOWN_EMAIL'}>`;
} else if (context.eventName === 'workflow_run') {
const headBranch = context.payload.workflow_run.head_branch;
const { data: allPRs } = await github.rest.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
});
const matchingPRs = allPRs.filter(p => p.head.ref === headBranch);
if (matchingPRs.length === 0) {
throw new Error(`No open PR found for ${headBranch}`)
}
const { data: prData } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: matchingPRs[0].number
});
pr = prData;
const actor = context.payload.workflow_run.triggering_actor.login;
const { data: user } = await github.rest.users.getByUsername({
username: actor
});
if (!user.name) {
throw new Error(`User ${actor} does not expose their full name`);
}
trailer = `Reviewed-by: ${user.name} <${user.email || 'UNKNOWN_EMAIL'}>`;
}
return {
commits: pr.commits,
head_repo: pr.head.repo.full_name,
head_ref: pr.head.ref,
trailer: trailer,
};
- uses: actions/checkout@v4
with:
token: ${{ secrets.TRAILER_BOT_TOKEN }}
repository: ${{ fromJSON(steps.info.outputs.result).head_repo }}
ref: ${{ fromJSON(steps.info.outputs.result).head_ref }}
fetch-depth: 0
persist-credentials: true
- name: Amend all pull request commits with the trailer
env:
GIT_TRAILER_DEBUG: 1
COMMITS: ${{ fromJSON(steps.info.outputs.result).commits }}
HEAD_REF: ${{ fromJSON(steps.info.outputs.result).head_ref }}
TRAILER: ${{ fromJSON(steps.info.outputs.result).trailer }}
run: |
set -xe
if echo "$TRAILER" | grep -qw 'UNKNOWN_EMAIL'; then
NAME=$(echo "$TRAILER" | sed -En 's/.+-by:[[:blank:]]+([^<]+)[[:blank:]]<.+/\1/p')
EMAIL=$(git log --pretty=%aE --author="$NAME" | head -n1)
if [ -z "$EMAIL" ]; then
EMAIL=$(git shortlog -se -w0 --group=author --group=committer \
--group=trailer:acked-by --group=trailer:reviewed-by \
--group=trailer:reported-by --group=trailer:signed-off-by \
--group=trailer:tested-by HEAD | \
sed -En "s/^[[:space:]]+[0-9]+[[:space:]]+$NAME <([^@]+@[^>]+)>$/\\1/p" | head -n1)
fi
if [ -z "$EMAIL" ]; then
echo "Error: Could not find email for $NAME"
exit 1
fi
TRAILER=$(echo "$TRAILER" | sed "s/\\<UNKNOWN_EMAIL\\>/$EMAIL/")
fi
GIT_COMMITTER_NAME=$(git log -1 --pretty=%cN)
GIT_COMMITTER_EMAIL=$(git log -1 --pretty=%cE)
git config set user.name "$GIT_COMMITTER_NAME"
git config set user.email "$GIT_COMMITTER_EMAIL"
export GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL
rm -f .git/hooks/commit-msg
ln -s ../../devtools/commit-msg .git/hooks/commit-msg
git rebase "HEAD~$COMMITS" \
--exec "git commit -C HEAD --no-edit --amend --trailer='$TRAILER'"
git push --force origin "$HEAD_REF"