|  | #!/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 | 
        
          |  |  |