# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: BUSL-1.1
function tag_release {
# Arguments:
# $1 - Path to top level consul source
# $2 - Version string to use for tagging the release
# $3 - Alternative GPG key id used for signing the release commit (optional)
#
# Returns:
# 0 - success
# * - error
#
# Notes:
# If the RELEASE_UNSIGNED environment variable is set then no gpg signing will occur
if ! test -d " $1 "
then
err " ERROR: ' $1 ' is not a directory. tag_release must be called with the path to the top level source as the first argument' "
return 1
fi
if test -z " $2 "
then
err "ERROR: tag_release must be called with a version number as the second argument"
return 1
fi
# determine whether the gpg key to use is being overridden
local gpg_key = ${ HASHICORP_GPG_KEY }
if test -n " $3 "
then
gpg_key = $3
fi
pushd " $1 " > /dev/null
local ret = 0
local branch_to_tag = $( git_branch) || ret = 1
# perform an usngined release if requested (mainly for testing locally)
if test ${ ret } -ne 0
then
err "ERROR: Failed to determine git branch to tag"
elif is_set " $RELEASE_UNSIGNED "
then
(
git commit --allow-empty -a -m " Release v ${ 2 } " &&
git tag -a -m " Version ${ 2 } " " v ${ 2 } " " ${ branch_to_tag } "
)
ret = $?
# perform a signed release (official releases should do this)
elif have_gpg_key ${ gpg_key }
then
(
git commit --allow-empty -a --gpg-sign= ${ gpg_key } -m " Release v ${ 2 } " &&
git tag -a -m " Version ${ 2 } " -s -u ${ gpg_key } " v ${ 2 } " " ${ branch_to_tag } "
)
ret = $?
# unsigned release not requested and gpg key isn't useable
else
err " ERROR: GPG key ${ gpg_key } is not in the local keychain - to continue set RELEASE_UNSIGNED=1 in the env "
ret = 1
fi
popd > /dev/null
return $ret
}
function package_binaries {
# Arguments:
# $1 - Path to the directory containing the built binaries
# $2 - Destination path of the packaged binaries
# $3 - Version
#
# Returns:
# 0 - success
# * - error
local sdir = " $1 "
local ddir = " $2 "
local vers = " $3 "
local ret = 0
if ! test -d " ${ sdir } "
then
err " ERROR: ' $1 ' is not a directory. package_binaries must be called with the path to the directory containing the binaries "
return 1
fi
rm -rf " ${ ddir } " > /dev/null 2>& 1
mkdir -p " ${ ddir } " >/dev/null 2>& 1
for platform in $( find " ${ sdir } " -mindepth 1 -maxdepth 1 -type d )
do
local os_arch = $( basename $platform )
local dest = " ${ ddir } / ${ CONSUL_PKG_NAME } _ ${ vers } _ ${ os_arch } .zip "
status " Compressing ${ os_arch } directory into ${ dest } "
pushd " ${ platform } " > /dev/null
zip " ${ ddir } / ${ CONSUL_PKG_NAME } _ ${ vers } _ ${ os_arch } .zip " ./*
ret = $?
popd > /dev/null
if test " $ret " -ne 0
then
break
fi
done
return ${ ret }
}
function package_release_one {
# Arguments:
# $1 - Path to the top level Consul source
# $2 - Version to use in the names of the zip files (optional)
# $3 - Subdirectory under pkg/dist to use (optional)
#
# Returns:
# 0 - success
# * - error
if ! test -d " $1 "
then
err " ERROR: ' $1 ' is not a directory. package_release must be called with the path to the top level source as the first argument' "
return 1
fi
local sdir = " $1 "
local ret = 0
local vers = " $2 "
local extra_dir_name = " $3 "
local extra_dir = ""
if test -n " ${ extra_dir_name } "
then
extra_dir = " ${ extra_dir_name } / "
fi
if test -z " ${ vers } "
then
vers = $( get_version " ${ sdir } " true false )
ret = $?
if test " $ret " -ne 0
then
err "ERROR: failed to determine the version."
return $ret
fi
fi
package_binaries " ${ sdir } /pkg/bin/ ${ extra_dir } " " ${ sdir } /pkg/dist/ ${ extra_dir } " " ${ vers } "
return $?
}
function package_release {
# Arguments:
# $1 - Path to the top level Consul source
# $2 - Version to use in the names of the zip files (optional)
#
# Returns:
# 0 - success
# * - error
package_release_one " $1 " " $2 " ""
return $?
}
function shasum_release {
# Arguments:
# $1 - Path to the dist directory
# $2 - Version of the release
#
# Returns:
# 0 - success
# * - failure
local sdir = " $1 "
local vers = " $2 "
if ! test -d " $1 "
then
err "ERROR: sign_release requires a path to the dist dir as the first argument"
return 1
fi
if test -z " ${ vers } "
then
err "ERROR: sign_release requires a version to be specified as the second argument"
return 1
fi
local hfile = " ${ CONSUL_PKG_NAME } _ ${ vers } _SHA256SUMS "
shasum_directory " ${ sdir } " " ${ sdir } / ${ hfile } "
return $?
}
function sign_release {
# Arguments:
# $1 - Path to distribution directory
# $2 - Version
# $2 - Alternative GPG key to use for signing
#
# Returns:
# 0 - success
# * - failure
local sdir = " $1 "
local vers = " $2 "
if ! test -d " ${ sdir } "
then
err "ERROR: sign_release requires a path to the dist dir as the first argument"
return 1
fi
if test -z " ${ vers } "
then
err "ERROR: sign_release requires a version to be specified as the second argument"
return 1
fi
local hfile = " ${ CONSUL_PKG_NAME } _ ${ vers } _SHA256SUMS "
status_stage " ==> Signing ${ hfile } "
gpg_detach_sign " ${ 1 } / ${ hfile } " " $3 " || return 1
return 0
}
function check_release_one {
# Arguments:
# $1 - Path to the release files
# $2 - Version to expect
# $3 - boolean whether to expect the signature file
# $4 - Release Name (optional)
#
# Returns:
# 0 - success
# * - failure
declare -i ret = 0
declare -a expected_files
declare log_extra = ""
if test -n " $4 "
then
log_extra = " for $4 "
fi
expected_files += ( " ${ CONSUL_PKG_NAME } _ ${ 2 } _SHA256SUMS " )
echo " check sig: $3 "
if is_set " $3 "
then
expected_files += ( " ${ CONSUL_PKG_NAME } _ ${ 2 } _SHA256SUMS.sig " )
fi
expected_files += ( " ${ CONSUL_PKG_NAME } _ ${ 2 } _darwin_386.zip " )
expected_files += ( " ${ CONSUL_PKG_NAME } _ ${ 2 } _darwin_amd64.zip " )
expected_files += ( " ${ CONSUL_PKG_NAME } _ ${ 2 } _freebsd_386.zip " )
expected_files += ( " ${ CONSUL_PKG_NAME } _ ${ 2 } _freebsd_amd64.zip " )
expected_files += ( " ${ CONSUL_PKG_NAME } _ ${ 2 } _linux_386.zip " )
expected_files += ( " ${ CONSUL_PKG_NAME } _ ${ 2 } _linux_amd64.zip " )
expected_files += ( " ${ CONSUL_PKG_NAME } _ ${ 2 } _linux_arm.zip " )
expected_files += ( " ${ CONSUL_PKG_NAME } _ ${ 2 } _linux_arm64.zip " )
expected_files += ( " ${ CONSUL_PKG_NAME } _ ${ 2 } _solaris_amd64.zip " )
expected_files += ( " ${ CONSUL_PKG_NAME } _ ${ 2 } _windows_386.zip " )
expected_files += ( " ${ CONSUL_PKG_NAME } _ ${ 2 } _windows_amd64.zip " )
declare -a found_files
status_stage " ==> Verifying release contents ${ log_extra } - ${ 2 } "
debug "Expecting Files:"
for fname in " ${ expected_files [@] } "
do
debug " $fname "
done
pushd " $1 " > /dev/null
for actual_fname in $( ls)
do
local found = 0
for i in " ${ !expected_files[@] } "
do
local expected_fname = " ${ expected_files [i] } "
if test " ${ expected_fname } " = = " ${ actual_fname } "
then
# remove from the expected_files array
unset 'expected_files[i]'
# append to the list of found files
found_files += ( " ${ expected_fname } " )
# mark it as found so we dont error
found = 1
break
fi
done
if test $found -ne 1
then
err " ERROR: Release build has an extra file: ${ actual_fname } "
ret = 1
fi
done
for fname in " ${ expected_files [@] } "
do
err " ERROR: Release build is missing a file: $fname "
ret = 1
done
if test $ret -eq 0
then
if ! shasum -c -s " ${ CONSUL_PKG_NAME } _ ${ 2 } _SHA256SUMS "
then
err "ERROR: Failed SHA-256 hash verification"
shasum -c " ${ CONSUL_PKG_NAME } _ ${ 2 } _SHA256SUMS "
ret = 1
fi
fi
if test $ret -eq 0 && is_set " ${ 3 } "
then
if ! gpg --verify " ${ CONSUL_PKG_NAME } _ ${ 2 } _SHA256SUMS.sig " " ${ CONSUL_PKG_NAME } _ ${ 2 } _SHA256SUMS " > /dev/null 2>& 1
then
err "ERROR: Failed GPG verification of SHA256SUMS signature"
ret = 1
fi
fi
if test $ret -eq 0
then
status "Release build contents:"
for fname in " ${ found_files [@] } "
do
echo " $fname "
done
fi
popd > /dev/null
return $ret
}
function check_release {
# Arguments:
# $1 - Path to the release files
# $2 - Version to expect
# $3 - boolean whether to expect the signature file
#
# Returns:
# 0 - success
# * - failure
check_release_one " $1 " " $2 " " $3 "
return ${ ret }
}
function build_consul_release {
build_consul " $1 " "" " $2 "
}
function build_release {
# Arguments: (yeah there are lots)
# $1 - Path to the top level Consul source
# $2 - boolean whether to tag the release yet
# $3 - boolean whether to build the binaries
# $4 - boolean whether to generate the sha256 sums
# $5 - version to set within version.go and the changelog
# $6 - release date to set within the changelog
# $7 - release version to set
# $8 - alternative gpg key to use for signing operations (optional)
#
# Returns:
# 0 - success
# * - error
debug " Source Dir: $1 "
debug " Tag Release: $2 "
debug " Build Release: $3 "
debug " Sign Release: $4 "
debug " Version: $5 "
debug " Release Date: $6 "
debug " Release Vers: $7 "
debug " GPG Key: $8 "
if ! test -d " $1 "
then
err " ERROR: ' $1 ' is not a directory. build_release must be called with the path to the top level source as the first argument' "
return 1
fi
if test -z " $2 " -o -z " $3 " -o -z " $4 "
then
err "ERROR: build_release requires 4 arguments to be specified: <path to consul source> <tag release bool?> <build binaries bool?> <shasum 256 bool?>"
return 1
fi
local sdir = " $1 "
local do_tag = " $2 "
local do_build = " $3 "
local do_sha256 = " $4 "
local gpg_key = " $8 "
if test -z " ${ gpg_key } "
then
gpg_key = ${ HASHICORP_GPG_KEY }
fi
if ! is_set " ${ RELEASE_UNSIGNED } "
then
if ! have_gpg_key " ${ gpg_key } "
then
err "ERROR: Aborting build because no useable GPG key is present. Set RELEASE_UNSIGNED=1 to bypass this check"
return 1
fi
fi
if ! is_git_clean " ${ sdir } " true && ! is_set " ${ ALLOW_DIRTY_GIT } "
then
err "ERROR: Refusing to build because Git is dirty. Set ALLOW_DIRTY_GIT=1 in the environment to proceed anyways"
return 1
fi
local set_vers = " $5 "
local set_date = " $6 "
local set_release = " $7 "
if test -z " ${ set_vers } "
then
set_vers = $( get_version " ${ sdir } " false false )
set_release = $( parse_version " ${ sdir } " true false true )
fi
if is_set " ${ do_tag } " && ! set_release_mode " ${ sdir } " " ${ set_vers } " " ${ set_date } " " ${ set_release } "
then
err "ERROR: Failed to put source into release mode"
return 1
fi
local vers = " $( get_version ${ sdir } true false ) "
if test $? -ne 0
then
err "Please specify a version (couldn't find one based on build tags)."
return 1
fi
# Make sure we arent in dev mode
unset CONSUL_DEV
if is_set " ${ do_build } "
then
status_stage "==> Refreshing Docker Build Images"
refresh_docker_images " ${ sdir } "
if test $? -ne 0
then
err "ERROR: Failed to refresh docker images"
return 1
fi
status_stage " ==> Building UI for version ${ vers } "
# passing the version to override the version determined via tags
build_ui " ${ sdir } " " ${ UI_BUILD_TAG } " " ${ vers } "
if test $? -ne 0
then
err "ERROR: Failed to build the ui"
return 1
fi
if is_set " ${ do_tag } "
then
git add " ${ sdir } /agent/uiserver/dist "
if test $? -ne 0
then
err "ERROR: Failed to git add /agent/uiserver/dist directory"
return 1
fi
fi
status " UI Built with Version: $( ui_version " ${ sdir } /agent/uiserver/dist/index.html " ) "
fi
if is_set " ${ do_tag } "
then
status_stage " ==> Tagging version ${ vers } "
tag_release " ${ sdir } " " ${ vers } " " ${ gpg_key } "
if test $? -ne 0
then
err "ERROR: Failed to tag the release"
return 1
fi
update_git_env " ${ sdir } "
fi
if is_set " ${ do_build } "
then
status_stage " ==> Building Consul for version ${ vers } "
build_consul_release " ${ sdir } " " ${ GO_BUILD_TAG } "
if test $? -ne 0
then
err "ERROR: Failed to build the Consul binaries"
return 1
fi
status_stage "==> Packaging up release binaries"
package_release " ${ sdir } " " ${ vers } "
if test $? -ne 0
then
err "ERROR: Failed to package the release binaries"
return 1
fi
fi
status_stage "==> Generating SHA 256 Hashes for Binaries"
shasum_release " ${ sdir } /pkg/dist " " ${ vers } "
if test $? -ne 0
then
err "ERROR: Failed to generate SHA 256 hashes for the release"
return 1
fi
if is_set " ${ do_sha256 } "
then
sign_release " ${ sdir } /pkg/dist " " ${ vers } " " ${ gpg_key } "
if test $? -ne 0
then
err "ERROR: Failed to sign the SHA 256 hashes file"
return 1
fi
fi
check_release " ${ sdir } /pkg/dist " " ${ vers } " " ${ do_sha256 } "
return $?
}