blob: 3860f372426e4f947b7ea2f0f09211bdc3004582 (
plain)
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
|
#!/bin/bash
#Copyright 2021 The gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -euo pipefail
ensure_command () {
if command -v "$1" 1>/dev/null 2>&1; then
return 0
else
echo "$1 is not installed. Please install it to proceed." 1>&2
exit 1
fi
}
display_usage () {
cat << EOF >/dev/stderr
USAGE: $0 PR_ID GITHUB_USER BACKPORT_BRANCHES REVIEWERS [-c PER_BACKPORT_COMMAND]
PR_ID: The ID of the PR to be backported.
GITHUB_USER: Your GitHub username.
BACKPORT_BRANCHES: A space-separated list of branches to which the source PR will be backported.
REVIEWERS: A comma-separated list of users to add as both reviewer and assignee.
PER_BACKPORT_COMMAND : An optional command to run after cherrypicking the PR to the target branch.
If you use this option, ensure your working directory is clean, as "git add -A" will be used to
incorporate any generated files. Try running "git clean -xdff" beforehand.
Example: $0 25456 gnossen "v1.30.x v1.31.x v1.32.x v1.33.x v1.34.x v1.35.x v1.36.x" "menghanl,gnossen"
Example: $0 25493 gnossen "\$(seq 30 33 | xargs -n1 printf 'v1.%s.x ')" "menghanl" -c ./tools/dockerfile/push_testing_images.sh
EOF
exit 1
}
ensure_command "curl"
ensure_command "egrep"
ensure_command "hub"
ensure_command "jq"
if [ "$#" -lt "4" ]; then
display_usage
fi
PR_ID="$1"
GITHUB_USER="$2"
BACKPORT_BRANCHES="$3"
REVIEWERS="$4"
shift 4
PER_BACKPORT_COMMAND=""
while getopts "c:" OPT; do
case "$OPT" in
c )
PER_BACKPORT_COMMAND="$OPTARG"
;;
\? )
echo "Invalid option: $OPTARG" >/dev/stderr
display_usage
;;
: )
echo "Invalid option: $OPTARG requires an argument." >/dev/stderr
display_usage
;;
esac
done
if [[ ! -z "$(git status --porcelain)" && ! -z "$PER_BACKPORT_COMMAND" ]]; then
echo "Your working directory is not clean. Try running `git clean -xdff`. Warning: This is irreversible." > /dev/stderr
exit 1
fi
if [ -z "$GITHUB_TOKEN" ]; then
echo "A GitHub token is required to run this script. See " \
"https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token" \
" for more information" >/dev/stderr
exit 1
fi
echo "This script will create a collection of backport PRs. You will probably " \
"have to touch your gnubby a frustrating number of times. C'est la vie."
printf "Press any key to continue."
read -r RESPONSE </dev/tty
printf "\n"
PR_DATA=$(curl -s -u "$GITHUB_USER:$GITHUB_TOKEN" \
-H "Accept: application/vnd.github.v3+json" \
"https://api.github.com/repos/grpc/grpc/pulls/$PR_ID")
STATE=$(echo "$PR_DATA" | jq -r '.state')
if [ "$STATE" != "open" ]; then
TARGET_COMMITS=$(echo "$PR_DATA" | jq -r '.merge_commit_sha')
FETCH_HEAD_REF=$(echo "$PR_DATA" | jq -r '.base.ref')
SOURCE_REPO=$(echo "$PR_DATA" | jq -r '.base.repo.full_name')
else
COMMITS_URL=$(echo "$PR_DATA" | jq -r '.commits_url')
COMMITS_DATA=$(curl -s -u "$GITHUB_USER:$GITHUB_TOKEN" \
-H "Accept: application/vnd.github.v3+json" \
"$COMMITS_URL")
TARGET_COMMITS=$(echo "$COMMITS_DATA" | jq -r '. | map(.sha) | join(" ")')
FETCH_HEAD_REF=$(echo "$PR_DATA" | jq -r '.head.sha')
SOURCE_REPO=$(echo "$PR_DATA" | jq -r '.head.repo.full_name')
fi
PR_TITLE=$(echo "$PR_DATA" | jq -r '.title')
PR_DESCRIPTION=$(echo "$PR_DATA" | jq -r '.body')
LABELS=$(echo "$PR_DATA" | jq -r '.labels | map(.name) | join(",")')
set -x
git fetch "git@github.com:$SOURCE_REPO.git" "$FETCH_HEAD_REF"
BACKPORT_PRS=""
for BACKPORT_BRANCH in $BACKPORT_BRANCHES; do
echo "Backporting $TARGET_COMMITS to $BACKPORT_BRANCH."
git checkout "origin/$BACKPORT_BRANCH"
BRANCH_NAME="backport_${PR_ID}_to_${BACKPORT_BRANCH}"
# To make the script idempotent.
git branch -D "$BRANCH_NAME" || true
git checkout "$BACKPORT_BRANCH"
git checkout -b "$BRANCH_NAME"
for TARGET_COMMIT in $TARGET_COMMITS; do
git cherry-pick -m 1 "$TARGET_COMMIT"
done
if [[ ! -z "$PER_BACKPORT_COMMAND" ]]; then
git submodule update --init --recursive
# To remove dangling submodules.
git clean -xdff
eval "$PER_BACKPORT_COMMAND"
git add -A
git commit --amend --no-edit
fi
BACKPORT_PR=$(hub pull-request -p -m "[Backport] $PR_TITLE" \
-m "*Beep boop. This is an automatically generated backport of #${PR_ID}.*" \
-m "$PR_DESCRIPTION" \
-l "$LABELS" \
-b "$GITHUB_USER:$BACKPORT_BRANCH" \
-r "$REVIEWERS" \
-a "$REVIEWERS" | tail -n 1)
BACKPORT_PRS+="$BACKPORT_PR\n"
# TODO: Turn on automerge once the Github API allows it.
done
printf "Your backport PRs have been created:\n$BACKPORT_PRS"
|