Skip to content

Instantly share code, notes, and snippets.

@bfg
Last active November 16, 2023 18:35
Show Gist options
  • Save bfg/4bb860031e120f60bdb672e187176534 to your computer and use it in GitHub Desktop.
Save bfg/4bb860031e120f60bdb672e187176534 to your computer and use it in GitHub Desktop.
git-version.sh
#!/bin/sh
#
# NOTE: variables and functions are prefixed with `_` so that file can be safely sourced into a current
# shell in a attempt to prevent name clashes
_GIT_ENV=""
_GIT_JSON=""
_GIT_PROPS=""
_PROJECT_VERSION=""
_DISPLAY_VERSION=""
_DISPLAY_VERSION_PREFIX=""
die() {
echo "FATAL: $@" 1>&2
exit 1
}
_compute_project_version() {
# sanitize result; our project version should be able to be used as part of a url
_do_compute_project_version | \
awk '{print $1}' | \
tr '[A-Z]' '[a-z]' | \
tr '_' '-' | \
sed -e 's/--/-/g' | \
tr -cd '[:print:]'
}
_do_compute_project_version_from_file() {
local ver="$1"
test -z "$ver" && return 0
local git_commit_date=""
local git_commit_time=""
local git_commit_datetime=""
if [ ! -z "$GIT_COMMIT_TIMESTAMP" -a "$GIT_COMMIT_TIMESTAMP" != "0" ]; then
git_commit_date=$(date --date "@${GIT_COMMIT_TIMESTAMP}" +'%Y%m%d')
git_commit_time=$(date --date "@${GIT_COMMIT_TIMESTAMP}" +'%H%M')
git_commit_datetime=$(date --date "@${GIT_COMMIT_TIMESTAMP}" +'%Y%m%d-%H%M')
fi
echo "$ver" | \
sed -e "s/%GIT_SHA%/$GIT_COMMIT_ID_ABBREV/g" | \
sed -e "s/%GIT_BRANCH%/$GIT_BRANCH/g" | \
sed -e "s/%GIT_COMMIT_ID_ABBREV%/$GIT_COMMIT_ID_ABBREV/g" | \
sed -e "s/%GIT_COMMIT_TIMESTAMP%/$GIT_COMMIT_TIMESTAMP/g" | \
sed -e "s/%GIT_COMMIT_TIME%/$git_commit_time/g" | \
sed -e "s/%GIT_COMMIT_DATE%/$git_commit_date/g" | \
sed -e "s/%GIT_COMMIT_DATETIME%/$git_commit_datetime/g"
}
_do_compute_project_version_from_git_tags() {
local tags="$1"
test -z "$tags" && return 0
# select first from available git tags...
local tag=$(echo "$tags" | awk '{print $1}' | tr -d ' ')
if [ ! -z "$tag" ]; then
# remove release-/v prefixes
tag=$(echo "$tag" | sed -e 's/^release-//g' | sed -e 's/^v//g')
if [ ! -z "$tag" ]; then
echo "$tag"
return 0
fi
fi
return 0
}
_do_compute_project_version_fallback() {
if [ ! -z "$GIT_BRANCH" -a ! -z "$GIT_COMMIT_ID_ABBREV" ]; then
echo "${GIT_BRANCH}-${GIT_COMMIT_ID_ABBREV}"
else
echo "0.0-dev"
fi
}
_do_compute_project_version() {
# check if there's a git tag
local version=$(_do_compute_project_version_from_git_tags "$GIT_TAGS")
if [ ! -z "$version" ]; then
echo "$version"
return 0
fi
# try to compute something from VERSION file
version=$(_do_compute_project_version_from_file "$_PROJECT_VERSION")
if [ ! -z "$version" ]; then
echo "$version"
return 0
fi
# ... let's just make up something
_do_compute_project_version_fallback
}
_set_vars_via_env() {
export GIT_BRANCH="$CI_COMMIT_BRANCH"
export GIT_COMMIT_ID="$CI_COMMIT_SHA"
export GIT_COMMIT_ID_ABBREV="$CI_COMMIT_SHORT_SHA"
export GIT_COMMIT_TIME="$CI_COMMIT_TIMESTAMP"
export GIT_COMMIT_TIMESTAMP=$(date -d "${GIT_COMMIT_TIME}" +%s 2>/dev/null)
test -z GIT_COMMIT_TIMESTAMP && GIT_COMMIT_TIMESTAMP=0
export GIT_REMOTE_ORIGIN_URL=$(echo "$CI_REPOSITORY_URL" | sanitize_git_url)
export GIT_TAGS="$CI_COMMIT_TAG"
}
_set_vars_via_git() {
export GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
export GIT_COMMIT_ID=$(git rev-parse HEAD 2>/dev/null)
export GIT_COMMIT_ID_ABBREV=$(git rev-parse --short HEAD 2>/dev/null)
export GIT_COMMIT_TIME=$(TZ=UTC git show -s --date=iso-strict-local --format=%cd 2>/dev/null)
export GIT_COMMIT_TIMESTAMP=$(git show --no-patch --format=%ct 2>/dev/null)
test -z GIT_COMMIT_TIMESTAMP && GIT_COMMIT_TIMESTAMP=0
export GIT_REMOTE_ORIGIN_URL=$(git ls-remote --get-url origin 2>/dev/null | grep -v origin | head -n1 | sanitize_git_url)
export GIT_TAGS=$(git tag --points-at HEAD 2>/dev/null | sort -u|tr '\n' ' ' | sed -e 's/ $//g')
}
sanitize_git_url() {
# remove gitlab-ci-token:[MASKED]@ stuff from git remote url
sed -e 's/https:\/\/.*:.*@/https:\/\//g'
}
_set_vars() {
if [ -z "$CI" ]; then
_set_vars_via_git
else
_set_vars_via_env
fi
export GIT_BUILD_IS_RELEASE="false"
test ! -z "$GIT_TAGS" && GIT_BUILD_IS_RELEASE="true"
# try to read project version from a file
_PROJECT_VERSION=$(_project_version_read)
export GIT_BUILD_VERSION=$(_compute_project_version)
}
_project_version_read() {
local version_file="VERSION"
if [ -f "$version_file" ]; then
local tmp=$(cat "${version_file}" | grep -vE '^[[:space:]]*#' | grep -vE '^[[:space:]]*$' | head -n1 | awk '{print $1}')
if [ ! -z "${tmp}" ]; then
echo "$tmp"
return 0
fi
fi
return 0
}
_write_git_props() {
cat <<EOF
git.branch="$GIT_BRANCH"
git.build.version="$GIT_BUILD_VERSION"
git.build.is_release=$GIT_BUILD_IS_RELEASE
git.commit.id="$GIT_COMMIT_ID"
git.commit.id.abbrev="$GIT_COMMIT_ID_ABBREV"
git.commit.time="$GIT_COMMIT_TIME"
git.commit.time=$GIT_COMMIT_TIMESTAMP
git.remote.origin.url="$GIT_REMOTE_ORIGIN_URL"
git.tags="$GIT_TAGS"
EOF
}
_write_git_json() {
cat <<EOF
{
"git": {
"build": {
"version": "$GIT_BUILD_VERSION",
"is_release": $GIT_BUILD_IS_RELEASE
},
"commit": {
"id": "$GIT_COMMIT_ID",
"abbrev": "$GIT_COMMIT_ID_ABBREV",
"time": "$GIT_COMMIT_TIME",
"timestamp": $GIT_COMMIT_TIMESTAMP
},
"remote": {
"origin": {
"url": "$GIT_REMOTE_ORIGIN_URL"
}
},
"branch": "$GIT_BRANCH",
"tags": "$GIT_TAGS"
}
}
EOF
}
_write_git_envfile() {
cat <<EOF
export GIT_BRANCH="$GIT_BRANCH"
export GIT_BUILD_VERSION="$GIT_BUILD_VERSION"
export GIT_BUILD_IS_RELEASE="$GIT_BUILD_IS_RELEASE"
export GIT_COMMIT_ID="$GIT_COMMIT_ID"
export GIT_COMMIT_ID_ABBREV="$GIT_COMMIT_ID_ABBREV"
export GIT_COMMIT_TIME="$GIT_COMMIT_TIME"
export GIT_COMMIT_TIMESTAMP="$GIT_COMMIT_TIMESTAMP"
export GIT_REMOTE_ORIGIN_URL="$GIT_REMOTE_ORIGIN_URL"
export GIT_TAGS="$GIT_TAGS"
EOF
}
_do_run() {
# set variables
_set_vars
# write git info
test ! -z "$_GIT_ENV" && _write_git_envfile > "$_GIT_ENV"
test ! -z "$_GIT_JSON" && _write_git_json > "$_GIT_JSON"
test ! -z "$_GIT_PROPS" && _write_git_props > "$_GIT_PROPS"
# maybe output version
if [ ! -z "$_DISPLAY_VERSION" ]; then
local prefix=""
test "${GIT_BUILD_IS_RELEASE}" = "true" && prefix="${_DISPLAY_VERSION_PREFIX}"
echo "${prefix}${GIT_BUILD_VERSION}"
fi
# required because failed `test` above can make this function to return 1
return 0
}
_printhelp() {
cat <<EOF
Usage: $0 [OPTIONS]
This script tries to read GIT information via \$CI_XXX variables or via git(1) binary
and outputs various formats that can be consumed by application stored in the repository.
OPTIONS
-p <file> Outputs git.properties-style file
-j <file> Outputs json file
-e <file> Outputs file that can be sourced into any shell
-E Same as -e <file>, but output will be written to stdout
-v Shows computed project version
-T Prepend \`v\` to computed project version if current version is a release
-h This help message
PROJECT VERSION COMPUTATION
Project version (-v flag) gets computed from:
* first non-empty git tag
* \`VERSION\` file content with %magic% placeholders
* <git-branch>-<git sha> as a fallback
VERSION FILE
This script ready file \`VERSION\` if exists in current directory and uses it's
content to compute version from it (see -v flag). File can also contain magic placeholders
which interpolate to a git values
* %GIT_BRANCH% - git branch
* %GIT_SHA% - short git commit sha
ENVIRONMENT VARIABLES
This file can also be sourced into the current shell and the following variables will be set:
* GIT_BRANCH
* GIT_BUILD_VERSION
* GIT_BUILD_IS_RELEASE
* GIT_COMMIT_ID
* GIT_COMMIT_ID_ABBREV
* GIT_COMMIT_TIME
* GIT_COMMIT_TIMESTAMP
* GIT_REMOTE_ORIGIN_URL
* GIT_TAGS
EOF
}
# parse command line...
TEMP=$(getopt -o p:j:e:EvTh -- "$@")
test "$?" != "0" && die "Command line parsing error."
eval set -- "$TEMP"
while true; do
case $1 in
-p)
_GIT_PROPS="$2"
shift 2
;;
-j)
_GIT_JSON="$2"
shift 2
;;
-e)
_GIT_ENV="$2"
shift 2
;;
-E)
_GIT_ENV="/dev/stdout"
shift
;;
-v)
_DISPLAY_VERSION=1
shift
;;
-T)
_DISPLAY_VERSION_PREFIX="v"
shift
;;
-h|--help)
_printhelp
exit 0
;;
--)
shift
break
;;
*)
echo "Command line parsing error: '$1'." 1>&2
exit 1
;;
esac
done
_do_run
# vim:shiftwidth=2 softtabstop=2 expandtab
# EOF
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment