github.com/ironcore-dev/gardener-extension-provider-ironcore@v0.3.2-0.20240314231816-8336447fb9a0/hack/cherry-pick-pull.sh (about) 1 #!/usr/bin/env bash 2 3 # Copyright 2015 The Kubernetes Authors. 4 # 5 # Licensed under the Apache License, Version 2.0 (the "License"); 6 # you may not use this file except in compliance with the License. 7 # You may obtain a copy of the License at 8 # 9 # http://www.apache.org/licenses/LICENSE-2.0 10 # 11 # Unless required by applicable law or agreed to in writing, software 12 # distributed under the License is distributed on an "AS IS" BASIS, 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 # See the License for the specific language governing permissions and 15 # limitations under the License. 16 17 # This file was copied from the kubernetes/kubernetes project 18 # https://github.com/kubernetes/kubernetes/blob/v1.20.0/hack/cherry_pick_pull.sh 19 # 20 # Modifications Copyright 2021 SAP SE or an SAP affiliate company. All rights reserved. 21 22 # Usage Instructions: https://github.com/gardener/gardener/blob/master/docs/development/process.md#cherry-picks 23 24 # Checkout a PR from GitHub. (Yes, this is sitting in a Git tree. How 25 # meta.) Assumes you care about pulls from remote "upstream" and 26 # checks them out to a branch named: 27 # automated-cherry-pick-of-<pr>-<target branch>-<timestamp> 28 29 set -o errexit 30 set -o nounset 31 set -o pipefail 32 33 REPO_ROOT="$(git rev-parse --show-toplevel)" 34 declare -r REPO_ROOT 35 cd "${REPO_ROOT}" 36 37 STARTINGBRANCH=$(git symbolic-ref --short HEAD) 38 declare -r STARTINGBRANCH 39 declare -r REBASEMAGIC="${REPO_ROOT}/.git/rebase-apply" 40 DRY_RUN=${DRY_RUN:-""} 41 UPSTREAM_REMOTE=${UPSTREAM_REMOTE:-upstream} 42 FORK_REMOTE=${FORK_REMOTE:-origin} 43 MAIN_REPO_ORG=${MAIN_REPO_ORG:-$(git remote get-url "$UPSTREAM_REMOTE" | awk '{gsub(/http[s]:\/\/|git@/,"")}1' | awk -F'[@:./]' 'NR==1{print $3}')} 44 MAIN_REPO_NAME=${MAIN_REPO_NAME:-$(git remote get-url "$UPSTREAM_REMOTE" | awk '{gsub(/http[s]:\/\/|git@/,"")}1' | awk -F'[@:./]' 'NR==1{print $4}')} 45 DEPRECATED_RELEASE_NOTE_CATEGORY="|noteworthy|improvement|action" 46 RELEASE_NOTE_CATEGORY="(breaking|feature|bugfix|doc|other${DEPRECATED_RELEASE_NOTE_CATEGORY})" 47 RELEASE_NOTE_TARGET_GROUP="(user|operator|developer|dependency)" 48 49 if [[ -z ${GITHUB_USER:-} ]]; then 50 echo "Please export GITHUB_USER=<your-user> (or GH organization, if that's where your fork lives)" 51 exit 1 52 fi 53 54 if ! which hub > /dev/null; then 55 echo "Can't find 'hub' tool in PATH, please install from https://github.com/github/hub" 56 exit 1 57 fi 58 59 if [[ "$#" -lt 2 ]]; then 60 echo "${0} <upstream remote>/<remote branch> <pr-number>...: cherry pick one or more <pr> onto <remote branch> and leave instructions for proposing pull request" 61 echo 62 echo " Checks out <upstream remote>/<remote branch> and handles the cherry-pick of <pr> (possibly multiple) for you." 63 echo " Examples:" 64 echo " $0 ${UPSTREAM_REMOTE}/release-3.14 12345 # Cherry-picks PR 12345 onto ${UPSTREAM_REMOTE}/release-3.14 and proposes that as a PR." 65 echo " $0 ${UPSTREAM_REMOTE}/release-3.14 12345 56789 # Cherry-picks PR 12345, then 56789 and proposes the combination as a single PR." 66 echo 67 echo " Set the DRY_RUN environment var to skip git push and creating PR." 68 echo " This is useful for creating patches to a release branch without making a PR." 69 echo " When DRY_RUN is set the script will leave you in a branch containing the commits you cherry-picked." 70 echo 71 echo " Set UPSTREAM_REMOTE (default: upstream) and FORK_REMOTE (default: origin)" 72 echo " to override the default remote names to what you have locally." 73 exit 2 74 fi 75 76 if git_status=$(git status --porcelain --untracked=no 2>/dev/null) && [[ -n "${git_status}" ]]; then 77 echo "!!! Dirty tree. Clean up and try again." 78 exit 1 79 fi 80 81 if [[ -e "${REBASEMAGIC}" ]]; then 82 echo "!!! 'git rebase' or 'git am' in progress. Clean up and try again." 83 exit 1 84 fi 85 86 declare -r BRANCH="$1" 87 shift 1 88 declare -r PULLS=( "$@" ) 89 90 function join { local IFS="$1"; shift; echo "$*"; } 91 PULLDASH=$(join - "${PULLS[@]/#/#}") # Generates something like "#12345-#56789" 92 declare -r PULLDASH 93 PULLSUBJ=$(join " " "${PULLS[@]/#/#}") # Generates something like "#12345 #56789" 94 declare -r PULLSUBJ 95 96 echo "+++ Updating remotes..." 97 git remote update "${UPSTREAM_REMOTE}" "${FORK_REMOTE}" 98 99 if ! git log -n1 --format=%H "${BRANCH}" >/dev/null 2>&1; then 100 echo "!!! '${BRANCH}' not found. The second argument should be something like ${UPSTREAM_REMOTE}/release-0.21." 101 echo " (In particular, it needs to be a valid, existing remote branch that I can 'git checkout'.)" 102 exit 1 103 fi 104 105 NEWBRANCHREQ="automated-cherry-pick-of-${PULLDASH}" # "Required" portion for tools. 106 declare -r NEWBRANCHREQ 107 NEWBRANCH="$(echo "${NEWBRANCHREQ}-${BRANCH}" | sed 's/\//-/g')" 108 declare -r NEWBRANCH 109 NEWBRANCHUNIQ="${NEWBRANCH}-$(date +%s)" 110 declare -r NEWBRANCHUNIQ 111 echo "+++ Creating local branch ${NEWBRANCHUNIQ}" 112 113 cleanbranch="" 114 prtext="" 115 gitamcleanup=false 116 function return_to_kansas { 117 if [[ "${gitamcleanup}" == "true" ]]; then 118 echo 119 echo "+++ Aborting in-progress git am." 120 git am --abort >/dev/null 2>&1 || true 121 fi 122 123 # return to the starting branch and delete the PR text file 124 if [[ -z "${DRY_RUN}" ]]; then 125 echo 126 echo "+++ Returning you to the ${STARTINGBRANCH} branch and cleaning up." 127 git checkout -f "${STARTINGBRANCH}" >/dev/null 2>&1 || true 128 if [[ -n "${cleanbranch}" ]]; then 129 git branch -D "${cleanbranch}" >/dev/null 2>&1 || true 130 fi 131 if [[ -n "${prtext}" ]]; then 132 rm "${prtext}" 133 fi 134 fi 135 } 136 trap return_to_kansas EXIT 137 138 SUBJECTS=() 139 RELEASE_NOTES=() 140 LABELS=() 141 function make-a-pr() { 142 local rel 143 rel="$(basename "${BRANCH}")" 144 echo 145 echo "+++ Creating a pull request on GitHub at ${GITHUB_USER}:${NEWBRANCH}" 146 147 # This looks like an unnecessary use of a tmpfile, but it avoids 148 # https://github.com/github/hub/issues/976 Otherwise stdin is stolen 149 # when we shove the heredoc at hub directly, tickling the ioctl 150 # crash. 151 prtext="$(mktemp -t prtext.XXXX)" # cleaned in return_to_kansas 152 local numandtitle 153 numandtitle=$(printf '%s\n' "${SUBJECTS[@]}") 154 relnotes=$(printf "${RELEASE_NOTES[@]}") 155 labels=$(printf "${LABELS[@]}") 156 cat >"${prtext}" <<EOF 157 [${rel}] Automated cherry pick of ${numandtitle} 158 159 ${labels} 160 161 Cherry pick of ${PULLSUBJ} on ${rel}. 162 163 ${numandtitle} 164 165 **Release Notes:** 166 ${relnotes} 167 EOF 168 169 hub pull-request -F "${prtext}" -h "${GITHUB_USER}:${NEWBRANCH}" -b "${MAIN_REPO_ORG}:${rel}" 170 } 171 172 git checkout -b "${NEWBRANCHUNIQ}" "${BRANCH}" 173 cleanbranch="${NEWBRANCHUNIQ}" 174 175 gitamcleanup=true 176 for pull in "${PULLS[@]}"; do 177 echo "+++ Downloading patch to /tmp/${pull}.patch (in case you need to do this again)" 178 179 curl -o "/tmp/${pull}.patch" -sSL "https://github.com/${MAIN_REPO_ORG}/${MAIN_REPO_NAME}/pull/${pull}.patch" 180 echo 181 echo "+++ About to attempt cherry pick of PR. To reattempt:" 182 echo " $ git am -3 /tmp/${pull}.patch" 183 echo 184 git am -3 "/tmp/${pull}.patch" || { 185 conflicts=false 186 while unmerged=$(git status --porcelain | grep ^U) && [[ -n ${unmerged} ]] \ 187 || [[ -e "${REBASEMAGIC}" ]]; do 188 conflicts=true # <-- We should have detected conflicts once 189 echo 190 echo "+++ Conflicts detected:" 191 echo 192 (git status --porcelain | grep ^U) || echo "!!! None. Did you git am --continue?" 193 echo 194 echo "+++ Please resolve the conflicts in another window (and remember to 'git add / git am --continue')" 195 read -p "+++ Proceed (anything but 'y' aborts the cherry-pick)? [y/n] " -r 196 echo 197 if ! [[ "${REPLY}" =~ ^[yY]$ ]]; then 198 echo "Aborting." >&2 199 exit 1 200 fi 201 done 202 203 if [[ "${conflicts}" != "true" ]]; then 204 echo "!!! git am failed, likely because of an in-progress 'git am' or 'git rebase'" 205 exit 1 206 fi 207 } 208 209 # set the subject 210 pr_info=$(curl "https://api.github.com/repos/${MAIN_REPO_ORG}/${MAIN_REPO_NAME}/pulls/${pull}" -sS) 211 subject=$(echo ${pr_info} | jq -cr '.title') 212 SUBJECTS+=("#${pull}: ${subject}") 213 labels=$(echo ${pr_info} | jq '.labels[].name' -cr | grep -P '^(area|kind)' | sed -e 's|/| |' -e 's|^|/|g') 214 LABELS+=("${labels}") 215 216 # remove the patch file from /tmp 217 rm -f "/tmp/${pull}.patch" 218 219 # get the release notes 220 notes=$(echo ${pr_info} | jq '.body' | grep -Po "\`\`\` *${RELEASE_NOTE_CATEGORY} ${RELEASE_NOTE_TARGET_GROUP}.*?\`\`\`" || true) 221 RELEASE_NOTES+=("${notes}") 222 done 223 gitamcleanup=false 224 225 if [[ -n "${DRY_RUN}" ]]; then 226 echo "!!! Skipping git push and PR creation because you set DRY_RUN." 227 echo "To return to the branch you were in when you invoked this script:" 228 echo 229 echo " git checkout ${STARTINGBRANCH}" 230 echo 231 echo "To delete this branch:" 232 echo 233 echo " git branch -D ${NEWBRANCHUNIQ}" 234 exit 0 235 fi 236 237 if git remote -v | grep ^"${FORK_REMOTE}" | grep "${MAIN_REPO_ORG}/${MAIN_REPO_NAME}.git"; then 238 echo "!!! You have ${FORK_REMOTE} configured as your ${MAIN_REPO_ORG}/${MAIN_REPO_NAME}.git" 239 echo "This isn't normal. Leaving you with push instructions:" 240 echo 241 echo "+++ First manually push the branch this script created:" 242 echo 243 echo " git push REMOTE ${NEWBRANCHUNIQ}:${NEWBRANCH}" 244 echo 245 echo "where REMOTE is your personal fork (maybe ${UPSTREAM_REMOTE}? Consider swapping those.)." 246 echo "OR consider setting UPSTREAM_REMOTE and FORK_REMOTE to different values." 247 echo 248 make-a-pr 249 cleanbranch="" 250 exit 0 251 fi 252 253 echo 254 echo "+++ I'm about to do the following to push to GitHub (and I'm assuming ${FORK_REMOTE} is your personal fork):" 255 echo 256 echo " git push ${FORK_REMOTE} ${NEWBRANCHUNIQ}:${NEWBRANCH}" 257 echo 258 read -p "+++ Proceed (anything but 'y' aborts the cherry-pick)? [y/n] " -r 259 if ! [[ "${REPLY}" =~ ^[yY]$ ]]; then 260 echo "Aborting." >&2 261 exit 1 262 fi 263 264 git push "${FORK_REMOTE}" -f "${NEWBRANCHUNIQ}:${NEWBRANCH}" 265 make-a-pr