Automatic tagging of releases
How do you tag your release versions in git?
Now I have each release identified by build number, but the开发者_JAVA百科y increment even if there are no changes in the repo. My idea is to have it generated automatically on successful deployment on staging server. E.g.
- run Hudson build
- when successful, add new tag, i.e. 1.0-1
- on next successful build add next tag, 1.0-2
- version tag is displayed then in site footer
This would require:
- Hudson to manage next version numbers
- or script to store last tag in some file
- or parse git tags to determine last
Any tips?
I wrote this to help with updating tags incrementally e.g. 1.0.1 to 1.0.2 etc.
2020 update: I have posted an improved version beneath/here. (Also worth seeing @Geoffrey's answer below for some comments on branching).
#!/bin/bash
#get highest tag number
VERSION=`git describe --abbrev=0 --tags`
#replace . with space so can split into an array
VERSION_BITS=(${VERSION//./ })
#get number parts and increase last one by 1
VNUM1=${VERSION_BITS[0]}
VNUM2=${VERSION_BITS[1]}
VNUM3=${VERSION_BITS[2]}
VNUM3=$((VNUM3+1))
#create new tag
NEW_TAG="$VNUM1.$VNUM2.$VNUM3"
echo "Updating $VERSION to $NEW_TAG"
#get current hash and see if it already has a tag
GIT_COMMIT=`git rev-parse HEAD`
NEEDS_TAG=`git describe --contains $GIT_COMMIT 2>/dev/null`
#only tag if no tag already
if [ -z "$NEEDS_TAG" ]; then
git tag $NEW_TAG
echo "Tagged with $NEW_TAG"
git push --tags
else
echo "Already a tag on this commit"
fi
I revisit this 6 years later needing to write a similar script for uploading to npm. Code below or here, comments and suggestions appreciated and welcome:
#!/bin/bash
# https://github.com/unegma/bash-functions/blob/main/update.sh
VERSION=""
#get parameters
while getopts v: flag
do
case "${flag}" in
v) VERSION=${OPTARG};;
esac
done
#get highest tag number, and add 1.0.0 if doesn't exist
CURRENT_VERSION=`git describe --abbrev=0 --tags 2>/dev/null`
if [[ $CURRENT_VERSION == '' ]]
then
CURRENT_VERSION='1.0.0'
fi
echo "Current Version: $CURRENT_VERSION"
#replace . with space so can split into an array
CURRENT_VERSION_PARTS=(${CURRENT_VERSION//./ })
#get number parts
VNUM1=${CURRENT_VERSION_PARTS[0]}
VNUM2=${CURRENT_VERSION_PARTS[1]}
VNUM3=${CURRENT_VERSION_PARTS[2]}
if [[ $VERSION == 'major' ]]
then
VNUM1=$((VNUM1+1))
elif [[ $VERSION == 'minor' ]]
then
VNUM2=$((VNUM2+1))
elif [[ $VERSION == 'patch' ]]
then
VNUM3=$((VNUM3+1))
else
echo "No version type (https://semver.org/) or incorrect type specified, try: -v [major, minor, patch]"
exit 1
fi
#create new tag
NEW_TAG="$VNUM1.$VNUM2.$VNUM3"
echo "($VERSION) updating $CURRENT_VERSION to $NEW_TAG"
#get current hash and see if it already has a tag
GIT_COMMIT=`git rev-parse HEAD`
NEEDS_TAG=`git describe --contains $GIT_COMMIT 2>/dev/null`
#only tag if no tag already
#to publish, need to be logged in to npm, and with clean working directory: `npm login; git stash`
if [ -z "$NEEDS_TAG" ]; then
npm version $NEW_TAG
npm publish --access public
echo "Tagged with $NEW_TAG"
git push --tags
git push
else
echo "Already a tag on this commit"
fi
exit 0
Subnote: this won't work with zsh as (for one), zsh doesn't use 0 indexed arrays.
In case if you will need Posix version, almost the same as above answer
#!/bin/sh
#Get the highest tag number
VERSION=`git describe --abbrev=0 --tags`
VERSION=${VERSION:-'0.0.0'}
#Get number parts
MAJOR="${VERSION%%.*}"; VERSION="${VERSION#*.}"
MINOR="${VERSION%%.*}"; VERSION="${VERSION#*.}"
PATCH="${VERSION%%.*}"; VERSION="${VERSION#*.}"
#Increase version
PATCH=$((PATCH+1))
#Get current hash and see if it already has a tag
GIT_COMMIT=`git rev-parse HEAD`
NEEDS_TAG=`git describe --contains $GIT_COMMIT`
#Create new tag
NEW_TAG="$MAJOR.$MINOR.$PATCH"
echo "Updating to $NEW_TAG"
#Only tag if no tag already (would be better if the git describe command above could have a silent option)
if [ -z "$NEEDS_TAG" ]; then
echo "Tagged with $NEW_TAG (Ignoring fatal:cannot describe - this means commit is untagged) "
git tag $NEW_TAG
else
echo "Already a tag on this commit"
fi
What you are talking about is more akin to a technical revision number like the one a git describe
would generate.
That is different from a true application version, which you should still manage independently from Hudson since it depends on a versioning policy.
based on timhc22 answer, but slightly modified to handle initial tagging, and silence output errors in case of no tag is present on commit
#!/bin/bash
#get highest tag number
VERSION=`git describe --abbrev=0 --tags 2>/dev/null`
if [ -z $VERSION ];then
NEW_TAG="1.0.0"
echo "No tag present."
echo "Creating tag: $NEW_TAG"
git tag $NEW_TAG
git push --tags
echo "Tag created and pushed: $NEW_TAG"
exit 0;
fi
#replace . with space so can split into an array
VERSION_BITS=(${VERSION//./ })
#get number parts and increase last one by 1
VNUM1=${VERSION_BITS[0]}
VNUM2=${VERSION_BITS[1]}
VNUM3=${VERSION_BITS[2]}
VNUM3=$((VNUM3+1))
#create new tag
NEW_TAG="${VNUM1}.${VNUM2}.${VNUM3}"
#get current hash and see if it already has a tag
GIT_COMMIT=`git rev-parse HEAD`
CURRENT_COMMIT_TAG=`git describe --contains $GIT_COMMIT 2>/dev/null`
#only tag if no tag already (would be better if the git describe command above could have a silent option)
if [ -z "$CURRENT_COMMIT_TAG" ]; then
echo "Updating $VERSION to $NEW_TAG"
git tag $NEW_TAG
git push --tags
echo "Tag created and pushed: $NEW_TAG"
else
echo "This commit is already tagged as: $CURRENT_COMMIT_TAG"
fi
I'm using this function with gitlab pipelines, So I hope this will help for you
#!/bin/bash
bump_version_tag(){
if [ -z "$(git ls-remote --tags origin)" ]; then
local new_tag="v1.0.0"
else
local current_tag=$(git ls-remote --tags origin | cut -f 3 -d '/')
IFS='.' read -r -a array <<< "${current_tag:1}"
local major=${array[0]}
local minor=${array[1]}
local patch=${array[2]}
if [ "$minor" == 999 ] && [ "$patch" == 999 ]; then
((major++))
minor=0
patch=0
elif [ "$patch" == 999 ]; then
((minor++))
patch=0
else
((patch++))
fi
new_tag="v${major}.${minor}.${patch}"
fi
echo $new_tag
}
git tag $bump_version_tag -a "autogenerated tag"
git push origin $NEW_TAG
If you use v
at the beginning of the tag use ${current_tag:1}
, otherwise use ${current_tag}
.
Also if you want to increment only patch part of the tag, use this,
#!/bin/bash
RES=$(git show-ref --tags)
if [ -z "$RES" ]; then
NEW_TAG=v1.0.0
else
NEW_TAG=$(git describe --tags --abbrev=0 | awk -F. '{OFS="."; $NF+=1; print $0}')
fi
git tag $NEW_TAG -a "autogenerated tag"
git push origin $NEW_TAG
Really good solution timhc22 The only thing is that it takes the last tag (whatever the branch) If you work on a project with multiple branches you could have an issue. I proposed just an improvement with your base.
#!/bin/sh
# retrieve branch name
BRANCH_NAME=$(git branch | sed -n '/\* /s///p')
# remove prefix release
REGEXP_RELEASE="release\/"
VERSION_BRANCH=$(echo "$BRANCH_NAME" | sed "s/$REGEXP_RELEASE//")
echo "Current version branch is $VERSION_BRANCH"
# retrieve the last commit on the branch
VERSION=$(git describe --tags --match=$VERSION_BRANCH* --abbrev=0)
# split into array
VERSION_BITS=(${VERSION//./ })
#get number parts and increase last one by 1
VNUM1=${VERSION_BITS[0]}
VNUM2=${VERSION_BITS[1]}
VNUM3=${VERSION_BITS[2]}
VNUM3=$((VNUM3+1))
#create new tag
NEW_TAG="$VNUM1.$VNUM2.$VNUM3"
echo "Updating $VERSION to $NEW_TAG"
#get current hash and see if it already has a tag
GIT_COMMIT=`git rev-parse HEAD`
NEEDS_TAG=`git describe --contains $GIT_COMMIT`
#only tag if no tag already (would be better if the git describe command above could have a silent option)
if [ -z "$NEEDS_TAG" ]; then
echo "Tagged with $NEW_TAG (Ignoring fatal:cannot describe - this means commit is untagged) "
git tag $NEW_TAG
git push --tags
else
echo "Already a tag on this commit"
fi
This works for example if you have:
- a master branch: will create master-X.Y.Z
- a release/X.Y: will create X.Y.Z
In any case thanks a lot it helped me a lot.
Hudson automatically tags the build, if you use the git plugin and let Hudson extract the code. I'm not sure if this gets pushed automatically; in our set up we do extra tagging and include a 'git push --tags' in our build script, so we definitely see the Hudson tags in our central repository.
I am using as below. It is working perfectly with branches. Below snippets inspired from above comments and the gitversion / semver.org.
#!/bin/sh
# This script will be executed after commit in placed in .git/hooks/post-commit
# Semantic Versioning 2.0.0 guideline
#
# Given a version number MAJOR.MINOR.PATCH, increment the:
# MAJOR version when you make incompatible API changes,
# MINOR version when you add functionality in a backwards-compatible manner, and
# PATCH version when you make backwards-compatible bug fixes.
echo "Starting the taging process based on commit message +semver: xxxxx"
#get highest tags across all branches, not just the current branch
VERSION=`git describe --tags $(git rev-list --tags --max-count=1)`
# split into array
VERSION_BITS=(${VERSION//./ })
echo "Latest version tag: $VERSION"
#get number parts and increase last one by 1
VNUM1=${VERSION_BITS[0]}
VNUM2=${VERSION_BITS[1]}
VNUM3=${VERSION_BITS[2]}
# VNUM3=$((VNUM3+1))
# Taken from gitversion
# major-version-bump-message: '\+semver:\s?(breaking|major)'
# minor-version-bump-message: '\+semver:\s?(feature|minor)'
# patch-version-bump-message: '\+semver:\s?(fix|patch)'
# get last commit message and extract the count for "semver: (major|minor|patch)"
COUNT_OF_COMMIT_MSG_HAVE_SEMVER_MAJOR=`git log -1 --pretty=%B | egrep -c '\+semver:\s?(breaking|major)'`
COUNT_OF_COMMIT_MSG_HAVE_SEMVER_MINOR=`git log -1 --pretty=%B | egrep -c '\+semver:\s?(feature|minor)'`
COUNT_OF_COMMIT_MSG_HAVE_SEMVER_PATCH=`git log -1 --pretty=%B | egrep -c '\+semver:\s?(fix|patch)'`
if [ $COUNT_OF_COMMIT_MSG_HAVE_SEMVER_MAJOR -gt 0 ]; then
VNUM1=$((VNUM1+1))
fi
if [ $COUNT_OF_COMMIT_MSG_HAVE_SEMVER_MINOR -gt 0 ]; then
VNUM2=$((VNUM2+1))
fi
if [ $COUNT_OF_COMMIT_MSG_HAVE_SEMVER_PATCH -gt 0 ]; then
VNUM3=$((VNUM3+1))
fi
# count all commits for a branch
GIT_COMMIT_COUNT=`git rev-list --count HEAD`
echo "Commit count: $GIT_COMMIT_COUNT"
export BUILD_NUMBER=$GIT_COMMIT_COUNT
#create new tag
NEW_TAG="$VNUM1.$VNUM2.$VNUM3"
echo "Updating $VERSION to $NEW_TAG"
#only tag if commit message have version-bump-message as mentioned above
if [ $COUNT_OF_COMMIT_MSG_HAVE_SEMVER_MAJOR -gt 0 ] || [ $COUNT_OF_COMMIT_MSG_HAVE_SEMVER_MINOR -gt 0 ] || [ $COUNT_OF_COMMIT_MSG_HAVE_SEMVER_PATCH -gt 0 ]; then
echo "Tagged with $NEW_TAG (Ignoring fatal:cannot describe - this means commit is untagged) "
git tag "$NEW_TAG"
else
echo "Already a tag on this commit"
fi
For posterity, this version adapted from the above.
-Requires a comment
-Ensures tags are pulled from the remote
-Redirects stderr to not pollute the shell.
-Prepends 'v' onto versions (may not be required by the OP but others may need this).
-Initializes v0.0.0 if none exist.
-Pushes back to origin
#!/bin/bash
if [[ -z "$1" ]]; then
echo "ERROR: arg must be a description of the release in quotes"
exit 1
fi
DESCRIPTION=$1
# ensure local tags are current
git fetch --tags origin
#get highest tag number
HIGHESTVERSION=$(git tag -l --sort -version:refname | head -n 1 2> /dev/null)
set -e
VERSION=${HIGHESTVERSION:-'v0.0.0'}
REMOVE="${VERSION%%v*}"; VERSION="${VERSION#*v}"
MAJOR="${VERSION%%.*}"; VERSION="${VERSION#*.}"
MINOR="${VERSION%%.*}"; VERSION="${VERSION#*.}"
PATCH="${VERSION%%.*}"; VERSION="${VERSION#*.}"
#Increase version
PATCH=$((PATCH+1))
#Get current hash and see if it already has a tag
GIT_COMMIT=$(git rev-parse HEAD)
NEEDS_TAG=$(git describe --contains $GIT_COMMIT 2> /dev/null) && exit_status=0 || exit_status=$?
#Only tag if no tag already (would be better if the git describe command above could have a silent option)
if [[ -z "$NEEDS_TAG" && ! $exit_status -eq 0 ]]; then
#Create new tag
NEW_TAG="v$MAJOR.$MINOR.$PATCH"
echo "Updating to $NEW_TAG"
git tag -a $NEW_TAG -m "$DESCRIPTION"
echo "Tagged with $NEW_TAG"
git push origin $NEW_TAG
git -c advice.detachedHead=false checkout $NEW_TAG
echo "Checkout release"
git checkout $NEW_TAG
else
echo "Already a tag $HIGHESTVERSION on this commit"
fi
Whats missing? It would be best practice to ensure the commit is merged to main or master, but since this is inconsistent between repositories, I have not included it.
good solution. my i suggest using annotated tags to represent a release? like:
git tag -a "$NEW_TAG" -m "autogenerated"
精彩评论