Skip to content

Instantly share code, notes, and snippets.

@kolomenkin
Last active May 19, 2022 18:02
Show Gist options
  • Save kolomenkin/a398381b0a663eb0e9f277678f5709ce to your computer and use it in GitHub Desktop.
Save kolomenkin/a398381b0a663eb0e9f277678f5709ce to your computer and use it in GitHub Desktop.
Version script to calculate software version from git tags and current date in format: <major>.<minor>.<patch>-<current-year-2-digits><current-day-of-year>.<commit-hash>[.dirty]
#!/usr/bin/env bash
script_version="1.33"
set -e
set -u
set -o pipefail
# set -x
# ==================================================
# This script calculates and prints a software version
# based of git repo in current directory and current time.
#
# Version format:
# <major>.<minor>.<patch>-<current-year-2-digits><current-day-of-year>.<commit-hash>[.dirty]
#
# Examples:
# 19.12.45-19350.82f35c
# 20.2.0-20078.7d92ba
# 20.2.0-20084.7d92ba.dirty
#
# <major> and <minor> are taken from the biggest tag in HEAD ancestor tree
# with name in format:
# ref/tags/epoch/v<major>.<optionally-zero-prepended-minor>
#
# Examples of tag names:
# epoch/v19.11
# epoch/v20.02
#
# <patch> is a number of commits between the Major-Minor tag and HEAD.
#
# <comit-hash> is a HEAD commit hash truncated to 6 left chars.
#
# ".dirty" is added in case git working directory is not clean:
# untracked files are present or tracked files are modified.
#
# ==================================================
# Settings
dir=.
commit=HEAD
commit_hash_length=6
tag_prefix="epoch/v"
debug_mode=0
# ==================================================
# Parse commmand line
if [[ "${1:-}" == "--help" ]]; then
echo >&2 "Script version: $script_version"
echo >&2 "Usage: ./version.sh [--debug] [<directory>]"
exit 2
fi
if [[ "${1:-}" == "--debug" ]]; then
debug_mode=1
shift
fi
if [[ -n "${1:-}" ]]; then
cd -- "$1"
fi
debug() {
if [[ "$debug_mode" == "1" ]]; then echo "dbg> $*" >&2; fi
}
dbg_print_var() {
variable_name="$1"
debug "$variable_name: ${!variable_name}"
}
# ==================================================
dbg_print_var "script_version"
# Calculate string in format "<last-two-year-digits><day-of-year>"
current_day_string=$(date '+%y%j')
dbg_print_var "current_day_string"
# Find if directory contains changes or untacked files
dirty=""
if [[ $(git -C "$dir" status --short) != "" ]]; then
dirty=".dirty"
fi
# Get sorted annotated tag list from specified commit descendent commit tree
tag_multiline_list=$(git -C "$dir" tag --list --sort="version:refname" --merged="$commit" --format="%(objecttype) %(refname:short)" -- "$tag_prefix*")
new_line="
"
debug "tag_multiline_list: ${tag_multiline_list//$new_line/; }"
# Get annotated tag name with biggest version
# in specified commit descendent commit tree
filtered_lines=$(echo "$tag_multiline_list" | grep "^tag " || true)
tag_name=$(echo "$filtered_lines" | sed "s/^tag //" | tail -n 1)
dbg_print_var "tag_name"
# ==================================================
major="0"
minor="0"
patch_level=""
commit_hash=""
if [[ "$tag_name" != "" ]]; then
# Calculate commit offset from specified tag
# and get truncated commit hash
# Example of describe_line:
# epoch/v22.11-12-g9382ab
describe_line=$(git -C "$dir" describe --abbrev=$commit_hash_length --long --match="$tag_name" -- "$commit")
dbg_print_var "describe_line"
# Parse describe_line into parts
# Example of str:
# 22.01-12-g9382ab
str=${describe_line#"$tag_prefix"}
str_regex="^([0-9]+)\\.0*([1-9][0-9]*|0)-([0-9]+)-g([0-9a-f]{$commit_hash_length})$"
dbg_print_var "str"
dbg_print_var "str_regex"
if [[ $str =~ $str_regex ]]; then
major="${BASH_REMATCH[1]}"
minor="${BASH_REMATCH[2]}"
patch_level="${BASH_REMATCH[3]}"
commit_hash="${BASH_REMATCH[4]}"
dbg_print_var "major"
dbg_print_var "minor"
dbg_print_var "patch_level"
dbg_print_var "commit_hash"
fi
fi
if [[ "$patch_level" == "" ]]; then
patch_level=$(git -C "$dir" rev-list --count "$commit")
dbg_print_var "patch_level"
fi
if [[ "$commit_hash" == "" ]]; then
commit_hash=$(git -C "$dir" rev-parse --short=$commit_hash_length "$commit")
dbg_print_var "commit_hash"
fi
# ==================================================
# Final software version formatting
echo "$major.$minor.$patch_level-$current_day_string.$commit_hash$dirty"
# ==================================================
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment