开发者

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"
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜