Skip to content

Instantly share code, notes, and snippets.

@samdolan
Created February 3, 2016 15:18
Show Gist options
  • Select an option

  • Save samdolan/b3738164a1dc1cd4ab4e to your computer and use it in GitHub Desktop.

Select an option

Save samdolan/b3738164a1dc1cd4ab4e to your computer and use it in GitHub Desktop.
A script for generating version numbers for your git repository
#!/bin/bash
set -eu
#==================================
# ENV variables
#----------------------------------
# enable debug mode
BUILD_REV_DEBUG=0
BUILD_REV_REPO_DIR_DEFAULT=$(pwd)
BUILD_REV_REPO_DIR="${BUILD_REV_REPO_DIR:-$BUILD_REV_REPO_DIR_DEFAULT}"
BUILD_REV_INCLUDE_TIMESTAMP_DEFAULT=1
BUILD_REV_INCLUDE_TIMESTAMP=${BUILD_REV_INCLUDE_TIMESTAMP:-$BUILD_REV_INCLUDE_TIMESTAMP_DEFAULT}
BUILD_REV_DIRTY_MARK_DEFAULT='*'
BUILD_REV_DIRTY_MARK="${BUILD_REV_DIRTY_MARK:-$BUILD_REV_DIRTY_MARK_DEFAULT}"
BUILD_REV_DATE_FORMAT_DEFAULT=+%Y%m%d-%H:%M
BUILD_REV_DATE_FORMAT=${BUILD_REV_DATE_FORMAT:-$BUILD_REV_DATE_FORMAT_DEFAULT}
BUILD_REV_INCLUDE_HOSTNAME_DEFAULT=0
BUILD_REV_INCLUDE_HOSTNAME=${BUILD_REV_INCLUDE_HOSTNAME:-$BUILD_REV_INCLUDE_HOSTNAME_DEFAULT}
#==================================
# README/DEBUG
#----------------------------------
# Print the "info" banner
function print_banner () {
echo "============================================================"
echo "|_--------------------------------------------------------_|"
echo "| git-build-rev |"
echo "|~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|"
echo "| |"
echo "| a tool for creating build version numbering |"
echo "| flexible enough to handle a continuous |"
echo "| deploy environment. |"
echo "| |"
echo "| |"
echo "| |"
echo "|//////////// 222Labs.com //////////////////////////////////"
echo "+__________________________________________________________+"
}
# Print out the help readme
function print_usage () {
echo
echo "=> PROGRAM USAGE > "
echo
echo ' `./git-build-rev <directory>` - Generate build version for git directory. Defaults to current directory.'
echo ' `./git-build-rev env` - Print the output of a lot of our commands'
echo ' `./git-build-rev branch` - Print the current git branch of the active repo'
echo ' `./git-build-rev tag` - Print the current git tag of the active repo'
echo ' `./git-build-rev tag-closest` - Print the latest tag that contained the currently checkout out commit in the active repo'
echo ' `./git-build-rev debug` - Prints a ton of debug information'
echo ' `./git-build-rev --help` - This help message'
echo
}
# Print out information about how we format revisions
function print_revision_format () {
echo "REVISION FORMAT:"
echo
echo " Revision Format Spec = \`<git_short_hash>.<git_branch+git_tag><git_dirty_state>.<utc_formatted_timestamp>\`"
echo
echo 'EXAMPLE `generate` OUTPUT:'
echo
echo " > Git repo on master, with local changes"
echo ' $ ./git-project-revision'
echo ' $ d95c476.master*20120203-10:24'
echo
echo ' > Git repo on a seperate branch, with no local change (directory paths are expanded)'
echo ' $ ./git-project-revision generate $HOME/somerepo'
echo ' $ 01dd5a7.test_branch.*20120203-10:28'
echo
echo ' > No timestamp and a changed dirty flag'
echo ' $ BUILD_REV_INCLUDE_TIMESTAMP=0 BUILD_REV_DIRTY_MARK='!\!' ./git-project-revision ~/somerepo'
echo ' $ 01dd5a7.test_branch.*20120201-10:28'
echo
echo ' > Include the hostname at the end of the build'
echo ' $ BUILD_REV_INCLUDE_HOSTNAME=1 ./git-project-revision'
echo ' $ 01dd5a7.test_branch.20120203-09:57*@loving-engelbart.samasterhq'
echo
}
# Print out information about the environment variable settings
function print_env_vars () {
echo
echo "ENVIRONMENT VARIABLES:"
echo
echo "BUILD_REV_INCLUDE_TIMESTAMP - Should we append the current timestamp?"
echo "Current => ${BUILD_REV_INCLUDE_TIMESTAMP}"
echo "Default => ${BUILD_REV_INCLUDE_TIMESTAMP_DEFAULT}"
echo
echo "BUILD_REV_REPO_DIR - The path to the repository we're concerned about"
echo "Current => ${BUILD_REV_REPO_DIR}"
echo "Default => ${BUILD_REV_REPO_DIR_DEFAULT}"
echo
echo "BUILD_REV_DIRTY_MARK - The string to use to mark a revision as dirty (has local uncommitted changes)"
echo "Current => ${BUILD_REV_DIRTY_MARK}"
echo "Default => ${BUILD_REV_DIRTY_MARK_DEFAULT}"
echo
echo 'BUILD_REV_DATE_FORMAT - The date format string to use as the timestamp. Uses `date` command. See `man date`'
echo "Current => ${BUILD_REV_DATE_FORMAT}"
echo "Default => ${BUILD_REV_DATE_FORMAT_DEFAULT}"
echo
echo 'BUILD_REV_INCLUDE_HOSTNAME - Append `@$HOSTNAME` to the end of the build string. (e.g. <build_str>@mycomputer-com)'
echo "Current => ${BUILD_REV_INCLUDE_HOSTNAME}"
echo "Default => ${BUILD_REV_INCLUDE_HOSTNAME_DEFAULT}"
echo
}
# Print the output of our primary functions to aid in debugging
function print_func_debug () {
echo
echo "FUNCTION DEBUG:"
echo
echo "git_hash_short => $(git_hash_short)"
echo "is_git_repo_dirty => $(is_git_repo_dirty)"
echo "git_branch => $(git_branch)"
echo "git_tag => $(git_tag)"
echo "git_closest_tag => $(git_closest_tag)"
echo "gen_timestamp_utc => $(gen_timestamp_utc)"
echo
}
#=======================================
# Core functions
#---------------------------------------
# Check if our directory even exists before we do anything
function git_repo_exists () {
[[ $(git status 2> /dev/null | tail -n1) != "fatal: Not a git repository (or any of the parent directories): .git" ]] && echo 0
}
# Get the 7 digit short hash of the commit
function git_hash_short () {
git rev-parse --short HEAD
}
# Fetch the local status and see if there are any uncommited changes
# Returns `1` if is dirty
function is_git_repo_dirty () {
[[ $(git status 2> /dev/null | tail -n1) != "nothing to commit (working directory clean)" ]] && echo "0"
}
# Get the current branchname (master, my_new_features)
function git_branch () {
git rev-parse --abbrev-ref HEAD
}
# Get the closest tag that contains our current commit
# The commmand git_tag will only match if we are checked out at the exact commit of the tag
# Gonna leave it in here as a helper function for later on
function git_closest_tag () {
git tag --list --contains "$(git log -n1 --pretty='%h')" --sort -refname | head -n 1
}
# Grab the current tag name if there is one, otherwise "" (empty string)
function git_tag () {
current_tag=$(git describe --exact-match --tags "$(git log -n1 --pretty='%h')" 2>/dev/null)
if test "$(echo "$current_tag" | grep -c "fatal: No names")" != "0";then
echo ""
else
echo $current_tag
fi
}
# Generate the UTC timestamp
function gen_timestamp_utc () {
date -u "$BUILD_REV_DATE_FORMAT"
}
# Format the hostname string based on the BUILD_REV_INCLUDE_HOSTNAME env variable
# If not set, don't include anything
# If set to 1, use $HOSTNAME
# If set to anything else, just use that (e.g. <build_str>@prod)
function get_hostname_str () {
# Did the user change the setting at all?
if test "$BUILD_REV_INCLUDE_HOSTNAME" != "$BUILD_REV_INCLUDE_HOSTNAME_DEFAULT"; then
# Setting it to one just uses the computers hostname
if test "${BUILD_REV_INCLUDE_HOSTNAME}" = "1"; then
echo "${HOST:-$HOSTNAME}"
else
# or they can just set it to whatever they want (e.g. @prod)
echo "$BUILD_REV_INCLUDE_HOSTNAME"
fi
fi
}
# The top dog. Prints out the actual revision number
#
# Revision strings are formatted in the following template
# > <git_short_hash>.<git_branch_name>.<current_timestamp|(optional)><git_dirty_flag(optional)<@+<hostname>(optional)>
function generate_revision_str () {
# Let's go visit the directory
pushd "$BUILD_REV_REPO_DIR" >/dev/null 2>&1 || kaboom "Could not cd to $BUILD_REV_REPO_DIR"
# Build out the base
REV="$(git_hash_short).$(git_branch)"
log "starting with REV: $REV. Enabling options now"
CURRENT_TAG="$(git_tag)"
if [[ ! -z $CURRENT_TAG ]];then
log "got $CURRENT_TAG"
REV="$REV+$CURRENT_TAG"
log "got new rev $REV"
fi
# Do they want the timestamp?
if test "$BUILD_REV_INCLUDE_TIMESTAMP" = "1"; then
log "include timestamp enabled. Adding utc now"
REV="${REV}@$(gen_timestamp_utc)"
log "got REV of $REV"
fi
# add the "dirty flag" string
IS_DIRTY=$(is_git_repo_dirty)
if test "$IS_DIRTY" != "0"; then
log "git repo is dirty. adding now."
REV="${REV}${BUILD_REV_DIRTY_MARK}"
log "got REV of $REV"
fi
# add the hostname if needed
HOSTNAME_STR=$(get_hostname_str)
log "HOSTNAME STR $HOSTNAME_STR"
log "INCLUDE_HOSTNAME $BUILD_REV_INCLUDE_HOSTNAME"
log "INCLUDE_HOSTNAME_DEFAULT $BUILD_REV_INCLUDE_HOSTNAME_DEFAULT"
if test "$HOSTNAME_STR" != "$BUILD_REV_INCLUDE_HOSTNAME_DEFAULT" && test "$BUILD_REV_INCLUDE_HOSTNAME" = "1"; then
REV="${REV}@$HOSTNAME_STR"
fi
# Print it out
echo "$REV"
# Take them ack to where they came from
popd >/dev/null 2>&1 || kaboom "Could not cd back to starting directory.. Strange."
}
#=============================
# Shell helper functions
#-----------------------------
# Log something to start out if BUILD_REV_DEBUG isn't set to anything
function log () {
# dont log all the time
if [[ "${BUILD_REV_DEBUG:-}" = "1" ]]; then
echo "$@"
fi
}
# quit hapilly
function bye () {
exit 0
}
# print debug then quit
function kaboom () {
echo "${@:-Something exploded}"
exit 1
}
#==============================
# Program argument parsing
#-------------------------------
EMPTY_CMD="empty"
FIRST_ARG=${1:-}
CMD="${FIRST_ARG:-$EMPTY_CMD}"
# Command line parsing
if test "$CMD" = "debug"; then
print_func_debug
bye
fi
# Environment variable
if test "$CMD" = "env"; then
print_banner
print_env_vars
bye
fi
# Print the help menu
if [[ "$CMD" = "branch" ]]; then
log print_banner
echo "CURRENT TAG: \`$(git_branch)\`"
bye
fi
# Print the closest tag name
if [[ "$CMD" = "tag-closest" ]]; then
log print_banner
echo "CLOSEST TAG: \`$(git_closest_tag)\`"
bye
fi
# Print the tag name
if [[ "$CMD" = "tag" ]]; then
log print_banner
echo "CURRENT TAG: \`$(git_tag)\`"
bye
fi
# Print the help menu
if [[ "$CMD" =~ .*help$ ]]; then
print_banner
print_usage
print_format
print_examples
bye
fi
# see if we're dealing with noarg case or the named generate cmd
# offset the arg nums we pull the dir from
MAYBE_REPO_DIR=""
if test "$CMD" = "${EMPTYCMD:-}"; then
MAYBE_REPO_DIR="${1:-}"
elif test "$CMD" = "generate"; then
MAYBE_REPO_DIR="${2:-}"
fi
# If we probably directory, it was probably intentional
# So we overwrite what's ever set in BUILD_REV_REPO_DIR to that directory
# and let the git repo verification handle with blowing up if it needs it
if test -d "${MAYBE_REPO_DIR}"; then
BUILD_REV_REPO_DIR=$MAYBE_REPO_DIR
fi
# Check if it's a git repo first then echo out the revision number
# Otherwise blow up and print the directory they tried to stdout
if test "0" = "$(git_repo_exists "$BUILD_REV_REPO_DIR")"; then
generate_revision_str
bye
else
kaboom "the build directory is not a git repo \`$BUILD_REV_REPO_DIR\`. Exiting now..."
fi
# There's nothing left that we can do, so just blow up
kaboom "Invalid command $CMD \n $(print_help)"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment