Last active
November 16, 2023 18:35
-
-
Save bfg/4bb860031e120f60bdb672e187176534 to your computer and use it in GitHub Desktop.
git-version.sh
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
#!/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