forked from DataDog/dd-trace-py
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbackport
executable file
·155 lines (128 loc) · 4.45 KB
/
backport
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
#!/usr/bin/env bash
set -euo pipefail
if ! which gh &>/dev/null;
then
echo "gh cli tool is required in order to continue"
echo "MacOS install via: brew install gh"
echo "https://github.com/cli/cli#installation"
exit 1
fi
if ! which jq &>/dev/null;
then
echo "jq tool is required in order to continue"
echo "MacOS install via: brew install jq"
echo "https://stedolan.github.io/jq/"
exit 1
fi
usage () {
echo "Usage:"
echo " $0 PR_NUMBER BRANCH_NAME [, BRANCH_NAME, ...]"
echo ""
echo " PR_NUMBER - the GitHub PR number to backport. example: 4376"
echo " BRANCH_NAME - the name of the branch to backport PR_NUMBER to. example: 1.8"
echo ""
echo "Examples:"
echo ""
echo " Backport PR #3489 to branch 0.61"
echo " $0 3489 0.61"
echo ""
echo " Backport PR #4376 to branches 1.8, 1.7, and 1.5"
echo " $0 4376 1.8 1.7 1.5"
}
if [ $# -lt 2 ];
then
echo "Error:"
echo " Too few arguments, must supply at least PR_NUMBER and 1 BRANCH_NAME"
echo ""
usage
exit 1
fi
PR_NUMBER="${1}"
shift 1
TO_BRANCHES="$@"
# Determine the remote to use
# DEV: If anything here fails, then default to 'origin'
# DEV: We might have https://github.com/DataDog/dd-trace-py, or [email protected]:DataDog/dd-trace-py.
REMOTE=origin
if git remote -v show | grep -qe "[Dd]ata[Dd]og/dd-trace-py\(\.git\)\? (push)" > /dev/null
then
REMOTE=$(git remote -v show | grep -e "[Dd]ata[Dd]og/dd-trace-py\(\.git\)\? (push)" | awk '{print $1}')
else
echo "🟠 Failed to automatically determine the remote to use, falling back to 'origin'"
REMOTE="origin"
fi
if [[ -z "${PR_NUMBER}" ]];
then
echo "Error:"
echo " Must supply a PR number to backport"
echo ""
usage
exit 1
fi
if [[ -z "${TO_BRANCHES}" ]];
then
echo "Error:"
echo " Must supply at least 1 branch to backport to"
echo ""
usage
exit 1
fi
# Download data about the PR
echo "Fetching data for PR ${PR_NUMBER}"
pr_data="$(gh pr view --json "state,title,mergeCommit,author,labels,body" ${PR_NUMBER})"
# Verify that we are going to have a merge commit
if "$(echo "${pr_data}" | jq -e '.state != "MERGED"')";
then
echo "PR ${PR_NUMBER} has not been merged yet"
exit 1
fi
# Extract data from the original PR
merge_commit_sha="$(echo "${pr_data}" | jq -r '.mergeCommit.oid')"
original_title="$(echo "${pr_data}" | jq -r '.title')"
original_body="$(echo "${pr_data}" | jq -r '.body')"
original_author="$(echo "${pr_data}" | jq -r '.author.login')"
original_labels="$(echo "${pr_data}" | jq -r '[.labels[].name] | join(",")')"
# Get the current branch so we can switch back to it when we are done
current_branch="$(git rev-parse --abbrev-ref HEAD)"
# Try to make sure we have the latest data from remote
git fetch --all &>/dev/null || true
for base_branch in "$@";
do
echo "Backporting ${PR_NUMBER} (sha: ${merge_commit_sha}) to ${base_branch}"
branch_name="backport-${PR_NUMBER}-to-${base_branch}"
if ! git switch --detach "${REMOTE}/${base_branch}"
then
echo "🟠 Cannot switch to base branch ${base_branch}, skipping ..."
continue
fi
git switch -C "${branch_name}"
echo "Cherry-picking ${merge_commit_sha} (git commit signing required here)"
if ! git cherry-pick -x "${merge_commit_sha}";
then
echo "Cherry-pick failed. In a separate shell, please:"
echo " 1. Manually resolve merge conflicts"
echo " 2. git add resolved files"
echo " 3. Run git cherry-pick --continue to complete the merge"
echo ""
read -p "Press any key to continue once conflict is manually resolved..." -n1 -s
echo ""
fi
echo "Pushing branch ${branch_name}"
git push --set-upstream ${REMOTE} "${branch_name}"
echo "Creating pull request"
IFS='' read -r -d '' new_body <<EOF || true
Backport of #${PR_NUMBER} to ${base_branch}
${original_body}
EOF
new_title="${original_title} [backport #${PR_NUMBER} to ${base_branch}]"
gh pr create --base "${base_branch}" --title "${new_title}" --body "${new_body}" --assignee "@me,${original_author}" --label "${original_labels}"
echo "Enabling auto-merge on PR"
gh pr merge --auto "${branch_name}"
echo "Opening PR in the browser for review"
gh pr view --web "${branch_name}"
# Back to the original branch and delete the temporary branch
echo "Switching back to ${current_branch} and deleting temporary branch ${branch_name}"
git switch "${current_branch}"
git branch -D "${branch_name}"
done
echo "✨ 🍰 ✨ All done! ✨ 🍰 ✨"