Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 129 additions & 0 deletions docs/development/cherry-picks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# How to cherry-pick PRs

This document explains how cherry picks are managed on release branches within
the `volcano-sh/volcano` 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)
- [Tooling](#tooling)
- [GitHub Setup](#github-setup)
- [Authentication](#authentication)
- [Environment Variables](#environment-variables)
- [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)

## Prerequisites

Before you initiate a cherry-pick, you need to ensure your environment is correctly configured.

### Tooling
- **Git**: A working `git` installation.
- **GitHub CLI**: The `gh` command-line tool. You can find in [installation instructions](https://github.com/cli/cli#installation).

### GitHub Setup
- **A merged pull request**: The PR you want to cherry-pick must already be merged into the `master` branch.
- **An existing release branch**: The target branch for the cherry-pick must exist (e.g., [release-1.11](https://github.com/volcano-sh/volcano/tree/release-1.11)).
- **A personal fork**: You need a fork of the `volcano-sh/volcano` repository under your own GitHub account or organization.
- **Configured Git remotes**: You should have remotes configured for both the upstream repository and your fork. You can check with `git remote -v`.

### Authentication

You must be authenticated with GitHub for the script to work. The recommended way is to use the GitHub CLI's built-in authentication.

1. Run the following command in your terminal:
```shell
gh auth login
```
2. Follow the on-screen prompts. The easiest method is usually to log in with a web browser.
3. If you choose to authenticate with a personal access token, ensure it has the `repo` and `read:org` scopes.

### Environment Variables
The script relies on a few environment variables to function correctly:
- `GITHUB_USER`: Your GitHub username. This is mandatory and used to correctly target your fork when creating the cherry-pick pull request.
```shell
export GITHUB_USER="your-github-username"
```
- `UPSTREAM_REMOTE` (optional, default: `upstream`): The name of your git remote that points to the main `volcano-sh/volcano` repository.
- `FORK_REMOTE` (optional, default: `origin`): The name of your git remote that points to your personal fork.

You can set these variables in your shell profile or prefix them to the command when you run it.

## What Kind of PRs are Good for Cherry Picks

Patch releases must be easy and safe to consume, so security fixes
and critical bugfixes can be delivered with minimal risk of regression.

Only the following types of changes are expected to be backported:

- Security fixes
- Dependency updates that just aim to silence some scanners
and do not fix any vulnerable code are **not** eligible for cherry-picks.
- Critical bug fixes (loss of data, memory corruption, panic, crash, hang)
- Test-only changes to stabilize failing / flaky tests on release branches

If you are proposing a cherry pick outside these categories, 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

## Initiate a Cherry Pick

- Run the [cherry pick script](https://github.com/volcano-sh/volcano/blob/master/hack/cherry_pick_pull.sh).

The script requires the target branch and the PRs as arguments. For example, to apply PR #4272 to `release-1.11`:

```shell
hack/cherry_pick_pull.sh upstream/release-1.11 4272
```

If your local git remote for the main repository is `origin` and your fork's remote is `downstream`, you could run:
```shell
UPSTREAM_REMOTE=origin FORK_REMOTE=downstream hack/cherry_pick_pull.sh origin/release-1.11 4272
```
- 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.
77 changes: 37 additions & 40 deletions hack/cherry_pick_pull.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.

# Usage Instructions: https://github.com/volcano-sh/volcano/tree/master/docs/development/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 thems out to a branch named:
Expand All @@ -23,23 +25,25 @@ set -o errexit
set -o nounset
set -o pipefail

declare -r KUBE_ROOT="$(dirname "${BASH_SOURCE}")/.."
cd "${KUBE_ROOT}"
REPO_ROOT="$(git rev-parse --show-toplevel)"
declare -r REPO_ROOT
cd "${REPO_ROOT}"

declare -r STARTINGBRANCH=$(git symbolic-ref --short HEAD)
declare -r REBASEMAGIC="${KUBE_ROOT}/.git/rebase-apply"
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=<your-user> (or GH organization, if that's where your fork lives)"
exit 1
fi

if ! which hub > /dev/null; then
echo "Can't find 'hub' tool in PATH, please install from https://github.com/github/hub"
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

Expand All @@ -55,14 +59,16 @@ if [[ "$#" -lt 2 ]]; then
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 the REGENERATE_DOCS environment var to regenerate documentation for the target branch after picking the specified commits."
echo " This is useful when picking commits containing changes to API documentation."
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/volcano-sh/volcano/tree/master/docs/development/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
Expand All @@ -75,7 +81,7 @@ fi

declare -r BRANCH="$1"
shift 1
declare -r PULLS=( "$@" )
declare -r PULLS=("$@")

function join { local IFS="$1"; shift; echo "$*"; }
declare -r PULLDASH=$(join - "${PULLS[@]/#/#}") # Generates something like "#12345-#56789"
Expand All @@ -85,7 +91,7 @@ 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-0.21."
echo "!!! '${BRANCH}' not found. The second argument should be something like ${UPSTREAM_REMOTE}/release-1.11."
echo " (In particular, it needs to be a valid, existing remote branch that I can 'git checkout'.)"
exit 1
fi
Expand All @@ -96,7 +102,6 @@ declare -r NEWBRANCHUNIQ="${NEWBRANCH}-$(date +%s)"
echo "+++ Creating local branch ${NEWBRANCHUNIQ}"

cleanbranch=""
prtext=""
gitamcleanup=false
function return_to_kansas {
if [[ "${gitamcleanup}" == "true" ]]; then
Expand All @@ -113,34 +118,32 @@ function return_to_kansas {
if [[ -n "${cleanbranch}" ]]; then
git branch -D "${cleanbranch}" >/dev/null 2>&1 || true
fi
if [[ -n "${prtext}" ]]; then
rm "${prtext}"
fi
fi
}
trap return_to_kansas EXIT

SUBJECTS=()
RELEASE_NOTES=()
function make-a-pr() {
local rel="$(basename "${BRANCH}")"
echo
echo "+++ Creating a pull request on GitHub at ${GITHUB_USER}:${NEWBRANCH}"

# This looks like an unnecessary use of a tmpfile, but it avoids
# https://github.com/github/hub/issues/976 Otherwise stdin is stolen
# when we shove the heredoc at hub directly, tickling the ioctl
# crash.
prtext="$(mktemp -t prtext.XXXX)" # cleaned in return_to_kansas
local numandtitle=$(printf '%s\n' "${SUBJECTS[@]}")
cat >"${prtext}" <<EOF
Automated cherry pick of ${numandtitle}

local numandtitle
numandtitle=$(printf '%s\n' "${SUBJECTS[@]}")
prtext=$(
cat <<EOF
Cherry pick of ${PULLSUBJ} on ${rel}.

${numandtitle}
For details on the cherry pick process, see the [cherry picks](https://github.com/volcano-sh/volcano/tree/master/docs/development/cherry-picks.md) page.
\`\`\`release-note
$(printf '%s\n' "${RELEASE_NOTES[@]}")
\`\`\`
EOF
)

hub pull-request -F "${prtext}" -h "${GITHUB_USER}:${NEWBRANCH}" -b "volcano-sh/volcano:${rel}"
gh pr create --title="Automated cherry pick of ${numandtitle}" --body="${prtext}" --head "${GITHUB_USER}:${NEWBRANCH}" --base "${rel}" --repo="${MAIN_REPO_ORG}/${MAIN_REPO_NAME}"
}

git checkout -b "${NEWBRANCHUNIQ}" "${BRANCH}"
Expand All @@ -149,7 +152,8 @@ cleanbranch="${NEWBRANCHUNIQ}"
gitamcleanup=true
for pull in "${PULLS[@]}"; do
echo "+++ Downloading patch to /tmp/${pull}.patch (in case you need to do this again)"
curl -o "/tmp/${pull}.patch" -sSL "https://github.com/volcano-sh/volcano/pull/${pull}.patch"

curl -o "/tmp/${pull}.patch" -sSL "https://github.com/${MAIN_REPO_ORG}/${MAIN_REPO_NAME}/pull/${pull}.patch"
echo
echo "+++ About to attempt cherry pick of PR. To reattempt:"
echo " $ git am -3 /tmp/${pull}.patch"
Expand Down Expand Up @@ -180,25 +184,18 @@ for pull in "${PULLS[@]}"; do
}

# set the subject
subject=$(grep -m 1 "^Subject" "/tmp/${pull}.patch" | sed -e 's/Subject: \[PATCH//g' | sed 's/.*] //')
subject=$(gh pr view "$pull" --json title --jq '.["title"]')
SUBJECTS+=("#${pull}: ${subject}")

# set the release note
release_note=$(gh pr view "$pull" --json body --jq '.["body"]' | awk '/```release-note/{f=1;next} /```/{f=0} f')
RELEASE_NOTES+=("${release_note}")

# remove the patch file from /tmp
rm -f "/tmp/${pull}.patch"
done
gitamcleanup=false

# Re-generate docs (if needed)
if [[ -n "${REGENERATE_DOCS}" ]]; then
echo
echo "Regenerating docs..."
if ! hack/generate-docs.sh; then
echo
echo "hack/generate-docs.sh FAILED to complete."
exit 1
fi
fi

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:"
Expand All @@ -211,8 +208,8 @@ if [[ -n "${DRY_RUN}" ]]; then
exit 0
fi

if git remote -v | grep ^${FORK_REMOTE} | grep volcano-sh/volcano.git; then
echo "!!! You have ${FORK_REMOTE} configured as your volcano-sh/volcano.git"
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:"
Expand Down
Loading