github-merge-pr: add generic enough instruction on how to use the script
[maintainer-tools.git] / github-merge-pr.sh
1 #!/bin/bash
2
3 # How to setup the repository to make this correctly work
4 # 1. Clone repo and make sure you can correctly push to that
5 # (example with openwrt main repo you need to use ssh remote url)
6 # 2. Make sure you can correctly push and force push to the github
7 # repository
8 #
9 # Usage: github-merge-pr.sh PR_NUMBER BRANCH
10
11 # Github repository, just the name/repo part, no .git suffix, no base url!
12 REPO="openwrt/openwrt"
13
14 # Your repository token, generate this token at your profile page:
15 # - Navigate to https://github.com/settings/tokens
16 # - Click on "Generate new token"
17 # - Enter a description, e.g. "pr.sh" and pick the "repo" scope
18 # - Hit "Generate token"
19 #TOKEN="d41d8cd98f00b204e9800998ecf8427e"
20 #
21 # Uncomment this line to use SSH key to rebase PR branch
22 # USE_SSH=1
23
24 PRID="$1"
25 BRANCH="${2:-main}"
26 DRY_RUN="$3"
27 GIT=git
28
29 yesno() {
30 local prompt="$1"
31 local default="${2:-n}"
32 local input
33
34 while [ 1 ]; do
35 printf "%s y/n [%s] > " "$prompt" "$default"
36 read input
37 case "${input:-$default}" in
38 y*) return 0 ;;
39 n*) return 1 ;;
40 esac
41 done
42 }
43
44 if ! command -v jq &> /dev/null; then
45 echo "jq could not be found! This script require jq!"
46 exit 1
47 fi
48
49 if [ -z "$PRID" -o -n "${PRID//[0-9]*/}" ]; then
50 echo "Usage: $0 <PR-ID> [rebase-branch] [dry-run]" >&2
51 exit 1
52 fi
53
54 if [ -n "$DRY_RUN" ]; then
55 GIT="echo git"
56 fi
57
58 if [ -z "$(git branch --list "$BRANCH")" ]; then
59 echo "Given rebase branch '$BRANCH' does not exist!" >&2
60 exit 2
61 fi
62
63 if ! PR_INFO="$(curl -f -s "https://api.github.com/repos/$REPO/pulls/$PRID")"; then
64 echo "Failed fetch PR #$PRID info" >&2
65 exit 3
66 fi
67
68 if [ "$(echo "$PR_INFO" | jq -r ".maintainer_can_modify")" == "false" ]; then
69 echo "PR #$PRID can't be force pushed by maintainers. Trying to merge as is!" >&2
70 fi
71
72 if [ "$(echo "$PR_INFO" | jq -r ".mergeable")" == "false" ]; then
73 echo "PR #$PRID is not mergeable for Github.com. Check the PR!" >&2
74 exit 5
75 fi
76
77 echo "Pulling current $BRANCH from origin"
78 $GIT checkout $BRANCH
79 $GIT fetch origin $BRANCH
80
81 if ! $GIT rebase origin/$BRANCH; then
82 echo "Failed to rebase $BRANCH with origin/$BRANCH" >&2
83 exit 7
84 fi
85
86 PR_USER="$(echo "$PR_INFO" | jq -r ".head.user.login")"
87 PR_BRANCH="$(echo "$PR_INFO" | jq -r ".head.ref")"
88 LOCAL_PR_BRANCH="$PR_BRANCH"-"$PR_USER"
89 if [ "$USE_SSH" = "1" ]; then
90 PR_REPO="$(echo "$PR_INFO" | jq -r ".head.repo.ssh_url")"
91 else
92 PR_REPO="$(echo "$PR_INFO" | jq -r ".head.repo.html_url")"
93 fi
94
95 if ! $GIT remote get-url $PR_USER &> /dev/null || [ -n "$DRY_RUN" ]; then
96 echo "Adding $PR_USER with repo $PR_REPO to remote"
97 $GIT remote add $PR_USER $PR_REPO
98 fi
99
100 echo "Fetching remote $PR_USER"
101 $GIT fetch $PR_USER $PR_BRANCH
102
103 if [ "$(echo "$PR_INFO" | jq -r ".maintainer_can_modify")" == "true" ]; then
104 echo "Creating branch $LOCAL_PR_BRANCH for $PR_BRANCH"
105 if ! $GIT checkout -b $LOCAL_PR_BRANCH $PR_USER/$PR_BRANCH; then
106 echo "Failed to checkout new branch $PR_BRANCH from $PR_USER/$PR_BRANCH" >&2
107 exit 8
108 fi
109
110 echo "Rebasing $LOCAL_PR_BRANCH on top of $BRANCH"
111 if ! $GIT rebase origin/$BRANCH; then
112 echo "Failed to rebase $PR_BRANCH with origin/$BRANCH" >&2
113 exit 9
114 fi
115
116 echo "Force pushing $LOCAL_PR_BRANCH to HEAD:$PR_BRANCH for $PR_USER"
117 if ! $GIT push $PR_USER HEAD:$PR_BRANCH --force; then
118 echo "Failed to force push HEAD to $PR_USER" >&2
119 exit 10
120 fi
121
122 echo "Returning to $BRANCH"
123 $GIT checkout $BRANCH
124 fi
125
126 if [ -n "$($GIT log origin/$BRANCH..HEAD)" ]; then
127 echo "Working on dirty branch for $BRANCH! Please reset $BRANCH to origin/$BRANCH" >&2
128 exit 11
129 fi
130
131 echo "Actually merging the PR #$PRID from branch $PR_USER/$PR_BRANCH"
132 if ! $GIT merge --ff-only $PR_USER/$PR_BRANCH; then
133 echo "Failed to merge $PR_USER/$PR_BRANCH on $BRANCH" >&2
134 else
135 if yesno "Push to openwrt $BRANCH" "y"; then
136 echo "Pushing to openwrt git server"
137 if ! $GIT push; then
138 echo "Failed to push to $BRANCH but left branch as is." >&2
139 exit 12
140 fi
141
142 # Default close comment
143 COMMENT="Thanks! Rebased on top of $BRANCH and merged!"
144
145 if [ -n "$TOKEN" ] && [ -z "$DRY_RUN" ]; then
146 echo ""
147 echo "Enter a comment and hit <enter> to close the PR at Github automatically now."
148 echo "Hit <ctrl>-<c> to exit."
149 echo ""
150 echo "If you do not provide a comment, the default will be: "
151 echo "[$COMMENT]"
152
153 echo -n "Comment > "
154 read usercomment
155
156 echo "Sending message to PR..."
157
158 comment="${usercomment:-$COMMENT}"
159 comment="${comment//\\/\\\\}"
160 comment="${comment//\"/\\\"}"
161 comment="$(printf '{"body":"%s"}' "$comment")"
162
163 if ! curl -s -o /dev/null -w "%{http_code} %{url_effective}\\n" --user "$TOKEN:x-oauth-basic" --request POST --data "$comment" "https://api.github.com/repos/$REPO/issues/$PRID/comments" || \
164 ! curl -s -o /dev/null -w "%{http_code} %{url_effective}\\n" --user "$TOKEN:x-oauth-basic" --request PATCH --data '{"state":"closed"}' "https://api.github.com/repos/$REPO/pulls/$PRID"
165 then
166 echo "" >&2
167 echo "Something failed while sending comment to the PR via ">&2
168 echo "the Github API, please review the state manually at " >&2
169 echo "https://github.com/$REPO/pull/$PRID" >&2
170 exit 6
171 fi
172 fi
173
174 echo -e "\n"
175 echo "The PR has been merged!"
176 echo -e "\n"
177 fi
178 fi
179
180 if [ "$(echo "$PR_INFO" | jq -r ".maintainer_can_modify")" == "true" ]; then
181 echo "Deleting branch $LOCAL_PR_BRANCH"
182 $GIT branch -D $LOCAL_PR_BRANCH
183 fi
184
185 exit 0