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