Automatic tagging of releases
Asked Answered
C

10

70

How do you tag your release versions in git?

Now I have each release identified by build number, but they 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?

Cavill answered 21/9, 2010 at 12:5 Comment(0)
W
11

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.

Wink answered 21/9, 2010 at 12:55 Comment(0)
S
75

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.

Selena answered 6/12, 2014 at 14:3 Comment(8)
Is this triggered by a git hook (if so, which one?), or do you run this manually instead of git push?Chromatin
It is just in a .sh file which I run manually (but the .sh file is also run automatically as part of a Jenkins build in which the project always needs an update to the version number). It could probably be added to a hook somehow (I don't really know much about git hooks to be honest so can't help there I'm afraid!)Selena
Add 2>/dev/null at the end of git describe --contains $GIT_COMMIT to remove the "fatal" warning. Thanks for the scriptSexed
I would also add default values as 0, in case of no version exists, MAJOR=${VERSION_BITS[0]-0} MINOR=${VERSION_BITS[1]-0} PATCH=${VERSION_BITS[2]-0}Samal
@Selena Does the update script also support this syntax:v1.0.0Oly
I have a case where tags can be created from different commit histories (history rewrite or from another branch). For that specific case, the script needed an adaptation: while [ $(git tag -l "$NEW_TAG") ] do NUM3=$((NUM3 +1)) NEW_TAG="$NUM1.$NUM2.$NUM3" doneImtiaz
On the 2020 update script, shouldn't "MINOR" releases reset the PATCH number to 0, and MAJOR releases reset both MINOR and PATCH number to 0? In other words, v2.3.4 is followed by v3.0.0, not v3.3.4.Imtiaz
I get this error - fatal: No names found, cannot describe anything.Incalculable
S
15

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
Samal answered 7/8, 2018 at 13:43 Comment(1)
POSIX version worked for me on MacOS Catalina which is using zsh instead of old bash I started with @sergey 's solution but it didn't explode the dotted numbers to array then i used this one. +Reisman
W
11

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.

Wink answered 21/9, 2010 at 12:55 Comment(0)
W
8

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
Woodcraft answered 17/8, 2019 at 19:13 Comment(1)
It's better to post these kinds of answers as edits to the original answer.Ambrogino
C
6

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
Collate answered 29/1, 2021 at 9:34 Comment(2)
this is pretty good answer, I think only ${LATEST_TAG:1} is wrong (should be ${LATEST_TAG}Jesselton
If you use v at the beginning of the tag use ${current_tag:1}, otherwise use ${current_tag}.Collate
D
3

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.

Daisie answered 2/8, 2017 at 10:19 Comment(1)
This script doesn't work at all. There are so many problems with it I don't know where to start.Subcontract
S
2

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.

Sacroiliac answered 21/9, 2010 at 13:5 Comment(0)
A
2

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
Antoinetteanton answered 27/10, 2017 at 9:57 Comment(1)
Does the update script also support this syntax:v1.0.0Oly
I
1

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.

Incalculable answered 9/3, 2022 at 7:15 Comment(0)
S
-2

good solution. my i suggest using annotated tags to represent a release? like:

git tag -a "$NEW_TAG"  -m "autogenerated"
Scrutiny answered 4/7, 2019 at 8:38 Comment(1)
I tried this but it inserted literally $NEW_TAG as the new version. Not the next version as intended.Adali

© 2022 - 2024 — McMap. All rights reserved.