From 6538d4dde118181f11b2c9e672675532dae1af69 Mon Sep 17 00:00:00 2001 From: carlory Date: Wed, 5 Jan 2022 17:50:08 +0800 Subject: [PATCH] add script and doc for cherry_pick_pull Signed-off-by: carlory --- docs/userguide/cherry-picks.md | 117 ++++++++++++++++ hack/cherry_pick_pull.sh | 244 +++++++++++++++++++++++++++++++++ 2 files changed, 361 insertions(+) create mode 100644 docs/userguide/cherry-picks.md create mode 100755 hack/cherry_pick_pull.sh diff --git a/docs/userguide/cherry-picks.md b/docs/userguide/cherry-picks.md new file mode 100644 index 000000000..1d2e6ee4c --- /dev/null +++ b/docs/userguide/cherry-picks.md @@ -0,0 +1,117 @@ +# Overview + +This document explains how cherry picks are managed on release branches within +the `karmada-io/karmada` repository. +A common use case for this task is backporting PRs from master to release +branches. + +> This doc is lifted from [Kubernetes cherry-pick](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-release/cherry-picks.md). + +- [Prerequisites](#prerequisites) +- [What Kind of PRs are Good for Cherry Picks](#what-kind-of-prs-are-good-for-cherry-picks) +- [Initiate a Cherry Pick](#initiate-a-cherry-pick) +- [Cherry Pick Review](#cherry-pick-review) +- [Troubleshooting Cherry Picks](#troubleshooting-cherry-picks) +- [Cherry Picks for Unsupported Releases](#cherry-picks-for-unsupported-releases) + +## Prerequisites + +- A pull request merged against the `master` branch. +- The release branch exists (example: [`release-1.0`](https://github.com/karmada-io/karmada/tree/release-1.0)) +- The normal git and GitHub configured shell environment for pushing to your + karmada `origin` fork on GitHub and making a pull request against a + configured remote `upstream` that tracks + `https://github.com/karmada-io/karmada.git`, including `GITHUB_USER`. +- Have GitHub CLI (`gh`) installed following [installation instructions](https://github.com/cli/cli#installation). +- A github personal access token which has permissions "repo" and "read:org". + Permissions are required for [gh auth login](https://cli.github.com/manual/gh_auth_login) + and not used for anything unrelated to cherry-pick creation process + (creating a branch and initiating PR). + +## What Kind of PRs are Good for Cherry Picks + +Compared to the normal master branch's merge volume across time, +the release branches see one or two orders of magnitude less PRs. +This is because there is an order or two of magnitude higher scrutiny. +Again, the emphasis is on critical bug fixes, e.g., + +- Loss of data +- Memory corruption +- Panic, crash, hang +- Security + +A bugfix for a functional issue (not a data loss or security issue) that only +affects an alpha feature does not qualify as a critical bug fix. + +If you are proposing a cherry pick and it is not a clear and obvious critical +bug fix, please reconsider. If upon reflection you wish to continue, bolster +your case by supplementing your PR with e.g., + +- A GitHub issue detailing the problem + +- Scope of the change + +- Risks of adding a change + +- Risks of associated regression + +- Testing performed, test cases added + +- Key stakeholder reviewers/approvers attesting to their confidence in the + change being a required backport + +It is critical that our full community is actively engaged on enhancements in +the project. If a released feature was not enabled on a particular provider's +platform, this is a community miss that needs to be resolved in the `master` +branch for subsequent releases. Such enabling will not be backported to the +patch release branches. + +## Initiate a Cherry Pick + +- Run the [cherry pick script][cherry-pick-script] + + This example applies a master branch PR #1206 to the remote branch + `upstream/release-1.0`: + + ```shell + hack/cherry_pick_pull.sh upstream/release-1.0 1206 + ``` + + - Be aware the cherry pick script assumes you have a git remote called + `upstream` that points at the Karmada github org. + + - You will need to run the cherry pick script separately for each patch + release you want to cherry pick to. Cherry picks should be applied to all + active release branches where the fix is applicable. + + - If `GITHUB_TOKEN` is not set you will be asked for your github password: + provide the github [personal access token](https://github.com/settings/tokens) rather than your actual github + password. If you can securely set the environment variable `GITHUB_TOKEN` + to your personal access token then you can avoid an interactive prompt. + Refer [https://github.com/github/hub/issues/2655#issuecomment-735836048](https://github.com/github/hub/issues/2655#issuecomment-735836048) + +## Cherry Pick Review + +As with any other PR, code OWNERS review (`/lgtm`) and approve (`/approve`) on +cherry pick PRs as they deem appropriate. + +The same release note requirements apply as normal pull requests, except the +release note stanza will auto-populate from the master branch pull request from +which the cherry pick originated. + +## Troubleshooting Cherry Picks + +Contributors may encounter some of the following difficulties when initiating a +cherry pick. + +- A cherry pick PR does not apply cleanly against an old release branch. In + that case, you will need to manually fix conflicts. + +- The cherry pick PR includes code that does not pass CI tests. In such a case + you will have to fetch the auto-generated branch from your fork, amend the + problematic commit and force push to the auto-generated branch. + Alternatively, you can create a new PR, which is noisier. + +## Cherry Picks for Unsupported Releases + +The community supports & patches releases need to be discussed. diff --git a/hack/cherry_pick_pull.sh b/hack/cherry_pick_pull.sh new file mode 100755 index 000000000..e9c8a2c64 --- /dev/null +++ b/hack/cherry_pick_pull.sh @@ -0,0 +1,244 @@ +#!/usr/bin/env bash + +# Copyright 2015 The Kubernetes 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. + +# Usage Instructions: https://github.com/karmada-io/karmada/blob/master/docs/userguide/cherry-picks.md + +# Checkout a PR from GitHub. (Yes, this is sitting in a Git tree. How +# meta.) Assumes you care about pulls from remote "upstream" and +# checks them out to a branch named: +# automated-cherry-pick-of--- + +set -o errexit +set -o nounset +set -o pipefail + +REPO_ROOT="$(git rev-parse --show-toplevel)" +declare -r REPO_ROOT +cd "${REPO_ROOT}" + +STARTINGBRANCH=$(git symbolic-ref --short HEAD) +declare -r STARTINGBRANCH +declare -r REBASEMAGIC="${REPO_ROOT}/.git/rebase-apply" +DRY_RUN=${DRY_RUN:-""} +REGENERATE_DOCS=${REGENERATE_DOCS:-""} +UPSTREAM_REMOTE=${UPSTREAM_REMOTE:-upstream} +FORK_REMOTE=${FORK_REMOTE:-origin} +MAIN_REPO_ORG=${MAIN_REPO_ORG:-$(git remote get-url "$UPSTREAM_REMOTE" | awk '{gsub(/http[s]:\/\/|git@/,"")}1' | awk -F'[@:./]' 'NR==1{print $3}')} +MAIN_REPO_NAME=${MAIN_REPO_NAME:-$(git remote get-url "$UPSTREAM_REMOTE" | awk '{gsub(/http[s]:\/\/|git@/,"")}1' | awk -F'[@:./]' 'NR==1{print $4}')} + +if [[ -z ${GITHUB_USER:-} ]]; then + echo "Please export GITHUB_USER= (or GH organization, if that's where your fork lives)" + exit 1 +fi + +if ! command -v gh >/dev/null; then + echo "Can't find 'gh' tool in PATH, please install from https://github.com/cli/cli" + exit 1 +fi + +if [[ "$#" -lt 2 ]]; then + echo "${0} ...: cherry pick one or more onto and leave instructions for proposing pull request" + echo + echo " Checks out and handles the cherry-pick of (possibly multiple) for you." + echo " Examples:" + echo " $0 upstream/release-3.14 12345 # Cherry-picks PR 12345 onto upstream/release-3.14 and proposes that as a PR." + echo " $0 upstream/release-3.14 12345 56789 # Cherry-picks PR 12345, then 56789 and proposes the combination as a single PR." + echo + echo " Set the DRY_RUN environment var to skip git push and creating PR." + echo " This is useful for creating patches to a release branch without making a PR." + echo " When DRY_RUN is set the script will leave you in a branch containing the commits you cherry-picked." + echo + echo " Set UPSTREAM_REMOTE (default: upstream) and FORK_REMOTE (default: origin)" + echo " to override the default remote names to what you have locally." + echo + echo " For merge process info, see https://github.com/karmada-io/karmada/blob/master/docs/userguide/cherry-picks.md" + exit 2 +fi + +# Checks if you are logged in. Will error/bail if you are not. +gh auth status + +if git_status=$(git status --porcelain --untracked=no 2>/dev/null) && [[ -n "${git_status}" ]]; then + echo "!!! Dirty tree. Clean up and try again." + exit 1 +fi + +if [[ -e "${REBASEMAGIC}" ]]; then + echo "!!! 'git rebase' or 'git am' in progress. Clean up and try again." + exit 1 +fi + +declare -r BRANCH="$1" +shift 1 +declare -r PULLS=("$@") + +function join { + local IFS="$1" + shift + echo "$*" +} +PULLDASH=$(join - "${PULLS[@]/#/#}") # Generates something like "#12345-#56789" +declare -r PULLDASH +PULLSUBJ=$(join " " "${PULLS[@]/#/#}") # Generates something like "#12345 #56789" +declare -r PULLSUBJ + +echo "+++ Updating remotes..." +git remote update "${UPSTREAM_REMOTE}" "${FORK_REMOTE}" + +if ! git log -n1 --format=%H "${BRANCH}" >/dev/null 2>&1; then + echo "!!! '${BRANCH}' not found. The second argument should be something like ${UPSTREAM_REMOTE}/release-1.0." + echo " (In particular, it needs to be a valid, existing remote branch that I can 'git checkout'.)" + exit 1 +fi + +NEWBRANCHREQ="automated-cherry-pick-of-${PULLDASH}" # "Required" portion for tools. +declare -r NEWBRANCHREQ +NEWBRANCH="$(echo "${NEWBRANCHREQ}-${BRANCH}" | sed 's/\//-/g')" +declare -r NEWBRANCH +NEWBRANCHUNIQ="${NEWBRANCH}-$(date +%s)" +declare -r NEWBRANCHUNIQ +echo "+++ Creating local branch ${NEWBRANCHUNIQ}" + +cleanbranch="" +gitamcleanup=false +function return_to_kansas { + if [[ "${gitamcleanup}" == "true" ]]; then + echo + echo "+++ Aborting in-progress git am." + git am --abort >/dev/null 2>&1 || true + fi + + # return to the starting branch and delete the PR text file + if [[ -z "${DRY_RUN}" ]]; then + echo + echo "+++ Returning you to the ${STARTINGBRANCH} branch and cleaning up." + git checkout -f "${STARTINGBRANCH}" >/dev/null 2>&1 || true + if [[ -n "${cleanbranch}" ]]; then + git branch -D "${cleanbranch}" >/dev/null 2>&1 || true + fi + fi +} +trap return_to_kansas EXIT + +SUBJECTS=() +function make-a-pr() { + local rel + rel="$(basename "${BRANCH}")" + echo + echo "+++ Creating a pull request on GitHub at ${GITHUB_USER}:${NEWBRANCH}" + + local numandtitle + numandtitle=$(printf '%s\n' "${SUBJECTS[@]}") + prtext=$( + cat <&2 + exit 1 + fi + done + + if [[ "${conflicts}" != "true" ]]; then + echo "!!! git am failed, likely because of an in-progress 'git am' or 'git rebase'" + exit 1 + fi + } + + # set the subject + subject=$(grep -m 1 "^Subject" "/tmp/${pull}.patch" | sed -e 's/Subject: \[PATCH//g' | sed 's/.*] //') + SUBJECTS+=("#${pull}: ${subject}") + + # remove the patch file from /tmp + rm -f "/tmp/${pull}.patch" +done +gitamcleanup=false + +if [[ -n "${DRY_RUN}" ]]; then + echo "!!! Skipping git push and PR creation because you set DRY_RUN." + echo "To return to the branch you were in when you invoked this script:" + echo + echo " git checkout ${STARTINGBRANCH}" + echo + echo "To delete this branch:" + echo + echo " git branch -D ${NEWBRANCHUNIQ}" + exit 0 +fi + +if git remote -v | grep ^"${FORK_REMOTE}" | grep "${MAIN_REPO_ORG}/${MAIN_REPO_NAME}.git"; then + echo "!!! You have ${FORK_REMOTE} configured as your ${MAIN_REPO_ORG}/${MAIN_REPO_NAME}.git" + echo "This isn't normal. Leaving you with push instructions:" + echo + echo "+++ First manually push the branch this script created:" + echo + echo " git push REMOTE ${NEWBRANCHUNIQ}:${NEWBRANCH}" + echo + echo "where REMOTE is your personal fork (maybe ${UPSTREAM_REMOTE}? Consider swapping those.)." + echo "OR consider setting UPSTREAM_REMOTE and FORK_REMOTE to different values." + echo + make-a-pr + cleanbranch="" + exit 0 +fi + +echo +echo "+++ I'm about to do the following to push to GitHub (and I'm assuming ${FORK_REMOTE} is your personal fork):" +echo +echo " git push ${FORK_REMOTE} ${NEWBRANCHUNIQ}:${NEWBRANCH}" +echo +read -p "+++ Proceed (anything but 'y' aborts the cherry-pick)? [y/n] " -r +if ! [[ "${REPLY}" =~ ^[yY]$ ]]; then + echo "Aborting." >&2 + exit 1 +fi + +git push "${FORK_REMOTE}" -f "${NEWBRANCHUNIQ}:${NEWBRANCH}" +make-a-pr