Skip to content

Commit 094ca2b

Browse files
committed
ci: new workflow for sending emails after merging a PR
This new workflow is used to send emails to the gcc-patches@ mailing list after every merged PR. For each merged PR: - remove commits touching files not to-be-upstreamed (e.g. github workflow, README.md, ...) - if the number of commit is 1, send a single email and add a header explaining what this is about, a link to the PR, ... - if the number of commits is > 1, then create a cover letter and put the header there. Authors of commits will be put in Cc: of emails. Configure it by setting the following secrets in github: - PATCH_TO: the "To:" field for the emails - PATCH_CC: optional "Cc:" - SMTP_FROM, SMTP_PASSWORD, SMTP_PORT, SMTP_SERVER, STMP_USERNAME: self explanatory ChangeLog: * .github/workflows/send-emails.yml: New file.
1 parent 551b071 commit 094ca2b

File tree

1 file changed

+208
-0
lines changed

1 file changed

+208
-0
lines changed

.github/workflows/send-emails.yml

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
name: Send emails for merged PR
2+
3+
on:
4+
pull_request:
5+
types:
6+
- closed
7+
branches:
8+
- "master"
9+
10+
jobs:
11+
send_patches:
12+
if: github.event.pull_request.merged == true
13+
runs-on: ubuntu-latest
14+
15+
steps:
16+
- name: Checkout repo with full history
17+
uses: actions/checkout@v5
18+
with:
19+
fetch-depth: 100
20+
21+
- name: Install deps (git send-email and jq)
22+
run: |
23+
sudo apt-get update
24+
sudo apt-get install -y jq
25+
26+
## GHA images have a special git package that conflicts with
27+
## git-send-email So this is a workaround to force the installation of
28+
## the conflicting package.
29+
sudo apt-get install -y libauthen-sasl-perl \
30+
libemail-valid-perl \
31+
libio-socket-ssl-perl \
32+
libmailtools-perl perl \
33+
libnet-smtp-ssl-perl
34+
35+
apt-get download git-email
36+
sudo dpkg -i --force-all git-email*deb
37+
38+
- name: Configure git identity
39+
run: |
40+
git config user.name "gccrs gerris bot"
41+
git config user.email "${{ secrets.SMTP_FROM }}"
42+
43+
- name: Export env
44+
env:
45+
GH_EVENT: ${{ toJson(github.event) }}
46+
run: |
47+
echo "$GH_EVENT" > /tmp/gh_event.json
48+
49+
PR_BASE_REF=$(jq -r '.pull_request.base.sha' /tmp/gh_event.json)
50+
echo "PR_BASE_REF=$PR_BASE_REF" >> $GITHUB_ENV
51+
52+
PR_NUMBER=$(jq -r '.pull_request.number' /tmp/gh_event.json)
53+
echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_ENV
54+
55+
PR_TITLE=$(jq -r '.pull_request.title' /tmp/gh_event.json)
56+
echo "PR_TITLE=$PR_TITLE" >> $GITHUB_ENV
57+
58+
PR_URL=$(jq -r '.pull_request.html_url' /tmp/gh_event.json)
59+
echo "PR_URL=$PR_URL" >> $GITHUB_ENV
60+
61+
PR_MERGE_COMMIT=$(jq -r '.pull_request.merge_commit_sha' /tmp/gh_event.json)
62+
echo "PR_MERGE_COMMIT=$PR_MERGE_COMMIT" >> $GITHUB_ENV
63+
64+
PR_TARGET_BRANCH=$(jq -r '.pull_request.base.ref' /tmp/gh_event.json)
65+
echo "PR_TARGET_BRANCH=$PR_TARGET_BRANCH" >> $GITHUB_ENV
66+
67+
PR_LABELS=$(jq -r '[.pull_request.labels[].name] | join(",")' /tmp/gh_event.json)
68+
echo "PR_LABELS=$PR_LABELS" >> $GITHUB_ENV
69+
70+
REPO_SSH=$(jq -r '.repository.ssh_url' /tmp/gh_event.json)
71+
echo "REPO_SSH=$REPO_SSH" >> $GITHUB_ENV
72+
73+
echo "SERIES_DIR=/tmp/series" >> $GITHUB_ENV
74+
75+
- name: Check for label 'no-ml' to skip sending emails
76+
run: |
77+
# Skip if PR has label "no-ml"
78+
if echo "$PR_LABELS" | grep -qiE "(^|,)no-ml(,|$)"; then
79+
echo "Opt-out label present: skipping mailing list."
80+
echo "Opt-out label present: skipping mailing list." > $GITHUB_STEP_SUMMARY
81+
exit 0
82+
fi
83+
84+
- name: Get commit list from PR and skip the internal ones
85+
id: commits
86+
run: |
87+
# Skip commits that touches any of these
88+
patterns=(".github/"
89+
"CODE_OF_CONDUCT.md"
90+
"CONTRIBUTING.md"
91+
"Dockerfile"
92+
"README.md"
93+
"logo.png"
94+
"gcc/rust/gource-gccrs.sh"
95+
"gcc/rust/monthly-diff.py"
96+
)
97+
regex=$(printf '%s\n' "${patterns[@]}" | sed -e 's/[.[\*^$+?(){|\/\\]/\\&/g' | paste -sd'|' -)
98+
99+
rm -f /tmp/commits.txt
100+
101+
git log --format="%H" "$PR_BASE_REF..HEAD" | while read SHA1; do
102+
if grep -q -E "$regex" <(git diff-tree --no-commit-id --name-only -r "$SHA1"); then
103+
echo "Touching something not to be upstreamed, skipping commit $SHA1"
104+
else
105+
echo "$SHA1" >> /tmp/commits.txt
106+
fi
107+
done
108+
109+
COUNT=$(wc -l < /tmp/commits.txt)
110+
echo "COUNT=$COUNT" >> $GITHUB_ENV
111+
112+
- name: Check what to do based on series' size
113+
run: |
114+
MAX=150
115+
if [ "${COUNT}" -gt "$MAX" ]; then
116+
echo "Series has $COUNT commits (> $MAX). Not doing anything" >> $GITHUB_STEP_SUMMARY
117+
exit 0
118+
else if [ "${COUNT}" -eq 1 ]; then
119+
echo "Single commit PR, don't do the cover letter"
120+
echo "DO_COVER=0" >> $GITHUB_ENV
121+
else
122+
echo "Multiple commits ($COUNT) in PR, create a cover letter"
123+
echo "DO_COVER=1" >> $GITHUB_ENV
124+
fi fi
125+
126+
- name: Prepare patch series
127+
run: |
128+
set -euo pipefail
129+
130+
# Create a temporary branch that linearizes the PR commits
131+
git checkout -B ci-mail-patches "$PR_BASE_REF"
132+
# Cherry-pick commits in the exact PR order (no-commit to batch, then commit)
133+
while read sha; do
134+
git cherry-pick "$sha"
135+
done < /tmp/commits.txt
136+
137+
# Build cover letter text
138+
N="${COUNT:-0}"
139+
TITLE="$(printf '[PATCH 0/%d] PR #%s: %s' "$N" "$PR_NUMBER" "$PR_TITLE")"
140+
141+
echo "PR: $PR_URL" > /tmp/description.txt
142+
echo "Merged by: ${{ github.actor }}" >> /tmp/description.txt
143+
echo "Base: $PR_TARGET_BRANCH" >> /tmp/description.txt
144+
echo "" >> /tmp/description.txt
145+
echo "This series was merged into the gccrs repository and is posted here for" >> /tmp/description.txt
146+
echo "upstream visibility and potential drive-by review, as requested by GCC" >> /tmp/description.txt
147+
echo "release managers." >> /tmp/description.txt
148+
echo "" >> /tmp/description.txt
149+
echo " Notes:" >> /tmp/description.txt
150+
echo " - Source branch: $(jq -r '.pull_request.head.label' /tmp/gh_event.json)" >> /tmp/description.txt
151+
echo " - Merge commit: $PR_MERGE_COMMIT" >> /tmp/description.txt
152+
echo " - Commit count: $N" >> /tmp/description.txt
153+
echo "" >> /tmp/description.txt
154+
155+
mkdir /tmp/series
156+
157+
if [ "$DO_COVER" = "1" ]; then
158+
# Generate series + cover letter
159+
git format-patch \
160+
--subject-prefix="gccrs COMMIT" \
161+
--cover-letter \
162+
--description-file=/tmp/description.txt \
163+
--base="$PR_BASE_REF" \
164+
--output-directory /tmp/series \
165+
"$PR_BASE_REF"..HEAD
166+
167+
awk -v CONTENT="$PR_TITLE" '{gsub(/\*\*\* SUBJECT HERE \*\*\*/, CONTENT); print}' /tmp/series/0000-cover-letter.patch > /tmp/temp
168+
mv /tmp/temp /tmp/series/0000-cover-letter.patch
169+
else
170+
# Generate series + cover letter
171+
git format-patch \
172+
--subject-prefix="gccrs COMMIT" \
173+
--no-cover-letter \
174+
--base="$PR_BASE_REF" \
175+
--output-directory /tmp/series \
176+
"$PR_BASE_REF"..HEAD
177+
178+
# for every patch file, insert the github header right after the '---'.
179+
sed '/^---$/ r /tmp/description.txt' -i /tmp/series/*
180+
fi
181+
182+
- name: Send series via git send-email
183+
env:
184+
GIT_SMTP_SERVER: ${{ secrets.SMTP_SERVER }}
185+
GIT_SMTP_ENCRYPTION: tls
186+
GIT_SMTP_SERVER_PORT: ${{ secrets.SMTP_PORT }}
187+
GIT_SMTP_AUTH: "true"
188+
GIT_SMTP_USER: ${{ secrets.SMTP_USERNAME }}
189+
GIT_SMTP_PASSWORD: ${{ secrets.SMTP_PASSWORD }}
190+
FROM: ${{ secrets.SMTP_FROM }}
191+
TO: ${{ secrets.PATCH_TO }}
192+
CC: ${{ secrets.PATCH_CC }}
193+
run: |
194+
set -euo pipefail
195+
196+
git config sendemail.smtpserver "$GIT_SMTP_SERVER"
197+
git config sendemail.smtpserverport "$GIT_SMTP_SERVER_PORT"
198+
git config sendemail.smtpencryption "$GIT_SMTP_ENCRYPTION"
199+
git config sendemail.smtpuser "$GIT_SMTP_USER"
200+
git config sendemail.smtppass "$GIT_SMTP_PASSWORD"
201+
git config sendemail.from "$FROM"
202+
git config sendemail.to "$TO"
203+
if [ -n "${CC:-}" ]; then git config sendemail.cc "$CC"; fi
204+
git config sendemail.thread "true"
205+
git config sendemail.confirm "never"
206+
git config sendemail.annotate "false"
207+
208+
git send-email /tmp/series/*

0 commit comments

Comments
 (0)