1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
|
#!/usr/bin/env bash
#
# Usage: tag-release.sh <release-ver> [<ref>]
#
# Creates a new bugfix branch and release tag for a release.
# Optionally, the release can be based off a specific git ref.
# Default is HEAD of the current branch.
#
# The script does a lot of error checking, but boils down to
# the following actions.
#
# For major/minor releases (X.Y.0) from master branch:
# git checkout -b X.Y.x
# git tag -a X.Y.Z -m "X.Y.Z" HEAD
# and optionally:
# git push -u origin X.Y.x
# git push origin refs/tags/X.Y.Z
#
# For bugfix releases from associated branch (X.Y.x):
# git tag -a X.Y.Z -m "X.Y.Z" HEAD
# and optionally:
# git push origin refs/tags/X.Y.Z
#
SELF="${BASH_SOURCE[0]}"
SELF_NAME=$(basename "${SELF}")
GIT_EXE='git'
function validate_repo()
{
local HASH err AHEAD BEHIND proceed
# Check whether we have git
if ! hash ${GIT_EXE} 2>/dev/null; then
echo "Command '${GIT_EXE}' not found." 1>&2
return 1
fi
# Check if there is a valid git repo here
HASH=$(${GIT_EXE} rev-parse HEAD)
err=$?
if [[ ${err} -ne 0 ]]; then
echo "Not a valid repository." 1>&2
return ${err}
elif [[ -z ${HASH} ]]; then
echo "Not a valid repository." 1>&2
return 1
fi
if [[ -n "$(${GIT_EXE} status --porcelain)" ]]; then
echo "There are uncommitted changes. Aborting." 1>&2
return 1
fi
echo "Fetching repo data..."
${GIT_EXE} fetch
err=$?
if [[ ${err} -ne 0 ]]; then
echo "Failed to fetch repo data." 1>&2
return ${err}
fi
AHEAD=$(${GIT_EXE} rev-list @{u}..HEAD --count)
BEHIND=$(${GIT_EXE} rev-list HEAD..@{u} --count)
if [[ ${AHEAD} -ne 0 ]]; then
echo "There are unpushed changes. Continue anyway? (y/N)"
read proceed
if [[ ( "x${proceed}" != "xy" ) && ( "x${proceed}" != "xY" ) ]] ; then
echo "Aborting..."
return 1
fi
fi
if [[ ${BEHIND} -ne 0 ]]; then
echo "There are unmerged upstream changes. Continue anyway? (y/N)"
read proceed
if [[ ( "x${proceed}" != "xy" ) && ( "x${proceed}" != "xY" ) ]] ; then
echo "Aborting..."
return 1
fi
fi
}
function tag_release()
{
local TAG REF COMMIT BRANCH proceed new_branch ERR HASH
TAG=${1}
REF=${2}
if [ "x${TAG}" == "x" ]; then
echo "Missing release tag (e.g. 1.0.0)"
echo "Usage: ${SELF_NAME} tag [commit]"
return 1
fi
# bugfix branch name
BRANCH=${TAG%.[0-9]*}.x
if [ "x${REF}" == "x" ]; then
echo "Creating release tag ${TAG} and branch ${BRANCH} from HEAD, proceed? (y/N)"
# retrieve full hash of HEAD
COMMIT=$(${GIT_EXE} rev-list HEAD --max-count=1)
else
echo "Creating release tag ${TAG} and branch ${BRANCH} from ${REF}, proceed? (y/N)"
# retrieve full hash from ref or short hash
COMMIT=$(${GIT_EXE} rev-list ${REF} --max-count=1)
fi
read proceed
if [[ ( "x${proceed}" != "xy" ) && ( "x${proceed}" != "xY" ) ]] ; then
echo "Aborting..."
return 0
fi
# check if the remote branch already exists
${GIT_EXE} rev-parse --quiet --verify origin/${BRANCH} > /dev/null
if [ $? -ne 0 ]; then
# remote branch does not exist
new_branch=1
# does the branch already exist locally?
${GIT_EXE} rev-parse --quiet --verify ${BRANCH} > /dev/null
if [ $? -ne 0 ]; then
# local branch does not exist
# create bugfix branch from commit
${GIT_EXE} checkout "${COMMIT}" -b "${BRANCH}"
ERR=$?
if [ ${ERR} -ne 0 ]; then
echo "Failed to create branch ${BRANCH}"
return ${ERR}
fi
else
# local branch already exists
# When the branch already exists, make sure it is being used!
current_branch=$(${GIT_EXE} rev-parse --abbrev-ref HEAD)
if [ "$current_branch" != "${BRANCH}" ]; then
echo "You did not checkout the correct branch ${BRANCH} for tag ${TAG}"
return 1
fi
fi
else
new_branch=0
# When the branch already exists, make sure it is being used!
current_branch=$(${GIT_EXE} rev-parse --abbrev-ref HEAD)
if [ "$current_branch" != "${BRANCH}" ]; then
echo "You did not checkout the correct branch ${BRANCH} for tag ${TAG}"
return 1
fi
fi
# at this point we should be at the head of the tracking branch
# for this release. Make certain that HEAD matches COMMIT
HASH=$(${GIT_EXE} rev-list HEAD --max-count=1)
if [ ${HASH} != ${COMMIT} ]; then
echo "Commit specified does not match current branch HEAD"
return 1
fi
# create tag
${GIT_EXE} tag -a "${TAG}" -m "${TAG}" HEAD
ERR=$?
if [ ${ERR} -ne 0 ]; then
echo "Failed to create tag ${TAG}"
# cleanup... remove the branch that was created
${GIT_EXE} branch -d "${BRANCH}"
return ${ERR}
fi
# checkout tag in preparation for building release
# this should put you in a "detached HEAD" state
${GIT_EXE} checkout "${TAG}"
ERR=$?
if [ ${ERR} -ne 0 ]; then
echo "Failed to checkout tag ${TAG}"
# cleanup... remove the branch that was created
${GIT_EXE} branch -d "${BRANCH}"
return ${ERR}
fi
remote=$(${GIT_EXE} config remote.origin.url)
echo
echo "Do you wish to push this release branch and tag to $remote? (y/N)"
echo "You may want to do this manually after creating and verifying release."
echo "e.g."
echo " git push -u origin ${BRANCH}"
echo " git push origin refs/tags/${TAG}"
read proceed
if [[ ( "x${proceed}" == "xy" ) || ( "x${proceed}" == "xY" ) ]] ; then
if [ $new_branch .eq 1 ]; then
${GIT_EXE} push -u origin "${BRANCH}"
ERR=$?
if [ ${ERR} -ne 0 ]; then
echo "Failed to push branch ${BRANCH} to remote"
return ${ERR}
fi
fi
${GIT_EXE} push origin refs/tags/"${TAG}"
ERR=$?
if [ ${ERR} -ne 0 ]; then
echo "Failed to push tag ${BRANCH} to remote"
return ${ERR}
fi
else
echo "Branch and tag are local, changes not pushed to remote!"
fi
}
function main()
{
if validate_repo; then
tag_release "$@"
else
return $?
fi
}
main "$@"
|