Skip to content

Commit 8660e3d

Browse files
authored
Rollup merge of #87604 - yaymukund:verify-backported-commits, r=Mark-Simulacrum
CI: Verify commits in beta & stable are in upstream branches. Closes #74721 I think this does the trick. #87597 is an example of it failing as it should.
2 parents d3e2578 + f19ba35 commit 8660e3d

File tree

4 files changed

+197
-0
lines changed

4 files changed

+197
-0
lines changed

.github/workflows/ci.yml

+9
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,9 @@ jobs:
128128
- name: ensure line endings are correct
129129
run: src/ci/scripts/verify-line-endings.sh
130130
if: success() && !env.SKIP_JOB
131+
- name: ensure backported commits are in upstream branches
132+
run: src/ci/scripts/verify-backported-commits.sh
133+
if: success() && !env.SKIP_JOB
131134
- name: run the build
132135
run: src/ci/scripts/run-build-from-ci.sh
133136
env:
@@ -499,6 +502,9 @@ jobs:
499502
- name: ensure line endings are correct
500503
run: src/ci/scripts/verify-line-endings.sh
501504
if: success() && !env.SKIP_JOB
505+
- name: ensure backported commits are in upstream branches
506+
run: src/ci/scripts/verify-backported-commits.sh
507+
if: success() && !env.SKIP_JOB
502508
- name: run the build
503509
run: src/ci/scripts/run-build-from-ci.sh
504510
env:
@@ -609,6 +615,9 @@ jobs:
609615
- name: ensure line endings are correct
610616
run: src/ci/scripts/verify-line-endings.sh
611617
if: success() && !env.SKIP_JOB
618+
- name: ensure backported commits are in upstream branches
619+
run: src/ci/scripts/verify-backported-commits.sh
620+
if: success() && !env.SKIP_JOB
612621
- name: run the build
613622
run: src/ci/scripts/run-build-from-ci.sh
614623
env:

src/ci/github-actions/ci.yml

+4
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,10 @@ x--expand-yaml-anchors--remove:
206206
run: src/ci/scripts/verify-line-endings.sh
207207
<<: *step
208208

209+
- name: ensure backported commits are in upstream branches
210+
run: src/ci/scripts/verify-backported-commits.sh
211+
<<: *step
212+
209213
- name: run the build
210214
run: src/ci/scripts/run-build-from-ci.sh
211215
env:
+150
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
#!/bin/bash
2+
# Ensure commits in beta are in master & commits in stable are in beta + master.
3+
set -euo pipefail
4+
IFS=$'\n\t'
5+
6+
source "$(cd "$(dirname "$0")" && pwd)/../shared.sh"
7+
8+
# We don't care about commits that predate this automation check, so we pass a
9+
# `<limit>` argument to `git cherry`.
10+
BETA_LIMIT="53fd98ca776cb875bc9e5514f56b52eb74f9e7a9"
11+
STABLE_LIMIT="a178d0322ce20e33eac124758e837cbd80a6f633"
12+
13+
verify_backported_commits_main() {
14+
ci_base_branch=$(ciBaseBranch)
15+
16+
if [[ "$ci_base_branch" != "beta" && "$ci_base_branch" != "stable" ]]; then
17+
echo 'Skipping. This is only run when merging to the beta or stable branches.'
18+
exit 0
19+
fi
20+
21+
echo 'git: unshallowing the repository so we can check commits'
22+
git fetch \
23+
--no-tags \
24+
--no-recurse-submodules \
25+
--progress \
26+
--prune \
27+
--unshallow
28+
29+
if [[ $ci_base_branch == "beta" ]]; then
30+
verify_cherries master "$BETA_LIMIT" \
31+
|| exit 1
32+
33+
elif [[ $ci_base_branch == "stable" ]]; then
34+
(verify_cherries master "$STABLE_LIMIT" \
35+
& verify_cherries beta "$STABLE_LIMIT") \
36+
|| exit 1
37+
38+
fi
39+
}
40+
41+
# Verify all commits in `HEAD` are backports of a commit in <upstream>. See
42+
# https://git-scm.com/docs/git-cherry for an explanation of the arguments.
43+
#
44+
# $1 = <upstream>
45+
# $2 = <limit>
46+
verify_cherries() {
47+
# commits that lack a `backport-of` comment.
48+
local no_backports=()
49+
# commits with an incorrect `backport-of` comment.
50+
local bad_backports=()
51+
52+
commits=$(git cherry "origin/$1" HEAD "$2")
53+
54+
if [[ -z "$commits" ]]; then
55+
echo "All commits in \`HEAD\` are present in \`$1\`"
56+
return 0
57+
fi
58+
59+
commits=$(echo "$commits" | grep '^\+' | cut -c 3-)
60+
61+
while read sha; do
62+
# Check each commit in <current>..<upstream>
63+
backport_sha=$(get_backport "$sha")
64+
65+
if [[ "$backport_sha" == "nothing" ]]; then
66+
echo "\`$sha\` backports nothing"
67+
continue
68+
fi
69+
70+
if [[ -z "$backport_sha" ]]; then
71+
no_backports+=("$sha")
72+
continue
73+
fi
74+
75+
if ! is_in_master "$backport_sha"; then
76+
bad_backports+=("$sha")
77+
continue
78+
fi
79+
80+
echo "\`$sha\` backports \`$backport_sha\`"
81+
done <<< "$commits"
82+
83+
failure=0
84+
85+
if [ ${#no_backports[@]} -ne 0 ]; then
86+
echo 'Error: Could not find backports for all commits.'
87+
echo
88+
echo 'All commits in \`HEAD\` are required to have a corresponding upstream commit.'
89+
echo 'It looks like the following commits:'
90+
echo
91+
for commit in "${no_backports[@]}"; do
92+
echo " $commit"
93+
done
94+
echo
95+
echo "do not match any commits in \`$1\`. If this was intended, add the text"
96+
echo '\`backport-of: <SHA of a commit already in master>\`'
97+
echo 'somewhere in the message of each of these commits.'
98+
echo
99+
failure=1
100+
fi
101+
102+
if [ ${#bad_backports[@]} -ne 0 ]; then
103+
echo 'Error: Found incorrectly marked commits.'
104+
echo
105+
echo 'The following commits:'
106+
echo
107+
for commit in "${bad_backports[@]}"; do
108+
echo " $commit"
109+
done
110+
echo
111+
echo 'have commit messages marked \`backport-of: <SHA>\`, but the SHA is not in'
112+
echo '\`master\`.'
113+
echo
114+
failure=1
115+
fi
116+
117+
return $failure
118+
}
119+
120+
# Get the backport of a commit. It echoes one of:
121+
#
122+
# 1. A SHA of the backported commit
123+
# 2. The string "nothing"
124+
# 3. An empty string
125+
#
126+
# $1 = <sha>
127+
get_backport() {
128+
# This regex is:
129+
#
130+
# ^.* - throw away any extra starting characters
131+
# backport-of: - prefix
132+
# \s\? - optional space
133+
# \(\) - capture group
134+
# [a-f0-9]\+\|nothing - a SHA or the text 'nothing'
135+
# .* - throw away any extra ending characters
136+
# \1 - replace it with the first match
137+
# {s//\1/p;q} - print the first occurrence and quit
138+
#
139+
git show -s --format=%B "$1" \
140+
| sed -n '/^.*backport-of:\s\?\([a-f0-9]\+\|nothing\).*/{s//\1/p;q}'
141+
}
142+
143+
# Check if a commit is in master.
144+
#
145+
# $1 = <sha>
146+
is_in_master() {
147+
git merge-base --is-ancestor "$1" origin/master 2> /dev/null
148+
}
149+
150+
verify_backported_commits_main

src/tools/cherry-pick.sh

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!/bin/bash
2+
set -euo pipefail
3+
IFS=$'\n\t'
4+
5+
print_error() {
6+
echo "Error: \`$1\` is not a valid commit. To debug, run:"
7+
echo
8+
echo " git rev-parse --verify $1"
9+
echo
10+
}
11+
12+
full_sha() {
13+
git rev-parse \
14+
--verify \
15+
--quiet \
16+
"$1^{object}" || print_error $1
17+
}
18+
19+
commit_message_with_backport_note() {
20+
message=$(git log --format=%B -n 1 $1)
21+
echo $message | awk "NR==1{print; print \"\n(backport-of: $1)\"} NR!=1"
22+
}
23+
24+
cherry_pick_commit() {
25+
sha=$(full_sha $1)
26+
git cherry-pick $sha > /dev/null
27+
git commit \
28+
--amend \
29+
--file <(commit_message_with_backport_note $sha)
30+
}
31+
32+
for arg ; do
33+
cherry_pick_commit $arg
34+
done

0 commit comments

Comments
 (0)