433 lines
12 KiB
Bash
Executable File
433 lines
12 KiB
Bash
Executable File
# last-modified: Tue Jan 14 20:37:58 UTC 2020
|
|
#!/usr/bin/env bash
|
|
|
|
# Path to file used for output redirect
|
|
# and extracting messages for warning and
|
|
# failure information sent to slack
|
|
function output_file() {
|
|
printf "/tmp/.ci-output"
|
|
}
|
|
|
|
# Write failure message, send error to configured
|
|
# slack, and exit with non-zero status. If an
|
|
# "$(output_file)" file exists, the last 5 lines will be
|
|
# included in the slack message.
|
|
#
|
|
# $1: Failure message
|
|
function fail() {
|
|
(>&2 echo "ERROR: ${1}")
|
|
if [ -f ""$(output_file)"" ]; then
|
|
slack -s error -m "ERROR: ${1}" -f "$(output_file)" -T 5
|
|
else
|
|
slack -s error -m "ERROR: ${1}"
|
|
fi
|
|
exit 1
|
|
}
|
|
|
|
# Write warning message, send warning to configured
|
|
# slack
|
|
#
|
|
# $1: Warning message
|
|
function warn() {
|
|
(>&2 echo "WARN: ${1}")
|
|
if [ -f ""$(output_file)"" ]; then
|
|
slack -s warn -m "WARNING: ${1}" -f "$(output_file)"
|
|
else
|
|
slack -s warn -m "WARNING: ${1}"
|
|
fi
|
|
}
|
|
|
|
# Execute command while redirecting all output to
|
|
# a file (file is used within fail mesage on when
|
|
# command is unsuccessful). Final argument is the
|
|
# error message used when the command fails.
|
|
#
|
|
# $@{1:$#-1}: Command to execute
|
|
# $@{$#}: Failure message
|
|
function wrap() {
|
|
i=$(("${#}" - 1))
|
|
wrap_raw "${@:1:$i}"
|
|
if [ $? -ne 0 ]; then
|
|
cat "$(output_file)"
|
|
fail "${@:$#}"
|
|
fi
|
|
rm "$(output_file)"
|
|
}
|
|
|
|
# Execute command while redirecting all output to
|
|
# a file. Exit status is returned.
|
|
function wrap_raw() {
|
|
rm -f "$(output_file)"
|
|
"${@}" > "$(output_file)" 2>&1
|
|
return $?
|
|
}
|
|
|
|
# Execute command while redirecting all output to
|
|
# a file (file is used within fail mesage on when
|
|
# command is unsuccessful). Command output will be
|
|
# streamed during execution. Final argument is the
|
|
# error message used when the command fails.
|
|
#
|
|
# $@{1:$#-1}: Command to execute
|
|
# $@{$#}: Failure message
|
|
function wrap_stream() {
|
|
i=$(("${#}" - 1))
|
|
wrap_stream_raw "${@:1:$i}"
|
|
if [ $? -ne 0 ]; then
|
|
fail "${@:$#}"
|
|
fi
|
|
rm "$(output_file)"
|
|
}
|
|
|
|
# Execute command while redirecting all output
|
|
# to a file. Command output will be streamed
|
|
# during execution. Exit status is returned
|
|
function wrap_stream_raw() {
|
|
rm -f "$(output_file)"
|
|
"${@}" > "$(output_file)" 2>&1 &
|
|
pid=$!
|
|
until [ -f "$(output_file)" ]; do
|
|
sleep 0.1
|
|
done
|
|
tail -f --quiet --pid "${pid}" "$(output_file)"
|
|
wait "${pid}"
|
|
return $?
|
|
}
|
|
|
|
|
|
# Send command to packet device and wrap
|
|
# execution
|
|
# $@{1:$#-1}: Command to execute
|
|
# $@{$#}: Failure message
|
|
function pkt_wrap() {
|
|
wrap packet-exec run -quiet -- "${@}"
|
|
}
|
|
|
|
# Send command to packet device and wrap
|
|
# execution
|
|
# $@: Command to execute
|
|
function pkt_wrap_raw() {
|
|
wrap_raw packet-exec run -quiet -- "${@}"
|
|
}
|
|
|
|
# Send command to packet device and wrap
|
|
# execution with output streaming
|
|
# $@{1:$#-1}: Command to execute
|
|
# $@{$#}: Failure message
|
|
function pkt_wrap_stream() {
|
|
wrap_stream packet-exec run -quiet -- "${@}"
|
|
}
|
|
|
|
# Send command to packet device and wrap
|
|
# execution with output streaming
|
|
# $@: Command to execute
|
|
function pkt_wrap_stream_raw() {
|
|
wrap_stream_raw packet-exec run -quiet -- "${@}"
|
|
}
|
|
|
|
# Generates location within the asset storage
|
|
# bucket to retain built assets.
|
|
function asset_location() {
|
|
if [ "${tag}" = "" ]; then
|
|
dst="${ASSETS_PRIVATE_LONGTERM}/${repository}/${ident_ref}/${short_sha}"
|
|
else
|
|
if [[ "${tag}" = *"+"* ]]; then
|
|
dst="${ASSETS_PRIVATE_LONGTERM}/${repository}/${tag}"
|
|
else
|
|
dst="${ASSETS_PRIVATE_BUCKET}/${repository}/${tag}"
|
|
fi
|
|
fi
|
|
echo -n "${dst}"
|
|
}
|
|
|
|
# Upload assets to the asset storage bucket.
|
|
#
|
|
# $1: Path to asset file or directory to upload
|
|
function upload_assets() {
|
|
if [ "${1}" = "" ]; then
|
|
fail "Parameter required for asset upload"
|
|
fi
|
|
if [ -d "${1}" ]; then
|
|
wrap aws s3 cp --recursive "${1}" "$(asset_location)/" \
|
|
"Upload to asset storage failed"
|
|
else
|
|
wrap aws s3 cp "${1}" "$(asset_location)/" \
|
|
"Upload to asset storage failed"
|
|
fi
|
|
}
|
|
|
|
# Download assets from the asset storage bucket. If
|
|
# destination is not provided, remote path will be
|
|
# used locally.
|
|
#
|
|
# $1: Path to asset or directory to download
|
|
# $2: Optional destination for downloaded assets
|
|
function download_assets() {
|
|
if [ "${1}" = "" ]; then
|
|
fail "At least one parameter required for asset download"
|
|
fi
|
|
if [ "${2}" = "" ]; then
|
|
dst="${1#/}"
|
|
else
|
|
dst="${2}"
|
|
fi
|
|
mkdir -p "${dst}"
|
|
src="$(asset_location)/${1#/}"
|
|
remote=$(aws s3 ls "${src}")
|
|
if [[ "${remote}" = *" PRE "* ]]; then
|
|
mkdir -p "${dst}"
|
|
wrap aws s3 cp --recursive "${src%/}/" "${dst}" \
|
|
"Download from asset storage failed"
|
|
else
|
|
mkdir -p "$(dirname "${dst}")"
|
|
wrap aws s3 cp "${src}" "${dst}" \
|
|
"Download from asset storage failed"
|
|
fi
|
|
}
|
|
|
|
# Upload assets to the cache storage bucket.
|
|
#
|
|
# $1: Path to asset file or directory to upload
|
|
function upload_cache() {
|
|
if [ "${1}" = "" ]; then
|
|
fail "Parameter required for cache upload"
|
|
fi
|
|
if [ -d "${1}" ]; then
|
|
wrap aws s3 cp --recursive "${1}" "${asset_cache}/" \
|
|
"Upload to cache failed"
|
|
else
|
|
wrap aws s3 cp "${1}" "${asset_cache}/" \
|
|
"Upload to cache failed"
|
|
fi
|
|
}
|
|
|
|
# Download assets from the cache storage bucket. If
|
|
# destination is not provided, remote path will be
|
|
# used locally.
|
|
#
|
|
# $1: Path to asset or directory to download
|
|
# $2: Optional destination for downloaded assets
|
|
function download_cache() {
|
|
if [ "${1}" = "" ]; then
|
|
fail "At least one parameter required for cache download"
|
|
fi
|
|
if [ "${2}" = "" ]; then
|
|
dst="${1#/}"
|
|
else
|
|
dst="${2}"
|
|
fi
|
|
mkdir -p "${dst}"
|
|
src="${asset_cache}/${1#/}"
|
|
remote=$(aws s3 ls "${src}")
|
|
if [[ "${remote}" = *" PRE "* ]]; then
|
|
mkdir -p "${dst}"
|
|
wrap aws s3 cp --recursive "${src%/}/" "${dst}" \
|
|
"Download from cache storage failed"
|
|
else
|
|
mkdir -p "$(dirname "${dst}")"
|
|
wrap aws s3 cp "${src}" "${dst}" \
|
|
"Download from cache storage failed"
|
|
fi
|
|
}
|
|
|
|
# Validate arguments for GitHub release. Checks for
|
|
# two arguments and that second argument is an exiting
|
|
# file asset, or directory.
|
|
#
|
|
# $1: GitHub tag name
|
|
# $2: Asset file or directory of assets
|
|
function release_validate() {
|
|
if [ "${1}" = "" ]; then
|
|
fail "Missing required position 1 argument (TAG) for release"
|
|
fi
|
|
if [ "${2}" = "" ]; then
|
|
fail "Missing required position 2 argument (PATH) for release"
|
|
fi
|
|
if [ ! -e "${2}" ]; then
|
|
fail "Path provided for release (${2}) does not exist"
|
|
fi
|
|
}
|
|
|
|
# Generate a GitHub release
|
|
#
|
|
# $1: GitHub tag name
|
|
# $2: Asset file or directory of assets
|
|
function release() {
|
|
release_validate "${@}"
|
|
wrap_raw ghr -u "${repo_owner}" -r "${repo_name}" -c "${full_sha}" -n "${1}" -delete
|
|
if [ $? -ne 0 ]; then
|
|
wrap ghr -u "${repo_owner}" -r "${repo_name}" -c "${full_sha}" -n "${1}" \
|
|
"${1}" "${2}" "Failed to create release for version ${1}"
|
|
fi
|
|
}
|
|
|
|
# Generate a GitHub prerelease
|
|
#
|
|
# $1: GitHub tag name
|
|
# $2: Asset file or directory of assets
|
|
function prerelease() {
|
|
release_validate "${@}"
|
|
if [[ "${1}" != *"+"* ]]; then
|
|
ptag="${1}+${short_sha}"
|
|
else
|
|
ptag="${1}"
|
|
fi
|
|
|
|
wrap_raw ghr -u "${repo_owner}" -r "${repo_name}" -c "${full_sha}" -n "${ptag}" \
|
|
-delete -prerelease "${ptag}" "${2}"
|
|
if [ $? -ne 0 ]; then
|
|
wrap ghr -u "${repo_owner}" -r "${repo_name}" -c "${full_sha}" -n "${ptag}" \
|
|
-prerelease "${ptag}" "${2}" \
|
|
"Failed to create prerelease for version ${1}"
|
|
fi
|
|
echo -n "${ptag}"
|
|
}
|
|
|
|
# Check if version string is valid for release
|
|
#
|
|
# $1: Version
|
|
# Returns: 0 if valid, 1 if invalid
|
|
function valid_release_version() {
|
|
if [[ "${1}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Validate arguments for HashiCorp release. Ensures asset
|
|
# directory exists, and checks that the SHASUMS and SHASUM.sig
|
|
# files are present.
|
|
#
|
|
# $1: Asset directory
|
|
function hashicorp_release_validate() {
|
|
directory="${1}"
|
|
|
|
# Directory checks
|
|
if [ "${directory}" = "" ]; then
|
|
fail "No asset directory was provided for HashiCorp release"
|
|
fi
|
|
if [ ! -d "${directory}" ]; then
|
|
fail "Asset directory for HashiCorp release does not exist"
|
|
fi
|
|
|
|
# SHASUMS checks
|
|
if [ ! -e "${directory}/"*SHASUMS ]; then
|
|
fail "Asset directory is missing SHASUMS file"
|
|
fi
|
|
if [ ! -e "${directory}/"*SHASUMS.sig ]; then
|
|
fail "Asset directory is missing SHASUMS signature file"
|
|
fi
|
|
}
|
|
|
|
# Verify release assets by validating checksum properly match
|
|
# and that signature file is valid
|
|
#
|
|
# $1: Asset directory
|
|
function hashicorp_release_verify() {
|
|
directory="${1}"
|
|
pushd "${directory}" > "${output}"
|
|
|
|
# First do a checksum validation
|
|
wrap shasum -a 256 -c *_SHA256SUMS \
|
|
"Checksum validation of release assets failed"
|
|
# Next check that the signature is valid
|
|
gpghome=$(mktemp -qd)
|
|
export GNUPGHOME="${gpghome}"
|
|
wrap gpg --import "${HASHICORP_PUBLIC_GPG_KEY}" \
|
|
"Failed to import HashiCorp public GPG key"
|
|
wrap gpg --verify *SHA256SUMS.sig *SHA256SUMS \
|
|
"Validation of SHA256SUMS signature failed"
|
|
rm -rf "${gpghome}" > "${output}" 2>&1
|
|
popd > "${output}"
|
|
}
|
|
|
|
# Generate a HashiCorp release
|
|
#
|
|
# $1: Asset directory
|
|
function hashicorp_release() {
|
|
directory="${1}"
|
|
|
|
hashicorp_release_validate "${directory}"
|
|
hashicorp_release_verify "${directory}"
|
|
|
|
oid="${AWS_ACCESS_KEY_ID}"
|
|
okey="${AWS_SECRET_ACCESS_KEY}"
|
|
export AWS_ACCESS_KEY_ID="${RELEASE_AWS_ACCESS_KEY_ID}"
|
|
export AWS_SECRET_ACCESS_KEY="${RELEASE_AWS_SECRET_ACCESS_KEY}"
|
|
|
|
wrap_stream hc-releases upload "${directory}" \
|
|
"Failed to upload HashiCorp release assets"
|
|
wrap_stream hc-releases publish \
|
|
"Failed to publish HashiCorp release"
|
|
|
|
export AWS_ACCESS_KEY_ID="${oid}"
|
|
export AWS_SECRET_ACCESS_KEY="${okey}"
|
|
}
|
|
|
|
# Configures git for hashibot usage
|
|
function hashibot_git() {
|
|
wrap git config user.name "${HASHIBOT_USERNAME}" \
|
|
"Failed to setup git for hashibot usage (username)"
|
|
wrap git config user.email "${HASHIBOT_EMAIL}" \
|
|
"Failed to setup git for hashibot usage (email)"
|
|
wrap git remote set-url origin "https://${HASHIBOT_USERNAME}:${HASHIBOT_TOKEN}@github.com/${repository}" \
|
|
"Failed to setup git for hashibot usage (remote)"
|
|
}
|
|
|
|
# Stub cleanup method which can be redefined
|
|
# within actual script
|
|
function cleanup() {
|
|
(>&2 echo "** No cleanup tasks defined")
|
|
}
|
|
|
|
trap cleanup EXIT
|
|
|
|
# Enable debugging. This needs to be enabled with
|
|
# extreme caution when used on public repositories.
|
|
# Output with debugging enabled will likely include
|
|
# secret values which should not be publicly exposed.
|
|
#
|
|
# If repository is public, FORCE_PUBLIC_DEBUG environment
|
|
# variable must also be set.
|
|
|
|
is_private=$(curl -H "Authorization: token ${HASHIBOT_TOKEN}" -s "https://api.github.com/repos/${GITHUB_REPOSITORY}" | jq .private)
|
|
|
|
if [ "${DEBUG}" != "" ]; then
|
|
if [ "${is_private}" = "false" ]; then
|
|
if [ "${FORCE_PUBLIC_DEBUG}" != "" ]; then
|
|
set -x
|
|
output="/dev/stdout"
|
|
else
|
|
fail "Cannot enable debug mode on public repository unless forced"
|
|
fi
|
|
else
|
|
set -x
|
|
output="/dev/stdout"
|
|
fi
|
|
else
|
|
output="/dev/null"
|
|
fi
|
|
|
|
# Check if we are running a public repository on private runners
|
|
if [ "${VAGRANT_PRIVATE}" != "" ] && [ "${is_private}" = "false" ]; then
|
|
fail "Cannot run public repositories on private Vagrant runners. Disable runners now!"
|
|
fi
|
|
|
|
# Common variables
|
|
full_sha="${GITHUB_SHA}"
|
|
short_sha="${full_sha:0:8}"
|
|
ident_ref="${GITHUB_REF#*/*/}"
|
|
if [[ "${GITHUB_REF}" == *"refs/tags/"* ]]; then
|
|
tag="${GITHUB_REF##*tags/}"
|
|
valid_release_version "${tag}"
|
|
if [ $? -eq 0 ]; then
|
|
release=1
|
|
fi
|
|
fi
|
|
repository="${GITHUB_REPOSITORY}"
|
|
repo_owner="${repository%/*}"
|
|
repo_name="${repository#*/}"
|
|
asset_cache="${ASSETS_PRIVATE_SHORTTERM}/${repository}/${GITHUB_ACTION}"
|
|
job_id="${GITHUB_ACTION}"
|