Created
June 11, 2019 12:18
-
-
Save johannjacobsohn/fb1a545d77203faee0952e0dd3a76d4b to your computer and use it in GitHub Desktop.
Sync redmine issues to gitlab merge requests and submit both in one go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
set -eu -o pipefail -o errexit -o noclobber -o nounset | |
IFS=$'\n\t' # http://redsymbol.net/articles/unofficial-bash-strict-mode/ | |
# set -x # DEBUG | |
# | |
# todo | |
# --time | |
# http://www.redmine.org/projects/redmine/wiki/Rest_TimeEntries#Creating-a-time-entry | |
# POST /time_entries.json | |
# time_entry (required): a hash of the time entry attributes, including: | |
# issue_id or project_id (only one is required): the issue id or project id to log time on | |
# spent_on: the date the time was spent (default to the current date) | |
# hours (required): the number of spent hours | |
# activity_id: the id of the time activity. This parameter is required unless a default activity is defined in Redmine. | |
# comments: short description for the entry (255 characters max) | |
# | |
function headline { | |
echo "git fix -- sync redmine issue and gitlab merge request" | |
echo "" | |
} | |
function usage { | |
echo "Usage:" | |
echo " /path/to/project $ git fix <ticket number> [--wip] [--nocommit] [--nohooks] [-h]" | |
} | |
function help { | |
headline | |
echo "A quick shell scripts that automatically integrates into git and does" | |
echo "all the annoying house-keeping tasks that are required for one-commit" | |
echo "bugfix ticket." | |
echo "" | |
echo "Will do the following (in this order)" | |
echo " - read config and access token from git config" | |
echo " - retrieve ticket data from redmine" | |
echo " - create bugfix branch from ticket title" | |
echo " - commit staged changes into said branch using ticket title and number" | |
echo " - push branch to origin (read from gitconfig remote)" | |
echo " - create merge request to develop in gitlab (link to redmine ticket)" | |
echo " - link merge request in redmine ticket" | |
echo "" | |
echo "Installation" | |
echo " - put this file into a directory that is in \$PATH (eg. ~/bin/)" | |
echo " - create access token in gitlab and redmine" | |
echo " - configure globally in ~/.gitconfig or project-dependent in /path/to/project/.git/config" | |
echo "" | |
echo "Configuration" | |
echo " Add this to your local or global git config:" | |
echo "" | |
echo " >[gitfix]" | |
echo " > gitlabToken = <token>" | |
echo " > redmineToken = <token>" | |
echo " > gitlabUrl = https://<gitlab>/api/v4/projects" | |
echo " > redmineUrl = https://<redmine>/issues" | |
echo "" | |
usage | |
echo "" | |
echo "Further reading" | |
echo " - http://www.redmine.org/projects/redmine/wiki/Rest_api_with_curl" | |
echo " - https://docs.gitlab.com/ce/api/merge_requests.html" | |
echo "" | |
echo "Dependencies" | |
echo " - curl" | |
echo " - jq" | |
echo " - sed" | |
echo " - git" | |
echo "" | |
} | |
function fail { | |
echo "❌ $1" | |
exit -1 | |
} | |
function success { | |
echo "✔ $1" | |
} | |
function errorOutputOnly { | |
out=`${1+"$@"}` | |
if [ $? != 0 ]; then | |
echo "$out" | |
fi | |
} | |
type=bugfix | |
issue=-1 | |
wip=0 | |
commit=1 | |
runhooks=1 | |
hooks="" | |
# Parse command line options | |
while [[ "$#" > 0 ]]; do | |
case $1 in | |
--wip) | |
wip=1 | |
;; | |
-h) | |
help | |
exit 0 | |
;; | |
--nocommit) | |
commit=0 | |
;; | |
--nohooks) | |
runhooks=0 | |
hooks="--no-verify" | |
;; | |
*) # unknown parameter | |
case "$1" in | |
''|*[!0-9]*) # unknown and not a number | |
headline | |
usage | |
echo "" | |
fail "unknown option $1" | |
exit -1 | |
;; | |
*) | |
issue=$1 | |
;; | |
esac | |
;; | |
esac | |
shift | |
done | |
# issue number is required, fail otherwise | |
if [ $issue = -1 ] | |
then | |
headline | |
usage | |
exit -1 | |
fi | |
if [ $commit = 1 ] | |
then | |
# check if there is something to be commited, otherwise this is pointless | |
git diff --quiet --exit-code --cached && fail "Nothing to be commited, genius. Maybe use --nocommit?" | |
fi | |
# check git hooks before doing all this work | |
if [ -f .git/hooks/pre-commit ] && [ $runhooks = 1 ] | |
then | |
errorOutputOnly .git/hooks/pre-commit | |
success "ran pre-commit hooks" | |
fi | |
if [ -f .git/hooks/pre-push ] && [ $runhooks = 1 ] | |
then | |
errorOutputOnly .git/hooks/pre-push | |
success "ran pre-push hooks" | |
fi | |
# read data from config | |
gitlab_token=`git config --get gitfix.gitlabToken` || fail "failed to read gitlabToken from git config" | |
redmine_token=`git config --get gitfix.redmineToken` || fail "failed to read redmineToken from git config" | |
gitlab_url=`git config --get gitfix.gitlabUrl` || fail "failed to read gitlabUrl from git config" | |
redmine_url=`git config --get gitfix.redmineUrl` || fail "failed to read redmineUrl from git config" | |
gitlab_project=`git config --get remote.origin.url | sed -n 's/.*:\(.*\)\.git/\1/p' | sed -r 's/\//%2F/g'` || fail "failed to parse remote origin url from git config" | |
success "read data from git config" | |
# redmine | |
# - get issue data | |
# - create branchname | |
issue_json=`curl -s -H "Content-Type: application/json" -X GET -H "X-Redmine-API-Key: $redmine_token" "$redmine_url/$issue.json"` || fail "failed to curl redmine data" | |
if [ "$issue_json" == "" ]; then fail "failed to curl redmine data ($redmine_url/$issue)"; fi | |
title=$(echo $issue_json | jq -r .issue.subject | sed -r 's/["]+/_/g') | |
id=$(echo $issue_json | jq -r .issue.id) | |
branch=$(echo "$type/$id-$title" | sed -r 's/[. :]+/-/g') | |
issue_url="$redmine_url/$issue" | |
success "read data from redmine (title: \"$title\", branch: \"$branch\")" | |
# git | |
# - create branch | |
# - commit | |
# - push | |
containsFECode=$(git diff --cached --name-only | grep --quiet "src/frontend" && echo 1 || echo 0) | |
containsBECode=$(git diff --cached --name-only | grep --quiet "src/main" && echo 1 || echo 0) | |
if [ `git rev-parse -q --verify "$branch"` ] | |
then | |
echo "Craptastic, branch already exists. Use existing branch? (y/n)" | |
read ans | |
if [ "$ans" = "y" ] | |
then | |
errorOutputOnly git checkout "$branch" | |
else | |
fail "failed to create or reuse branch" | |
fi | |
else | |
git checkout --quiet -b "$branch" || fail "failed to create branch" | |
fi | |
if [ $commit = 1 ] | |
then | |
git commit --quiet $hooks -em "fix $id: $title" | |
fi | |
errorOutputOnly git stash --include-untracked # stash uncommitted changes to make sure unit tests in pre-push hooks catch missing files | |
errorOutputOnly git push --quiet $hooks # hopefully, a pre-push hook will kick in and test the validity of this change | |
errorOutputOnly git stash pop # restore pre-commit state | |
success "branch created and pushed" | |
# gitlab | |
# - create merge request | |
# - link redmine issue | |
# - get merge request url | |
payload=$(echo '{ | |
"source_branch": "'"$branch"'", | |
"target_branch": "develop", | |
"title": "'$([ "$wip" = 1 ] && echo "WIP:")"$id: $title"'", | |
"description": "'"$issue_url"'", | |
"labels": [ | |
'$([ "$containsBECode" = 1 ] && echo "\"Backend\",")' | |
'$([ "$containsFECode" = 1 ] && echo "\"Frontend\"")' | |
] | |
}') | |
mr_json=`curl -s -H "Content-Type: application/json" -X POST -H "Private-Token: $gitlab_token" "$gitlab_url/$gitlab_project/merge_requests" -d "$payload"` | |
mr_url=`echo $mr_json | jq -r .web_url` | |
if [ "$mr_url" = "null" ] || [ "$mr_url" = "" ] | |
then | |
echo "merge request was not submitted, maybe it already exits?" | |
echo "payload: $payload" | |
echo " Anyway, this is what came back:" | |
echo $mr_json | |
exit -1 | |
fi | |
success "merge request submitted: $mr_url" | |
# redmine: | |
# - link pullrequest url | |
payload='{ | |
"issue": { | |
"notes": "'$mr_url'" | |
} | |
}' | |
errorOutputOnly curl -s -H "Content-Type: application/json" -X PUT -H "X-Redmine-API-Key: $redmine_token" "$redmine_url/$issue.json" -d "$payload" | |
success "issue updated: $redmine_url/$issue" | |
errorOutputOnly git checkout develop |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment