Created
February 3, 2016 15:18
-
-
Save samdolan/b3738164a1dc1cd4ab4e to your computer and use it in GitHub Desktop.
A script for generating version numbers for your git repository
This file contains hidden or 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/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