Skip to content

Instantly share code, notes, and snippets.

@dmrolfs
Last active May 4, 2018 22:37
Show Gist options
  • Save dmrolfs/de481311f8f0d89c293270fee20d56a7 to your computer and use it in GitHub Desktop.
Save dmrolfs/de481311f8f0d89c293270fee20d56a7 to your computer and use it in GitHub Desktop.
Publish into Artifactory Repository
#!/usr/bin/env bash
# _ _ _ _
# _ __ _ _| |__ | (_)___| |__
# | '_ \| | | | '_ \| | / __| '_ \
# | |_) | |_| | |_) | | \__ \ | | |
# | .__/ \__,_|_.__/|_|_|___/_| |_|
# |_|
#
#
# Usage:
# Publish artifacts into prod or sit artifactory repositories
#
# Usage:
# publish [--options] [<arguments>]
# publish -h | --help
#
# Options:
# -h --help Display this help information.
# -d --dry-run Perform a dry run publish in order to verify options
# -u --user Set the artifactory username. If not specified
# $ARTIFACTORY_USER is used.
# -p --password Set the artifactory password. If not specified
# $ARTIFACTORY_PASS is used.
# -r --repo Target repository. Valid values include 'prod' or 'sit'
# 'sit' is default
# -o --org Organization component of maven coordinate
# -a --artifact Artifact ID component of maven coordinate
# -v --version Version component of maven coordinate
#
# Bash Boilerplate: https://github.com/alphabetum/bash-boilerplate
#
# Copyright (c) 2018 Damon Rolfs
###############################################################################
# Strict Mode
###############################################################################
# Treat unset variables and parameters other than the special parameters ‘@’ or
# ‘*’ as an error when performing parameter expansion. An 'unbound variable'
# error message will be written to the standard error, and a non-interactive
# shell will exit.
#
# This requires using parameter expansion to test for unset variables.
#
# http://www.gnu.org/software/bash/manual/bashref.html#Shell-Parameter-Expansion
#
# The two approaches that are probably the most appropriate are:
#
# ${parameter:-word}
# If parameter is unset or null, the expansion of word is substituted.
# Otherwise, the value of parameter is substituted. In other words, "word"
# acts as a default value when the value of "$parameter" is blank. If "word"
# is not present, then the default is blank (essentially an empty string).
#
# ${parameter:?word}
# If parameter is null or unset, the expansion of word (or a message to that
# effect if word is not present) is written to the standard error and the
# shell, if it is not interactive, exits. Otherwise, the value of parameter
# is substituted.
#
# Examples
# ========
#
# Arrays:
#
# ${some_array[@]:-} # blank default value
# ${some_array[*]:-} # blank default value
# ${some_array[0]:-} # blank default value
# ${some_array[0]:-default_value} # default value: the string 'default_value'
#
# Positional variables:
#
# ${1:-alternative} # default value: the string 'alternative'
# ${2:-} # blank default value
#
# With an error message:
#
# ${1:?'error message'} # exit with 'error message' if variable is unbound
#
# Short form: set -u
set -o nounset
# Exit immediately if a pipeline returns non-zero.
#
# NOTE: this has issues. When using read -rd '' with a heredoc, the exit
# status is non-zero, even though there isn't an error, and this setting
# then causes the script to exit. read -rd '' is synonymous to read -d $'\0',
# which means read until it finds a NUL byte, but it reaches the EOF (end of
# heredoc) without finding one and exits with a 1 status. Therefore, when
# reading from heredocs with set -e, there are three potential solutions:
#
# Solution 1. set +e / set -e again:
#
# set +e
# read -rd '' variable <<EOF
# EOF
# set -e
#
# Solution 2. <<EOF || true:
#
# read -rd '' variable <<EOF || true
# EOF
#
# Solution 3. Don't use set -e or set -o errexit at all.
#
# More information:
#
# https://www.mail-archive.com/bug-bash@gnu.org/msg12170.html
#
# Short form: set -e
set -o errexit
# Print a helpful message if a pipeline with non-zero exit code causes the
# script to exit as described above.
trap 'echo "Aborting due to errexit on line $LINENO. Exit code: $?" >&2' ERR
# Allow the above trap be inherited by all functions in the script.
#
# Short form: set -E
set -o errtrace
# Return value of a pipeline is the value of the last (rightmost) command to
# exit with a non-zero status, or zero if all commands in the pipeline exit
# successfully.
set -o pipefail
# Set $IFS to only newline and tab.
#
# http://www.dwheeler.com/essays/filenames-in-shell.html
IFS=$'\n\t'
###############################################################################
# Environment
###############################################################################
# $_ME
#
# Set to the program's basename.
_ME=$(basename "${0}")
###############################################################################
# Debug
###############################################################################
# _debug()
#
# Usage:
# _debug printf "Debug info. Variable: %s\n" "$0"
#
# A simple function for executing a specified command if the `$_USE_DEBUG`
# variable has been set. The command is expected to print a message and
# should typically be either `echo`, `printf`, or `cat`.
__DEBUG_COUNTER=0
_debug() {
if [[ "${_USE_DEBUG:-"0"}" -eq 1 ]]
then
__DEBUG_COUNTER=$((__DEBUG_COUNTER+1))
# Prefix debug message with "bug (U+1F41B)"
printf "🐛 %s " "${__DEBUG_COUNTER}"
"${@}"
printf "――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――\\n"
fi
}
# debug()
#
# Usage:
# debug "Debug info. Variable: $0"
#
# Print the specified message if the `$_USE_DEBUG` variable has been set.
#
# This is a shortcut for the _debug() function that simply echos the message.
debug() {
_debug echo "${@}"
}
###############################################################################
# Die
###############################################################################
#todo maybe convert to this set
# _yell() { echo "$0: $*" >&2; }
# die() { yell "$*"; exit 111; }
# try() { "$@" || die "cannot $*"; }
# _die()
#
# Usage:
# _die printf "Error message. Variable: %s\n" "$0"
#
# A simple function for exiting with an error after executing the specified
# command. The command is expected to print a message and should typically
# be either `echo`, `printf`, or `cat`.
_die() {
# Prefix die message with "cross mark (U+274C)", often displayed as a red x.
printf "❌ "
"${@}" 1>&2
exit 1
}
# die()
#
# Usage:
# die "Error message. Variable: $0"
#
# Exit with an error and print the specified message.
#
# This is a shortcut for the _die() function that simply echos the message.
die() {
_die echo "${@}"
}
###############################################################################
# Help
###############################################################################
# _print_help()
#
# Usage:
# _print_help
#
# Print the program help information.
_print_help() {
cat <<HEREDOC
_ _ _ _
_ __ _ _| |__ | (_)___| |__
| '_ \| | | | '_ \| | / __| '_ \
| |_) | |_| | |_) | | \__ \ | | |
| .__/ \__,_|_.__/|_|_|___/_| |_|
|_|
Publish artifacts into prod or sit artifactory repositories
Usage:
${_ME} [--options] [<arguments>]
${_ME} -h | --help
Options:
-h --help Display this help information.
-d --dry-run Perform a dry run publish in order to verify options
-u --user Set the artifactory username. If not specified
$ARTIFACTORY_USER is used.
-p --password Set the artifactory password. If not specified
$ARTIFACTORY_PASS is used.
-r --repo Target repository. Valid values include 'prod' or 'sit'
'sit' is default
-o --org Organization component of maven coordinate
-a --artifact Artifact ID component of maven coordinate
-v --version Version component of maven coordinate
HEREDOC
}
###############################################################################
# Options
###############################################################################
# Steps:
#
# 1. set expected short options in `optstring` at beginning of the "Normalize
# Options" section,
# 2. parse options in while loop in the "Parse Options" section.
# Normalize Options ###########################################################
# Source:
# https://github.com/e36freak/templates/blob/master/options
# The first loop, even though it uses 'optstring', will NOT check if an
# option that takes a required argument has the argument provided. That must
# be done within the second loop and case statement, yourself. Its purpose
# is solely to determine that -oARG is split into -o ARG, and not -o -A -R -G.
# Set short options -----------------------------------------------------------
# option string, for short options.
#
# Very much like getopts, expected short options should be appended to the
# string here. Any option followed by a ':' takes a required argument.
#
# In this example, `-x` and `-h` are regular short options, while `o` is
# assumed to have an argument and will be split if joined with the string,
# meaning `-oARG` would be split to `-o ARG`.
optstring=dr:o:a:v:u:p:
# Normalize -------------------------------------------------------------------
# iterate over options, breaking -ab into -a -b and --foo=bar into --foo bar
# also turns -- into --endopts to avoid issues with things like '-o-', the '-'
# should not indicate the end of options, but be an invalid option (or the
# argument to the option, such as wget -qO-)
unset options
# while the number of arguments is greater than 0
while ((${#}))
do
case ${1} in
# if option is of type -ab
-[!-]?*)
# loop over each character starting with the second
for ((i=1; i<${#1}; i++))
do
# extract 1 character from position 'i'
c=${1:i:1}
# add current char to options
options+=("-${c}")
# if option takes a required argument, and it's not the last char
# make the rest of the string its argument
if [[ ${optstring} = *"${c}:"* && ${1:i+1} ]]
then
options+=("${1:i+1}")
break
fi
done
;;
# if option is of type --foo=bar, split on first '='
--?*=*)
options+=("${1%%=*}" "${1#*=}")
;;
# end of options, stop breaking them up
--)
options+=(--endopts)
shift
options+=("${@}")
break
;;
# otherwise, nothing special
*)
options+=("${1}")
;;
esac
shift
done
# set new positional parameters to altered options. Set default to blank.
set -- "${options[@]:-}"
unset options
# Parse Options ###############################################################
_SIT_REPO="https://artifactory.in.here.com/artifactory/here-olp-sit"
_PROD_REPO="https://repo.platform.here.com/artifactory/open-location-platform"
# Initialize $_CMD, which can continue to be blank depending
# on what the program needs.
_CMD="publish"
# Initialize $_COMMAND_ARGV array
#
# This array contains all of the arguments that get passed along to each
# command. This is essentially the same as the program arguments, minus those
# that have been filtered out in the program option parsing loop. This array
# is initialized with $0, which is the program's name.
_COMMAND_ARGV=("${0}")
# Initialize program option variables.
_PRINT_HELP=0
_USE_DEBUG=0
# Initialize additional expected option variables.
_IS_DRY_RUN=0
_ACTIVE_REPO=${_SIT_REPO}
_USERNAME=${ARTIFACTORY_USER}
_PASSWORD=${ARTIFACTORY_PASS}
_ORGANIZATION=""
_ARTIFACT_NAME=""
_VERSION=""
# _require_argument()
#
# Usage:
# _require_argument <option> <argument>
#
# If <argument> is blank or another option, print an error message and exit
# with status 1.
_require_argument() {
# Set local variables from arguments.
#
# NOTE: 'local' is a non-POSIX bash feature and keeps the variable local to
# the block of code, as defined by curly braces. It's easiest to just think
# of them as local to a function.
local _option="${1:-}"
local _argument="${2:-}"
if [[ -z "${_argument}" ]] || [[ "${_argument}" =~ ^- ]]
then
_die printf "Option requires a argument: %s\\n" "${_option}"
fi
}
# getopts and getopts have inconsistent behavior, so using a simple home-brewed
# while loop. This isn't perfectly compliant with POSIX, but it's close enough
# and this appears to be a widely used approach.
#
# More info:
# http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html
# http://stackoverflow.com/a/14203146
# http://stackoverflow.com/a/7948533
while [ ${#} -gt 0 ]
do
__option="${1:-}"
__maybe_param="${2:-}"
debug "option:[${__option}] maybe_param:[${__maybe_param}]"
case "${__option}" in
-h|--help)
_PRINT_HELP=1
;;
--debug)
_USE_DEBUG=1
;;
-d|--dry-run)
_IS_DRY_RUN=1
;;
-u|--user)
_require_argument "${__option}" "${__maybe_param}"
_USERNAME="${__maybe_param}"
shift
;;
-p|--password)
_require_argument "${__option}" "${__maybe_param}"
_PASSWORD="${__maybe_param}"
shift
;;
-r|--repo)
_require_argument "${__option}" "${__maybe_param}"
case "${__maybe_param}" in
"sit") _ACTIVE_REPO=${_SIT_REPO};;
"prod") _ACTIVE_REPO=${_PROD_REPO};;
*) die "expected repo option: sit or prod\n"
esac
shift
;;
-o|--org)
_require_argument "${__option}" "${__maybe_param}"
_ORGANIZATION="${__maybe_param}"
shift
;;
-a|--artifact)
_require_argument "${__option}" "${__maybe_param}"
_ARTIFACT_NAME="${__maybe_param}"
shift
;;
-v|--version)
_require_argument "${__option}" "${__maybe_param}"
_VERSION="${__maybe_param}"
shift
;;
--endopts)
# Terminate option parsing.
break
;;
*)
debug "non-option: ${__option}"
# The first non-option argument is assumed to be the command name.
# All subsequent arguments are added to $_COMMAND_ARGV.
if [[ -n ${_CMD} ]]
then
debug "adding ${__option} to ${_COMMAND_ARGV}"
_COMMAND_ARGV+=("${__option}")
debug "resulting _COMMAND_ARGV:\n${_COMMAND_ARGV[*]}"
else
_CMD="${__option}"
debug "setting command to be: ${_CMD}"
fi
# _die printf "Unexpected option: %s\\n" "${__option}"
;;
esac
shift
done
# Set $_COMMAND_PARAMETERS to $_COMMAND_ARGV, minus the initial element, $0. This
# provides an array that is equivalent to $* and $@ within each command
# function, though the array is zero-indexed, which could lead to confusion.
#
# Use `unset` to remove the first element rather than slicing (e.g.,
# `_COMMAND_PARAMETERS=("${_COMMAND_ARGV[@]:1}")`) because under bash 3.2 the
# resulting slice is treated as a quoted string and doesn't easily get coaxed
# into a new array.
_COMMAND_PARAMETERS=(${_COMMAND_ARGV[*]})
unset "_COMMAND_PARAMETERS[0]"
_debug printf \
"\${_CMD}: %s\\n" \
"${_CMD}"
_debug printf \
"\${_COMMAND_ARGV[*]}:\\n%s\\n" \
"${_COMMAND_ARGV[*]}"
_debug printf \
"\${_COMMAND_PARAMETERS[*]:-}:\\n%s\\n" \
"${_COMMAND_PARAMETERS[*]:-}"
###############################################################################
# Program Functions
###############################################################################
_path_for() {
${1//.//}
# sed -e 's/c/z/' $1
}
_simple() {
local org_path=${_ORGANIZATION//.//}
debug "simple org path=${org_path}"
_debug printf ">> Performing operation...\\n"
# shopt -s globstar
for file in ${_COMMAND_PARAMETERS[*]}; do
local name=$(basename ${file})
local url="${_ACTIVE_REPO}/${org_path}/${_ARTIFACT_NAME}/${_VERSION}/${name}"
printf "\nPublishing ${_ORGANIZATION}:${_ARTIFACT_NAME}:${_VERSION} to ${url} :\n"
if [[ "${_IS_DRY_RUN:-"0"}" -eq 0 ]]
then
curl -u ${_USERNAME}:${_PASSWORD} -X PUT "${url}" -T ${file};
printf "\n"
fi
done
# if ((_OPTION_X))
# then
# printf "Perform a simple operation with --option-x.\\n"
# else
# printf "Perform a simple operation.\\n"
# fi
# if [[ -n "${_SHORT_OPTION_WITH_PARAMETER}" ]]
# then
# printf "Short option parameter: %s\\n" "${_SHORT_OPTION_WITH_PARAMETER}"
# fi
# if [[ -n "${_LONG_OPTION_WITH_PARAMETER}" ]]
# then
# printf "Long option parameter: %s\\n" "${_LONG_OPTION_WITH_PARAMETER}"
# fi
}
###############################################################################
# Main
###############################################################################
# _main()
#
# Usage:
# _main [<options>] [<arguments>]
#
# Description:
# Entry point for the program, handling basic option parsing and dispatching.
_main() {
if ((_PRINT_HELP))
then
_print_help
else
_simple "$@"
fi
}
# Call `_main` after everything has been defined.
_main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment