|
#!/usr/bin/env bash |
|
|
|
## default options |
|
|
|
mode= |
|
|
|
githubApi=https://api.github.com |
|
pivotalApi=https://www.pivotaltracker.com/services/v5 |
|
|
|
projectGithubRepository=$(git config project.github.repository) |
|
projectPivotalId=$(git config project.pivotal.id) |
|
pivotalToken=$(git config pivotal.token) |
|
githubToken=$(git config github.token) |
|
|
|
estimation=1 |
|
|
|
## usage function |
|
|
|
function usage() { |
|
|
|
local warned= |
|
|
|
echo |
|
|
|
if [ "$projectGithubRepository" = "" ]; then |
|
echo "WARNING : Missing 'project.github.repository' git configuration entry" |
|
warned=yes |
|
fi |
|
|
|
if [ "$projectPivotalId" = "" ]; then |
|
echo "WARNING : Missing 'project.pivotal.id' git configuration entry" |
|
warned=yes |
|
fi |
|
|
|
if [ "$pivotalToken" = "" ]; then |
|
echo "WARNING : Missing 'pivotal.token' git configuration entry" |
|
warned=yes |
|
fi |
|
|
|
if [ "$githubToken" = "" ]; then |
|
echo "WARNING : Missing 'github.token' git configuration entry" |
|
warned=yes |
|
fi |
|
|
|
if ! type -t _git_pivotal > /dev/null; then |
|
echo "WARNING : The _git_pivotal completion function has not been found" |
|
warned=yes |
|
fi |
|
|
|
if [ "${warned}" != "" ]; then |
|
echo |
|
fi |
|
|
|
echo "git pivotal --start <branch_01234567>|<01234567>" |
|
echo " Creates a new branch, sets its upstream to the correct remote branch then" |
|
echo " sets the task state to 'started'. If the branch name is omitted, the script" |
|
echo " will use the name of the current branch. If the branch name only contain an" |
|
echo " issue ID, the script will try to make use of the _git_pivotal completion." |
|
echo |
|
echo "git pivotal --clean" |
|
echo " Fetchs the remote parent (master or develop), starts an interactive rebase" |
|
echo " with it, then push the result to the upstream branch." |
|
echo |
|
echo "git pivotal --deliver" |
|
echo " Creates a Github pull request then sets the task state to 'delivered'. If" |
|
echo " there's already a closed pull request on the same branch and same base, the" |
|
echo " old one will be reopened instead." |
|
|
|
echo |
|
|
|
echo "Branches whose name begins with 'hotfix/' depends on master (ie. that --clean" |
|
echo "will rebase on master, and --deliver will open a pull request on master), any" |
|
echo "other will depends on develop." |
|
|
|
echo |
|
|
|
} |
|
|
|
## tools |
|
|
|
pivotal_get() { |
|
curl -s -X GET -H "X-TrackerToken: ${pivotalToken}" -H "Content-Type: application/json" -d "${2}" "${pivotalApi}${1}" |
|
} |
|
|
|
pivotal_post() { |
|
curl -s -X POST -H "X-TrackerToken: ${pivotalToken}" -H "Content-Type: application/json" -d "${2}" "${pivotalApi}${1}" |
|
} |
|
|
|
pivotal_put() { |
|
curl -s -X PUT -H "X-TrackerToken: ${pivotalToken}" -H "Content-Type: application/json" -d "${2}" "${pivotalApi}${1}" |
|
} |
|
|
|
github_get() { |
|
curl -s -X GET -H "Authorization: token ${githubToken}" -H "Content-Type: application/json" -d "${2}" "${githubApi}${1}" |
|
} |
|
|
|
github_post() { |
|
curl -s -X POST -H "Authorization: token ${githubToken}" -H "Content-Type: application/json" -d "${2}" "${githubApi}${1}" |
|
} |
|
|
|
github_put() { |
|
curl -s -X PUT -H "Authorization: token ${githubToken}" -H "Content-Type: application/json" -d "${2}" "${githubApi}${1}" |
|
} |
|
|
|
github_patch() { |
|
curl -s -X PATCH -H "Authorization: token ${githubToken}" -H "Content-Type: application/json" -d "${2}" "${githubApi}${1}" |
|
} |
|
|
|
## command line parsing |
|
|
|
while [[ $1 = -?* ]]; do |
|
|
|
case $1 in |
|
|
|
-h|--help) |
|
usage |
|
exit |
|
;; |
|
|
|
--start) |
|
mode=start |
|
;; |
|
|
|
--estimation) |
|
shift |
|
estimation=$1 |
|
;; |
|
|
|
--clean) |
|
mode=clean |
|
;; |
|
|
|
--deliver) |
|
mode=deliver |
|
;; |
|
|
|
*) ;; |
|
|
|
esac |
|
|
|
shift |
|
|
|
done |
|
|
|
## configuration check |
|
|
|
if [ "${projectGithubRepository}" = "" ] || |
|
[ "${projectPivotalId}" = "" ] || |
|
[ "${pivotalToken}" = "" ] || |
|
[ "${githubToken}" = "" ]; then |
|
usage |
|
exit |
|
fi |
|
|
|
## execution |
|
|
|
case "${mode}" in |
|
|
|
start) |
|
branch="${1}" |
|
|
|
if [ "${branch}" = "" ]; then |
|
branch="$(git rev-parse --abbrev-ref HEAD)" |
|
fi |
|
|
|
if [[ "${branch}" =~ ^[0-9]+$ ]] && type -t _git_pivotal > /dev/null; then |
|
_git_pivotal # Require the _git_pivotal hook somewhere (export -f with bash is fine) |
|
branch=$(printf '%s\n' ${COMPREPLY[@]} | grep -P '_'"${branch}"'$') |
|
fi |
|
|
|
pivotalStoryId=$(printf '%s' "${branch}" | grep -Po '_[0-9]+$' | grep -Po '[0-9]+$') |
|
|
|
if [ "${pivotalStoryId}" = "" ]; then |
|
usage |
|
exit |
|
fi |
|
|
|
if printf '%s' "${branch}" | grep -P "^hotfix/"; then |
|
parent=master |
|
else |
|
parent=develop |
|
fi |
|
|
|
if ! git show-ref --verify --quiet "refs/heads/${branch}"; then |
|
git checkout "${parent}" || exit |
|
git checkout -b "${branch}" || exit |
|
git push --set-upstream origin "${branch}" |
|
else |
|
git checkout "${branch}" || exit |
|
fi |
|
|
|
pivotal_put /projects/"${projectPivotalId}"/stories/"${pivotalStoryId}" '{"estimate":'${estimation}',"current_state":"started"}' > /dev/null |
|
|
|
echo "Story started." |
|
;; |
|
|
|
clean) |
|
branch="$(git rev-parse --abbrev-ref HEAD)" |
|
|
|
if printf '%s' "${branch}" | grep -P "^hotfix/"; then |
|
parent=master |
|
else |
|
parent=develop |
|
fi |
|
|
|
git fetch |
|
git rebase -i origin/"${parent}" |
|
|
|
git push -f |
|
;; |
|
|
|
deliver) |
|
branch="$(git rev-parse --abbrev-ref HEAD)" |
|
pivotalStoryId=$(printf '%s' "${branch}" | grep -Po '_[0-9]+$' | grep -Po '[0-9]+$') |
|
|
|
if [ "${pivotalStoryId}" = "" ]; then |
|
usage |
|
exit |
|
fi |
|
|
|
if printf '%s' "${branch}" | grep -P "^hotfix/"; then |
|
parent=master |
|
else |
|
parent=develop |
|
fi |
|
|
|
git push || exit |
|
sleep 3 |
|
|
|
prUser=$(printf "%s" "${projectGithubRepository}" | grep -oP '^[^/]+') |
|
prRequest=$(github_get /repos/"${projectGithubRepository}"/pulls"?state=closed&head=${prUser}:${branch}&base=${parent}" | underscore --outfmt json extract 0 2> /dev/null) |
|
|
|
# Is there already a PR ? |
|
if [ "${prRequest}" = "" ]; then |
|
|
|
# Nope, create a new one |
|
prRequest=$(github_post /repos/"${projectGithubRepository}"/pulls '{"title":"[#'"${pivotalStoryId}"'] '"${branch}"'", "head":"'"${branch}"'", "base":"'"${parent}"'"}') |
|
|
|
else |
|
prNumber=$(printf "%s" "${prRequest}" | underscore --outfmt text extract number) |
|
prMerged=$(github_get /repos/"${projectGithubRepository}"/pulls/"${prNumber}"/merge | underscore --outfmt text select ':root > .id') |
|
|
|
# Has the pull request already been merged ? |
|
if [[ "${prMerged}" != "" ]]; then |
|
|
|
# Yep, create a new one |
|
prRequest=$(github_post /repos/"${projectGithubRepository}"/pulls '{"title":"[#'"${pivotalStoryId}"'] '"${branch}"'", "head":"'"${branch}"'", "base":"'"${parent}"'"}') |
|
|
|
else |
|
|
|
# Nope, reopen the old one |
|
github_patch /repos/"${projectGithubRepository}"/pulls/"${prNumber}" '{"state":"open"}' > /dev/null |
|
|
|
fi |
|
|
|
fi |
|
|
|
prLink=$(printf "%s" "${prRequest}" | underscore --outfmt text extract html_url) |
|
|
|
if [ "${prLink}" = "" ]; then |
|
echo "Pull request creation failed." |
|
exit |
|
fi |
|
|
|
echo "Pull request ready (${prLink})." |
|
|
|
pivotal_post /projects/"${projectPivotalId}"/stories/"${pivotalStoryId}"/comments '{"text":"Pull request is up at '"${prLink}"'"}' > /dev/null |
|
pivotal_put /projects/"${projectPivotalId}"/stories/"${pivotalStoryId}" '{"current_state":"delivered"}' > /dev/null |
|
echo "Story delivered." |
|
;; |
|
|
|
*) |
|
usage |
|
exit |
|
;; |
|
|
|
esac |
|
|