2020-03-25 15:51:55 +00:00
#!/usr/bin/env bash
#
2020-03-06 22:59:14 +00:00
# This script is meant to run on every new commit to master in CircleCI. If the commit comes from a PR, it will
# check the PR associated with the commit for labels. If the label matches `docs*` it will be cherry-picked
# to stable-website. If the label matches `backport/*`, it will be cherry-picked to the appropriate `release/*`
# branch.
# Requires $CIRCLE_PROJECT_USERNAME, $CIRCLE_PROJECT_REPONAME, and $CIRCLE_SHA1 from CircleCI
2020-03-25 15:51:55 +00:00
2020-06-23 22:37:20 +00:00
set -o pipefail
2020-03-06 22:59:14 +00:00
2020-03-10 15:47:23 +00:00
# colorized status prompt
function status {
tput setaf 4
echo " $@ "
tput sgr0
}
2020-09-21 21:31:21 +00:00
# Returns the latest GitHub "backport/*" label
function get_latest_backport_label {
local resp
local ret
local latest_backport_label
resp = $( curl -f -s -H " Authorization: token ${ GITHUB_TOKEN } " " https://api.github.com/repos/ ${ CIRCLE_PROJECT_USERNAME } / ${ CIRCLE_PROJECT_REPONAME } /labels " )
ret = " $? "
if [ [ " $ret " -ne 0 ] ] ; then
status " The GitHub API returned $ret which means it was probably rate limited. "
exit $ret
fi
latest_backport_label = $( echo " $resp " | jq -r '.[] | select(.name | startswith("backport/")) | .name' | sort -rV | head -n1)
2020-09-23 13:59:17 +00:00
echo " $latest_backport_label "
2020-09-21 21:31:21 +00:00
return 0
}
2020-03-10 15:47:23 +00:00
# This function will do the cherry-picking of a commit on a branch
# Exit 1 if cherry-picking fails
function cherry_pick_with_slack_notification {
# Arguments:
# $1 - branch to cherry-pick to
# $2 - commit to cherry-pick
# $3 - url to PR of commit
2020-06-16 18:18:53 +00:00
#
# Return:
# 0 for success
# 1 for error
2020-03-10 15:47:23 +00:00
2020-03-25 15:51:55 +00:00
local branch = " $1 "
local commit = " $2 "
local pr_url = " $3 "
2020-03-10 15:47:23 +00:00
2020-03-25 15:51:55 +00:00
git checkout " $branch " || exit 1
2021-01-30 00:42:14 +00:00
# If git cherry-pick fails or it fails to push, we send a failure notification
if ! ( git cherry-pick --mainline 1 " $commit " && git push origin " $branch " ) ; then
2020-03-10 15:47:23 +00:00
status " 🍒❌ Cherry pick of commit ${ commit : 0 : 7 } from $pr_url onto $branch failed! "
2020-06-16 18:18:53 +00:00
# send slack notification
2020-03-10 15:47:23 +00:00
curl -X POST -H 'Content-type: application/json' \
--data \
" { \
\" attachments\" : [ \
{ \
\" fallback\" : \" Cherry pick failed!\" , \
\" text\" : \" 🍒❌ Cherry picking of <$pr_url | ${ commit : 0 : 7 } > to \` $branch \` failed!\n \n Build Log: ${ CIRCLE_BUILD_URL } \" , \
\" footer\" : \" ${ CIRCLE_PROJECT_USERNAME } /${ CIRCLE_PROJECT_REPONAME } \" , \
\" ts\" : \" $( date +%s) \" , \
\" color\" : \" danger\" \
} \
] \
2020-03-25 15:51:55 +00:00
} " " ${ CONSUL_SLACK_WEBHOOK_URL } "
2020-06-16 18:18:53 +00:00
# post PR comment to GitHub
github_message = " :cherries::x: Cherry pick of commit ${ commit } onto \` $branch \` failed! [Build Log]( $CIRCLE_BUILD_URL ) "
pr_id = $( basename ${ pr_url } )
curl -f -s -H " Authorization: token ${ GITHUB_TOKEN } " \
-X POST \
-d " { \"body\": \" ${ github_message } \"} " \
" https://api.github.com/repos/ ${ CIRCLE_PROJECT_USERNAME } / ${ CIRCLE_PROJECT_REPONAME } /issues/ ${ pr_id } /comments "
# run git status to leave error in CircleCI log
2020-03-10 15:47:23 +00:00
git status
2020-06-16 18:18:53 +00:00
return 1
2020-03-10 15:47:23 +00:00
# Else we send a success notification
else
2020-06-16 18:18:53 +00:00
status " 🍒✅ Cherry picking of PR commit ${ commit : 0 : 7 } from ${ pr_url } succeeded! "
2020-03-10 15:47:23 +00:00
curl -X POST -H 'Content-type: application/json' \
--data \
" { \
\" attachments\" : [ \
{ \
\" fallback\" : \" Cherry pick succeeded!\" , \
2020-06-16 18:18:53 +00:00
\" text\" : \" 🍒✅ Cherry picking of <$pr_url | ${ commit : 0 : 7 } > to \` $branch \` succeeded!\" , \
2020-03-10 15:47:23 +00:00
\" footer\" : \" ${ CIRCLE_PROJECT_USERNAME } /${ CIRCLE_PROJECT_REPONAME } \" , \
\" ts\" : \" $( date +%s) \" , \
\" color\" : \" good\" \
} \
] \
2020-03-25 15:51:55 +00:00
} " " ${ CONSUL_SLACK_WEBHOOK_URL } "
2020-06-16 18:18:53 +00:00
# post PR comment to GitHub
github_message = " :cherries::white_check_mark: Cherry pick of commit ${ commit } onto \` $branch \` succeeded! "
pr_id = $( basename ${ pr_url } )
curl -f -s -H " Authorization: token ${ GITHUB_TOKEN } " \
-X POST \
-d " { \"body\": \" ${ github_message } \"} " \
" https://api.github.com/repos/ ${ CIRCLE_PROJECT_USERNAME } / ${ CIRCLE_PROJECT_REPONAME } /issues/ ${ pr_id } /comments "
2020-03-10 15:47:23 +00:00
fi
2020-06-16 18:18:53 +00:00
return 0
2020-03-10 15:47:23 +00:00
}
2020-03-06 22:59:14 +00:00
# search for the PR labels applicable to the specified commit
2020-06-16 18:18:53 +00:00
resp = $( curl -f -s -H " Authorization: token ${ GITHUB_TOKEN } " " https://api.github.com/search/issues?q=repo: ${ CIRCLE_PROJECT_USERNAME } / ${ CIRCLE_PROJECT_REPONAME } +sha: ${ CIRCLE_SHA1 } " )
2020-03-10 15:47:23 +00:00
ret = " $? "
if [ [ " $ret " -ne 0 ] ] ; then
status " The GitHub API returned $ret which means it was probably rate limited. "
exit $ret
2020-03-06 22:59:14 +00:00
fi
# get the count from the GitHub API to check if the commit matched a PR
count = $( echo " $resp " | jq '.total_count' )
if [ [ " $count " -eq 0 ] ] ; then
2020-03-10 15:47:23 +00:00
status "This commit was not associated with a PR"
exit 0
2020-03-06 22:59:14 +00:00
fi
2020-09-21 21:31:21 +00:00
# save PR number
pr_number = $( echo " $resp " | jq '.items[].number' )
2020-11-06 20:02:55 +00:00
# comment on the PR with the build number to make it easy to re-run the job when
# cherry-pick labels are added in the future
2020-11-13 22:14:47 +00:00
github_message = " :cherries: If backport labels were added before merging, cherry-picking will start automatically.\n\nTo retroactively trigger a backport after merging, add backport labels and re-run ${ CIRCLE_BUILD_URL } . "
2020-11-06 20:02:55 +00:00
curl -f -s -H " Authorization: token ${ GITHUB_TOKEN } " \
-X POST \
-d " { \"body\": \" ${ github_message } \"} " \
" https://api.github.com/repos/ ${ CIRCLE_PROJECT_USERNAME } / ${ CIRCLE_PROJECT_REPONAME } /issues/ ${ pr_number } /comments "
2020-03-06 22:59:14 +00:00
# If the API returned a non-zero count, we have found a PR with that commit so we find
# the labels from the PR
2020-06-16 18:18:53 +00:00
2020-09-21 21:31:21 +00:00
# Sorts the labels from a PR via version sort
2020-06-16 18:18:53 +00:00
labels = $( echo " $resp " | jq --raw-output '.items[].labels[] | .name' | sort -rV)
2020-03-10 15:47:23 +00:00
ret = " $? "
2020-03-10 16:31:58 +00:00
pr_url = $( echo " $resp " | jq --raw-output '.items[].pull_request.html_url' )
2020-03-10 15:47:23 +00:00
if [ [ " $ret " -ne 0 ] ] ; then
status " jq exited with $ret when trying to find label names. Are there labels applied to the PR ( $pr_url )? "
# This can be a valid error but usually this means we do not have any labels so it doesn't signal
# cherry-picking is possible. Exit 0 for now unless we run into cases where these failures are important.
exit 0
2020-03-06 22:59:14 +00:00
fi
2020-09-21 21:31:21 +00:00
# Attach label for latest release branch if 'docs-cherrypick' is present. Will noop if already applied.
latest_backport_label = $( get_latest_backport_label)
status " latest backport label is $latest_backport_label "
if echo " $resp " | jq -e '.items[].labels[] | select(.name | contains("docs-cherrypick"))' ; then
labels = $( curl -f -s -H " Authorization: token ${ GITHUB_TOKEN } " -X POST -d " {\"labels\":[\" $latest_backport_label \"]} " " https://api.github.com/repos/ ${ CIRCLE_PROJECT_USERNAME } / ${ CIRCLE_PROJECT_REPONAME } /issues/ ${ pr_number } /labels " | jq --raw-output '.[].name' | sort -rV)
ret = " $? "
if [ [ " $ret " -ne 0 ] ] ; then
status " Error applying $latest_backport_label to $pr_url "
exit $ret
fi
fi
2020-06-16 18:18:53 +00:00
backport_failures = 0
2020-03-06 22:59:14 +00:00
# loop through all labels on the PR
for label in $labels ; do
git config --local user.email "hashicorp-ci@users.noreply.github.com"
git config --local user.name "hashicorp-ci"
2020-03-10 15:47:23 +00:00
status " checking label: $label "
2020-03-12 17:22:51 +00:00
# if the label matches docs-cherrypick, it will attempt to cherry-pick to stable-website
2020-05-14 20:53:51 +00:00
if [ [ $label = ~ docs-cherrypick ] ] ; then
status "backporting to stable-website"
branch = "stable-website"
cherry_pick_with_slack_notification " $branch " " $CIRCLE_SHA1 " " $pr_url "
2020-06-23 22:37:20 +00:00
backport_failures = $(( backport_failures + " $? " ))
2020-03-06 22:59:14 +00:00
# else if the label matches backport/*, it will attempt to cherry-pick to the release branch
2020-05-14 20:53:51 +00:00
elif [ [ $label = ~ backport/* ] ] ; then
2020-03-10 15:47:23 +00:00
status " backporting to $label "
2020-03-06 22:59:14 +00:00
branch = " ${ label /backport/release } .x "
2020-03-25 15:51:55 +00:00
cherry_pick_with_slack_notification " $branch " " $CIRCLE_SHA1 " " $pr_url "
2020-06-23 22:37:20 +00:00
backport_failures = $(( backport_failures + " $? " ))
2020-03-06 22:59:14 +00:00
fi
2021-01-27 20:51:12 +00:00
# reset the working directory for the next label
git reset --hard
2020-03-12 17:22:51 +00:00
done
2020-06-16 18:18:53 +00:00
if [ " $backport_failures " -ne 0 ] ; then
echo " $backport_failures backports failed "
2020-06-23 22:37:20 +00:00
exit 1
2020-09-23 13:59:17 +00:00
fi