Last active
May 19, 2022 18:02
-
-
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]
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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