Merge pull request #30106 from smithfarm/wip-more-cbs

script/ceph-backport.sh: make the script idempotent

Reviewed-by: Jan Fajerski <jfajerski@suse.com>
Reviewed-by: Abhishek Lekshmanan <abhishek@suse.com>
This commit is contained in:
Nathan Cutler 2019-09-12 12:04:25 +02:00 committed by GitHub
commit 193ef136a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -4,135 +4,214 @@
#
# Credits: This script is based on work done by Loic Dachary
#
# With proper setup, this script takes care of opening the backport PR,
# updating the corresponding backport tracker issue, and cross-linking the
# backport PR with the tracker issue.
#
# However, before you start, some setup is required. Please be patient and
# read the "Setup instructions" and "Instructions for use" sections, below,
# carefully. If you still have issues, you could read the "Troubleshooting
# notes" section as well.
# This script automates the process of staging a backport starting from a
# Backport tracker issue.
#
# Setup, usage and troubleshooting:
#
# ceph-backport.sh --help
# ceph-backport.sh --setup-advice
# ceph-backport.sh --usage-advice
# ceph-backport.sh --troubleshooting-advice
#
#
# Setup instructions
# ------------------
#
# It is strongly suggested to copy the latest version of the script (from
# the "master" branch) into your PATH. In particular, do not use any version
# of the script from a stable (named) branch such as "nautilus", as these
# versions are not maintained. Only the version in master is maintained.
#
# You will need to find the right values for the following:
#
# redmine_key # "My account" -> "API access key" -> "Show"
# redmine_user_id # "Logged in as foobar", click on foobar link, Redmine User ID
# is in the URL, i.e. https://tracker.ceph.com/users/[redmine_user_id]
# github_token # https://github.com/settings/tokens -> Generate new token ->
# ensure it has "Full control of private repositories" scope
# github_user # Your github username
#
# Once you have the actual values for the above variables, create a file
# $HOME/bin/backport_common.sh with the following contents
#
# redmine_key=[your_redmine_key]
# redmine_user_id=[your_redmine_user_id]
# github_token=[your_github_personal_access_token]
# github_user=[your_github_username]
#
# You can also optionally add yours and ceph remote repo's name in this file,
# like
#
# github_repo=[your_github_repo_name]
# ceph_repo=[ceph_github_repo_name]
#
# If you don't add it, it will default to "origin" and "upstream", respectively.
#
# Obviously, since this file contains secrets, you should protect it from
# exposure using all available means (restricted file privileges, encrypted
# filesystem, etc.). Without correct values for these four variables, this
# script will not work!
#
#
# Instructions for use
# --------------------
#
# Assumes you have forked ceph/ceph.git, cloned your fork, and are running the
# script in the local clone!
#
# With this script, backporting workflow for backport issue
# https://tracker.ceph.com/issues/19206 (a jewel backport)
# becomes something like this:
#
# For simple backports you can just run:
#
# ceph-backport.sh 19206 --prepare
# ceph-backport.sh 19206
#
# Alternatively, instead of running the script with --prepare you can prepare
# the backport manually:
#
# git remote add ceph http://github.com/ceph/ceph.git
# git fetch ceph
# git checkout -b wip-19206-jewel ceph/jewel
# git cherry-pick -x ...
# ceph-backport.sh 19206
#
# Optionally, you can set the component label that will be added to the PR with
# an environment variable:
#
# COMPONENT=dashboard ceph-backport.sh 40056
#
#
# Troubleshooting notes
# ---------------------
#
# If the script inexplicably fails with:
#
# error: a cherry-pick or revert is already in progress
# hint: try "git cherry-pick (--continue | --quit | --abort)"
# fatal: cherry-pick failed
#
# This is because HEAD is not where git expects it to be:
#
# $ git cherry-pick --abort
# warning: You seem to have moved HEAD. Not rewinding, check your HEAD!
#
# This can be fixed by issuing the command:
#
# $ git cherry-pick --quit
#
#
# Reporting bugs
# --------------
#
# Please report any bugs in this script to https://tracker.ceph.com/projects/ceph/issues/new
#
# (Ideally, the bug report would include a typescript obtained while
# reproducing the bug with the --debug option. To understand what is meant by
# "typescript", see "man script".)
#
#
# Other backporting resources
# ---------------------------
#
# See https://tracker.ceph.com/projects/ceph-releases/wiki/HOWTO_backport_commits
# for more info on cherry-picking.
#
# Happy backporting!
# Nathan
#
source $HOME/bin/backport_common.sh
this_script=$(basename "$0")
verbose=
how_to_get_setup_advice="For setup advice, run: ${this_script} --setup-advice"
if [[ $* == *--debug* ]]; then
set -x
verbose="1"
fi
if [[ $* == *--verbose* ]]; then
verbose="1"
fi
function cherry_pick_phase {
local offset=0
populate_original_issue
if [ -z "$original_issue" ] ; then
error "Could not find original issue"
info "Does ${redmine_url} have a \"Copied from\" relation?"
false
fi
info "Parent issue: ${redmine_endpoint}/issues/${original_issue}"
populate_original_pr
if [ -z "$original_pr" ]; then
error "Could not find original PR"
info "Is the \"Pull request ID\" field of ${redmine_endpoint}/issues/${original_issue} populated?"
false
fi
info "Parent issue ostensibly fixed by: ${original_pr_url}"
debug "Counting commits in ${original_pr_url}"
number=$(curl --silent https://api.github.com/repos/ceph/ceph/pulls/${original_pr}?access_token=${github_token} | jq .commits)
if [ -z "$number" ] ; then
error "Could not determine the number of commits in ${original_pr_url}"
return 1
fi
info "Found $number commits in ${original_pr_url}"
debug "Fetching latest commits from $upstream_remote"
git fetch $upstream_remote
debug "Initializing local branch $local_branch to $milestone"
if git show-ref --verify --quiet refs/heads/$local_branch ; then
error "Cannot initialize $local_branch - local branch already exists"
false
else
git checkout $upstream_remote/$milestone -b $local_branch
fi
debug "Fetching latest commits from ${original_pr_url}"
git fetch $upstream_remote pull/$original_pr/head:pr-$original_pr
info "Attempting to cherry pick $number commits from ${original_pr_url} into local branch $local_branch"
let offset=$number-1 || true # don't fail on set -e when result is 0
for ((i=$offset; i>=0; i--)) ; do
debug "Cherry-picking commit $(git log --oneline --max-count=1 --no-decorate pr-$original_pr~$i)"
if git cherry-pick -x "pr-$original_pr~$i" ; then
true
else
if [ "$VERBOSE" ] ; then
git status
fi
error "Cherry pick failed"
info "Next, manually fix conflicts and complete the current cherry-pick"
if [ "$i" -gt "0" ] >/dev/null 2>&1 ; then
info "Then, cherry-pick the remaining commits from ${original_pr_url}, i.e.:"
for ((j=$i-1; j>=0; j--)) ; do
info "-> missing commit: $(git log --oneline --max-count=1 --no-decorate pr-$original_pr~$j)"
done
info "Finally, re-run the script"
else
info "Then re-run the script"
fi
false
fi
done
info "Cherry picking completed without conflicts"
}
function debug {
log debug "$@"
}
function deduce_remote {
local remote_type="$1"
local remote=""
local url_component=""
if [ "$remote_type" = "upstream" ] ; then
url_component="ceph"
elif [ "$remote_type" = "fork" ] ; then
url_component="$github_user"
else
error "Internal error in deduce_remote"
exit 1
fi
remote=$(git remote -v | egrep --ignore-case '(://|@)github.com(/|:)'$url_component'/ceph(\s|\.|\/)' | head -n1 | cut -f 1)
if [ "$remote" ] ; then
true
else
error "Cannot auto-determine ${remote_type}_remote"
info "Please set this variable explicitly and also file a bug report at ${redmine_endpoint}"
exit 1
fi
echo "$remote"
}
function eol {
log mtt=$1
error "$mtt is EOL"
false
}
function error {
log error "$@"
}
function failed_mandatory_var_check {
local varname="$1"
error "$varname not defined"
setup_ok=""
}
function info {
log info "$@"
}
function init_github_user {
debug "Initializing mandatory variables - GitHub user"
if [ "$github_user" ] ; then
true
else
warning "github_user variable not set, falling back to \$USER"
github_user="$USER"
if [ "$github_user" ] ; then
true
else
failed_mandatory_var_check github_user
info "$how_to_get_setup_advice"
exit 1
fi
fi
}
function init_mandatory_vars {
debug "Initializing mandatory variables - endpoints"
redmine_endpoint="${redmine_endpoint:-"https://tracker.ceph.com"}"
github_endpoint="${github_endpoint:-"https://github.com/ceph/ceph"}"
debug "Initializing mandatory variables - GitHub remotes"
upstream_remote="${upstream_remote:-$(deduce_remote upstream)}"
fork_remote="${fork_remote:-$(deduce_remote fork)}"
}
function setup_summary {
local not_set="!!! NOT SET !!!"
local redmine_user_id_display="$not_set"
local redmine_key_display="$not_set"
local github_user_display="$not_set"
local github_token_display="$not_set"
local upstream_remote_display="$not_set"
local fork_remote_display="$not_set"
[ "$redmine_user_id" ] && redmine_user_id_display="$redmine_user_id"
[ "$redmine_key" ] && redmine_key_display="(OK, not shown)"
[ "$github_user" ] && github_user_display="$github_user"
[ "$github_token" ] && github_token_display="(OK, not shown)"
[ "$upstream_remote" ] && upstream_remote_display="$upstream_remote"
[ "$fork_remote" ] && fork_remote_display="$fork_remote"
debug Re-checking mandatory variables
test "$redmine_key" || failed_mandatory_var_check redmine_key
test "$redmine_user_id" || failed_mandatory_var_check redmine_user_id
test "$github_user" || failed_mandatory_var_check github_user
test "$github_token" || failed_mandatory_var_check github_token
test "$upstream_remote" || failed_mandatory_var_check upstream_remote
test "$fork_remote" || failed_mandatory_var_check fork_remote
test "$redmine_endpoint" || failed_mandatory_var_check redmine_endpoint
test "$github_endpoint" || failed_mandatory_var_check github_endpoint
if [ "$SETUP_ONLY" ] ; then
read -r -d '' setup_summary <<EOM || true > /dev/null 2>&1
redmine_user_id $redmine_user_id_display
redmine_key $redmine_key_display
github_user $github_user_display
github_token $github_token_display
upstream_remote $upstream_remote_display
fork_remote $fork_remote_display
EOM
log bare "================================"
log bare "Setup summary"
log bare "--------------------------------"
log bare "variable name value"
log bare "--------------------------------"
log bare "$setup_summary"
log bare "================================"
elif [ "$VERBOSE" ] ; then
debug "redmine_user_id $redmine_user_id_display"
debug "redmine_key $redmine_key_display"
debug "github_user $github_user_display"
debug "github_token $github_token_display"
debug "upstream_remote $upstream_remote_display"
debug "fork_remote $fork_remote_display"
fi
}
function log {
local level="$1"
@ -158,61 +237,33 @@ function log {
verbose_only="1"
;;
esac
if [ "$verbose_only" -a ! "$verbose" ] ; then
:
if [ "$verbose_only" -a -z "$VERBOSE" ] ; then
true
else
msg="${prefix}${msg}"
echo "$msg" >&2
fi
}
function error {
log error $@
}
function warning {
log warning $@
}
function info {
log info $@
}
function debug {
log debug $@
}
function failed_required_variable_check {
local varname="$1"
error "$varname not defined. Did you create $HOME/bin/backport_common.sh?"
info "(For detailed instructions, see comment block at the beginning of the script)"
exit 1
}
debug Checking mandatory variables
test "$redmine_key" || failed_required_variable_check redmine_key
test "$redmine_user_id" || failed_required_variable_check redmine_user_id
test "$github_token" || failed_required_variable_check github_token
test "$github_user" || failed_required_variable_check github_user
: "${github_repo:=origin}"
: "${ceph_repo:=upstream}"
redmine_endpoint="https://tracker.ceph.com"
github_endpoint="https://github.com/ceph/ceph"
original_issue=
original_pr=
function usage {
log bare
log bare "Usage:"
log bare " ${this_script} BACKPORT_TRACKER_ISSUE_NUMBER [--debug] [--prepare] [--verbose]"
log bare
log bare "Example:"
log bare " ${this_script} 19206 --prepare"
log bare
log bare "The script must be run from inside a local git clone."
log bare
log bare "Documentation can be found in the comment block at the top of the script itself."
exit 1
function milestone_number_from_remote_api {
local mtt=$1 # milestone to try
local mn="" # milestone number
warning "Milestone ->$mtt<- unknown to script - falling back to GitHub API"
remote_api_output=$(curl -s -X GET 'https://api.github.com/repos/ceph/ceph/milestones?access_token='$github_token)
mn=$(echo $remote_api_output | jq --arg milestone $mtt '.[] | select(.title==$milestone) | .number')
if [ "$mn" -gt "0" ] >/dev/null 2>&1 ; then
echo "$mn"
else
error "Could not determine milestone number of ->$milestone<-"
if [ "$VERBOSE" ] ; then
info "GitHub API said:"
log bare "$remote_api_output"
fi
info "Valid values are $(curl -s -X GET 'https://api.github.com/repos/ceph/ceph/milestones?access_token='$github_token | jq '.[].title')"
info "(This probably means the Release field of ${redmine_url} is populated with"
info "an unexpected value - i.e. it does not match any of the GitHub milestones.)"
false
fi
}
function populate_original_issue {
@ -227,130 +278,377 @@ function populate_original_pr {
if [ -z "$original_pr" ] ; then
original_pr=$(curl --silent ${redmine_endpoint}/issues/${original_issue}.json |
jq -r '.issue.custom_fields[] | select(.id | contains(21)) | .value')
original_pr_url="${github_endpoint}/pull/${original_pr}"
fi
fi
}
function prepare {
local offset=0
populate_original_issue
if [ -z "$original_issue" ] ; then
error "Could not find original issue"
info "Does ${redmine_url} have a \"Copied from\" relation?"
exit 1
fi
info "Parent issue: ${redmine_endpoint}/issues/${original_issue}"
function setup_advice {
cat <<EOM
Setup advice
------------
populate_original_pr
if [ -z "$original_pr" ]; then
error "Could not find original PR"
info "Is the \"Pull request ID\" field of ${redmine_endpoint}/issues/${original_issue} populated?"
exit 1
fi
info "Parent issue ostensibly fixed by: ${github_endpoint}/pull/${original_pr}"
${this_script} expects to be run inside a local clone of the Ceph git repo.
Some initial setup is required for the script to become fully functional.
debug "Counting commits in ${github_endpoint}/pull/${original_pr}"
number=$(curl --silent https://api.github.com/repos/ceph/ceph/pulls/${original_pr}?access_token=${github_token} | jq .commits)
if [ -z "$number" ] ; then
error "Could not determine the number of commits in ${github_endpoint}/pull/${original_pr}"
return 1
fi
info "Found $number commits in ${github_endpoint}/pull/${original_pr}"
First, obtain the correct values for the following variables:
git fetch $ceph_repo
debug "Fetched latest commits from upstream"
redmine_key # "My account" -> "API access key" -> "Show"
redmine_user_id # "Logged in as foobar", click on foobar link, Redmine User ID
# is in the URL, i.e. https://tracker.ceph.com/users/[redmine_user_id]
github_token # https://github.com/settings/tokens -> Generate new token ->
# ensure it has "Full control of private repositories" scope
github_user # Your github username
git checkout $ceph_repo/$milestone -b $local_branch
The above variables must be set explicitly, as the script has no way of
determining reasonable defaults. If you prefer, you can ensure the variables
are set in the environment before running the script. Alternatively, you can
create a file, \$HOME/bin/backport_common.sh (this exact path), with the
variable assignments in it. The script will detect that the file exists and
"source" it.
git fetch $ceph_repo pull/$original_pr/head:pr-$original_pr
In any case, care should be taken to keep the values of redmine_key and
github_token secret.
debug "Cherry picking $number commits from ${github_endpoint}/pull/${original_pr} into local branch $local_branch"
debug "If this operation does not succeed, you will need to resolve the conflicts manually"
let offset=$number-1 || true # don't fail on set -e when result is 0
for ((i=$offset; i>=0; i--))
do
debug "Cherry-picking commit $(git log --oneline --max-count=1 --no-decorate)"
git cherry-pick -x "pr-$original_pr~$i"
done
info "Cherry picked $number commits from ${github_endpoint}/pull/${original_pr} into local branch $local_branch"
The script expects to run in a local clone of a Ceph repo with
at least two remotes defined - pointing to:
exit 0
https://github.com/ceph/ceph.git
https://github.com/\$github_user/ceph.git
In other words, the upstream GitHub repo and the user's fork thereof. It makes
no difference what these remotes are called - the script will determine the
right remote names automatically.
To find out whether you have any obvious problems with your setup before
actually using the script to stage a backport, run:
${this_script} --setup
EOM
}
function troubleshooting_advice {
cat <<EOM
Troubleshooting notes
---------------------
If the script inexplicably fails with:
error: a cherry-pick or revert is already in progress
hint: try "git cherry-pick (--continue | --quit | --abort)"
fatal: cherry-pick failed
This is because HEAD is not where git expects it to be:
$ git cherry-pick --abort
warning: You seem to have moved HEAD. Not rewinding, check your HEAD!
This can be fixed by issuing the command:
$ git cherry-pick --quit
EOM
}
# to update known milestones, consult:
# curl --verbose -X GET https://api.github.com/repos/ceph/ceph/milestones
function try_known_milestones {
local mtt=$1 # milestone to try
local mn="" # milestone number
case $mtt in
cuttlefish) eol "$mtt" ;;
dumpling) eol "$mtt" ;;
emperor) eol "$mtt" ;;
firefly) eol "$mtt" ;;
giant) eol "$mtt" ;;
hammer) eol "$mtt" ;;
infernalis) eol "$mtt" ;;
jewel) mn="8" ;;
kraken) eol "$mtt" ;;
luminous) mn="10" ;;
mimic) mn="11" ;;
nautilus) mn="12" ;;
esac
echo "$mn"
}
function usage {
cat <<EOM >&2
Documentation:
${this_script} --setup-advice | less
${this_script} --usage-advice | less
${this_script} --troubleshooting-advice | less
Usage:
${this_script} [OPTIONS] BACKPORT_TRACKER_ISSUE_NUMBER
Options:
-c/--component COMPONENT (for use with --set-milestone)
--debug (turns on "set -x")
-m/--set-milestone (requires elevated GitHub privs)
-s/--setup (just check the setup)
-v/--verbose
Example:
${this_script} -c dashboard -m 31459
(if cherry-pick conflicts are present, finish cherry-picking phase manually
and run the script again with the same arguments)
CAVEAT: The script must be run from inside a local git clone.
EOM
}
function usage_advice {
cat <<EOM
Usage advice
------------
Once you have completed setup (see --setup-advice), you can run the script
with the ID of a Backport tracker issue. For example, to stage the backport
https://tracker.ceph.com/issues/41502, run:
${this_script} 41502
If the commits in the corresponding master PR cherry-pick cleanly, the script
will automatically perform all steps required to stage the backport:
Cherry-pick phase:
1. fetching the latest commits from the upstream remote
2. creating a wip branch for the backport
3. figuring out which upstream PR contains the commits to cherry-pick
4. cherry-picking the commits
PR phase:
5. pushing the wip branch to your fork
6. opening the backport PR with compliant title and description describing
the backport
7. (optionally) setting the milestone and label in the PR
8. updating the Backport tracker issue
If any of the commits do not cherry-pick cleanly, the script will abort in
step 4. In this case, you can either finish the cherry-picking manually
or abort the cherry-pick. In any case, when and if the local wip branch is
ready (all commits cherry-picked), if you run the script again, like so:
${this_script} 41502
the script will detect that the wip branch already exists and skip over
steps 1-4, starting from step 5 ("PR phase"). In other words, if the wip branch
already exists for any reason, the script will assume that the cherry-pick
phase (steps 1-4) is complete.
As this implies, you can do steps 1-4 manually. Provided the wip branch name
is in the format wip-\$TRACKER_ID-\$STABLE_RELEASE (e.g. "wip-41502-mimic"),
the script will detect the wip branch and start from step 5.
For details on all the options the script takes, run:
${this_script} --help
For more information on Ceph backporting, see:
https://github.com/ceph/ceph/tree/master/SubmittingPatches-backports.rst
EOM
}
function warning {
log warning "$@"
}
#
# are we in a local git clone?
#
if git show-ref HEAD >/dev/null 2>&1 ; then
debug "In a local git clone. Good."
else
error "This script must be run from inside a local git clone"
exit 1
false
fi
if [ $verbose ] ; then
debug "Redmine user: ${redmine_user_id}"
debug "GitHub user: ${github_user}"
debug "Fork remote: ${github_repo}"
git remote -v | egrep ^${github_repo}\\s+
debug "Ceph remote: ${ceph_repo}"
git remote -v | egrep ^${ceph_repo}\\s+
#
# process command-line arguments
#
munged_options=$(getopt -o c:dhmpsv --long "component:,debug,help,prepare,set-milestone,setup,setup-advice,troubleshooting-advice,usage-advice,verbose" -n "$this_script" -- "$@")
eval set -- "$munged_options"
ADVICE=""
DEBUG=""
SET_MILESTONE=""
EXPLICIT_COMPONENT=""
EXPLICIT_PREPARE=""
HELP=""
ISSUE=""
SETUP_ADVICE=""
SETUP_ONLY=""
TROUBLESHOOTING_ADVICE=""
USAGE_ADVICE=""
VERBOSE=""
while true ; do
case "$1" in
--component|-c) shift ; EXPLICIT_COMPONENT="$1" ; shift ;;
--debug|-d) DEBUG="$1" ; shift ;;
--help|-h) ADVICE="1" ; HELP="$1" ; shift ;;
--prepare|-p) EXPLICIT_PREPARE="$1" ; shift ;;
--set-milestone|-m) SET_MILESTONE="$1" ; shift ;;
--setup|-s) SETUP_ONLY="$1" ; shift ;;
--setup-advice) ADVICE="1" ; SETUP_ADVICE="$1" ; shift ;;
--troubleshooting-advice) ADVICE="$1" ; TROUBLESHOOTING_ADVICE="$1" ; shift ;;
--usage-advice) ADVICE="$1" ; USAGE_ADVICE="$1" ; shift ;;
--verbose|-v) VERBOSE="$1" ; shift ;;
--) shift ; ISSUE="$1" ; break ;;
*) echo "Internal error" ; false ;;
esac
done
if [ "$ADVICE" ] ; then
[ "$HELP" ] && usage
[ "$SETUP_ADVICE" ] && setup_advice
[ "$USAGE_ADVICE" ] && usage_advice
[ "$TROUBLESHOOTING_ADVICE" ] && troubleshooting_advice
exit 0
fi
if [[ $1 =~ ^[0-9]+$ ]] ; then
issue=$1
[ "$SETUP_ONLY" ] && ISSUE="0"
if [[ $ISSUE =~ ^[0-9]+$ ]] ; then
issue=$ISSUE
else
error "Invalid or missing argument"
usage # does not return
usage
false
fi
if [ "$DEBUG" ]; then
set -x
VERBOSE="--verbose"
fi
if [ "$VERBOSE" ]; then
info "Verbose mode ON"
VERBOSE="--verbose"
fi
#
# initialize mandatory variables and check values for sanity
#
BACKPORT_COMMON=$HOME/bin/backport_common.sh
[ -f "$BACKPORT_COMMON" ] && source $HOME/bin/backport_common.sh
setup_ok="1"
init_github_user
init_mandatory_vars
setup_summary
if [ "$setup_ok" ] ; then
if [ "$SETUP_ONLY" ] ; then
log bare "Overall setup is OK"
exit 0
elif [ "$VERBOSE" ] ; then
debug "Overall setup is OK"
fi
else
if [ "$SETUP_ONLY" ] ; then
log bare "Setup is NOT OK"
log bare "$how_to_get_setup_advice"
false
else
error "Problem detected in your setup"
info "Run the script with --setup for a full report"
info "$how_to_get_setup_advice"
false
fi
fi
#
# query remote Redmine API for information about the Backport tracker issue
#
redmine_url="${redmine_endpoint}/issues/${issue}"
debug "Considering Redmine issue: $redmine_url - is it in the Backport tracker?"
tracker=$(curl --silent "${redmine_url}.json" | jq -r '.issue.tracker.name')
remote_api_output=$(curl --silent "${redmine_url}.json")
tracker=$(echo $remote_api_output | jq -r '.issue.tracker.name')
if [ "$tracker" = "Backport" ]; then
debug "Yes, $redmine_url is a Backport issue"
else
error "Issue $redmine_url is not a Backport"
info "(This script only works with Backport tracker issues.)"
exit 1
false
fi
debug "Looking up release/milestone of $redmine_url"
milestone=$(curl --silent "${redmine_url}.json" | jq -r '.issue.custom_fields[0].value')
milestone=$(echo $remote_api_output | jq -r '.issue.custom_fields[0].value')
if [ "$milestone" ] ; then
debug "Release/milestone: $milestone"
else
error "could not obtain release/milestone from ${redmine_url}"
exit 1
false
fi
# milestone numbers can be obtained manually with:
# curl --verbose -X GET https://api.github.com/repos/ceph/ceph/milestones
milestone_number=$(curl -s -X GET 'https://api.github.com/repos/ceph/ceph/milestones?access_token='$github_token | jq --arg milestone $milestone '.[] | select(.title==$milestone) | .number')
if test -n "$milestone_number" ; then
tracker_title=$(echo $remote_api_output | jq -r '.issue.subject')
debug "Title of $redmine_url is ->$tracker_title<-"
tracker_assignee_id=$(echo $remote_api_output | jq -r '.issue.assigned_to.id')
tracker_assignee_name=$(echo $remote_api_output | jq -r '.issue.assigned_to.name')
debug "$redmine_url is assigned to $tracker_assignee_name (ID $tracker_assignee_id)"
if [ "$tracker_assignee_id" = "null" -o "$tracker_assignee_id" = "$redmine_user_id" ] ; then
true
else
error "$redmine_url is assigned to $tracker_assignee_name (ID $tracker_assignee_id)"
info "Cowardly refusing to work on an issue that is assigned to someone else"
false
fi
milestone_number=$(try_known_milestones "$milestone")
if [ "$milestone_number" -gt "0" ] >/dev/null 2>&1 ; then
target_branch="$milestone"
else
error "Unsupported milestone"
info "Valid values are $(curl -s -X GET 'https://api.github.com/repos/ceph/ceph/milestones?access_token='$github_token | jq '.[].title')"
info "(This probably means the Release field of ${redmine_url} is populated with"
info "an unexpected value - i.e. it does not match any of the GitHub milestones.)"
exit 1
milestone_number=$(milestone_number_from_remote_api "$milestone")
fi
info "Milestone/release is $milestone"
debug "Milestone number is $milestone_number"
#
# cherry-pick phase
#
local_branch=wip-${issue}-${target_branch}
if [ $(curl --silent ${redmine_url}.json | jq -r .issue.tracker.name) != "Backport" ]
then
error "${redmine_endpoint}/issues/${issue} is not in the Backport tracker"
exit 1
if git show-ref --verify --quiet refs/heads/$local_branch ; then
if [ "$EXPLICIT_PREPARE" ] ; then
error "local branch $local_branch already exists -- cannot --prepare"
false
fi
info "local branch $local_branch already exists: skipping cherry-pick phase"
else
info "$local_branch does not exist: will create it and attempt automated cherry-pick"
cherry_pick_phase
fi
if [[ $* == *--prepare* ]]; then
debug "'--prepare' found, will only prepare the backport"
prepare # does not return
fi
debug "Pushing local branch $local_branch to remote $github_repo"
git push -u $github_repo $local_branch
#
# PR phase
#
current_branch=$(git rev-parse --abbrev-ref HEAD)
[ "$current_branch" = "$local_branch" ] || git checkout $local_branch
debug "Pushing local branch $local_branch to remote $fork_remote"
git push -u $fork_remote $local_branch
original_issue=""
original_pr=""
original_pr_url=""
debug "Generating backport PR description"
populate_original_issue
@ -364,22 +662,46 @@ fi
desc="${desc}\n\nthis backport was staged using ${github_endpoint}/blob/master/src/script/ceph-backport.sh"
debug "Generating backport PR title"
title="${milestone}: $(curl --silent https://api.github.com/repos/ceph/ceph/pulls/${original_pr} | jq -r '.title')"
if [ "$original_pr" ] ; then
title="${milestone}: $(curl --silent https://api.github.com/repos/ceph/ceph/pulls/${original_pr} | jq -r '.title')"
else
if [[ $tracker_title =~ ^${milestone}: ]] ; then
title="${tracker_title}"
else
title="${milestone}: ${tracker_title}"
fi
fi
if [[ $title =~ \" ]] ; then
title=$(echo $title | sed -e 's/"/\\"/g')
fi
debug "Opening backport PR"
number=$(curl --silent --data-binary '{"title":"'"$title"'","head":"'$github_user':'$local_branch'","base":"'$target_branch'","body":"'"${desc}"'"}' 'https://api.github.com/repos/ceph/ceph/pulls?access_token='$github_token | jq -r .number)
component=${COMPONENT:-core}
info "Opened backport PR ${github_endpoint}/pull/$number"
debug "Setting ${component} label"
curl --silent --data-binary '{"milestone":'$milestone_number',"labels":["'$component'"]}' 'https://api.github.com/repos/ceph/ceph/issues/'$number'?access_token='$github_token >/dev/null
info "Set ${component} label in PR"
remote_api_output=$(curl --silent --data-binary '{"title":"'"$title"'","head":"'$github_user':'$local_branch'","base":"'$target_branch'","body":"'"${desc}"'"}' 'https://api.github.com/repos/ceph/ceph/pulls?access_token='$github_token)
number=$(echo "$remote_api_output" | jq -r .number)
if [ "$number" = "null" ] ; then
error "Remote API call failed"
log bare "$remote_api_output"
false
fi
if [ "$SET_MILESTONE" -o "$CEPH_BACKPORT_SET_MILESTONE" ] ; then
if [ "$EXPLICIT_COMPONENT" ] ; then
component="$EXPLICIT_COMPONENT"
else
component=${COMPONENT:-core}
fi
info "Opened backport PR ${github_endpoint}/pull/$number"
debug "Setting ${component} label and ${milestone} milestone"
curl --silent --data-binary '{"milestone":'$milestone_number',"labels":["'$component'"]}' 'https://api.github.com/repos/ceph/ceph/issues/'$number'?access_token='$github_token >/dev/null 2>&1
info "Set component label and milestone in PR"
else
debug "Not setting component and milestone in PR (--set-milestone was not given and CEPH_BACKPORT_SET_MILESTONE not set)"
fi
pgrep firefox >/dev/null && firefox ${github_endpoint}/pull/$number
debug "Updating backport tracker issue in Redmine"
redmine_status=2 # In Progress
curl -X PUT --header 'Content-type: application/json' --data-binary '{"issue":{"description":"https://github.com/ceph/ceph/pull/'$number'","status_id":'$redmine_status',"assigned_to_id":'$redmine_user_id'}}' ${redmine_url}'.json?key='$redmine_key
curl -X PUT --header 'Content-type: application/json' --data-binary '{"issue":{"description":"https://github.com/ceph/ceph/pull/'$number'","status_id":'$redmine_status',"assigned_to_id":'$redmine_user_id'},"notes":"Updated automatically by ceph-backport.sh"}' ${redmine_url}'.json?key='$redmine_key
info "${redmine_url} updated"
pgrep firefox >/dev/null && firefox ${redmine_url}