1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
|
# shellcheck shell=sh
set -e
ch_bin="$(cd "$(dirname "$0")" && pwd)"
# shellcheck disable=SC2034
ch_base=${ch_bin%/*}
ch_lib=${ch_bin}/../lib
. "${ch_lib}/version.sh"
# Log level. Incremented by “--verbose” and decremented by “--quiet”, as in the
# Python code.
log_level=0
# Logging functions. Note that we disable SC2059 because we want these functions
# to behave exactly like printf(1), e.g. we want
#
# >>> VERBOSE "foo %s" "bar"
# foo bar
#
# Implementing the suggestion in SC2059 would instead result in something like
#
# >>> VERBOSE "foo %s" "bar"
# foo %sbar
DEBUG () {
if [ "$log_level" -ge 2 ]; then
# shellcheck disable=SC2059
printf "$@" 1>&2
printf '\n' 1>&2
fi
}
FATAL () {
printf 'error: ' 1>&2
# shellcheck disable=SC2059
printf "$@" 1>&2
printf '\n' 1>&2
exit 1
}
INFO () {
if [ "$log_level" -ge 0 ]; then
# shellcheck disable=SC2059
printf "$@" 1>&2
printf '\n' 1>&2
fi
}
VERBOSE () {
if [ "$log_level" -ge 1 ]; then
# shellcheck disable=SC2059
printf "$@" 1>&2
printf '\n' 1>&2
fi
}
WARNING () {
if [ "$log_level" -ge -1 ]; then
printf 'warning: ' 1>&2
# shellcheck disable=SC2059
printf "$@" 1>&2
printf '\n' 1>&2
fi
}
# Return success if path $1 exists, without dereferencing links, failure
# otherwise. (“test -e” dereferences.)
exist_p () {
stat "$1" > /dev/null 2>&1
}
# Try to parse $1 as a common argument. If accepted, either exit (for things
# like --help) or return success; otherwise, return failure (i.e., not a
# common argument).
parse_basic_arg () {
case $1 in
--_lib-path) # undocumented
echo "$ch_lib"
exit 0
;;
--help)
usage 0 # exits
;;
-q|--quiet)
if [ $log_level -gt 0 ]; then
FATAL "incompatible options: --quiet, --verbose"
fi
log_level=$((log_level-1))
return 0
;;
-v|--verbose)
if [ $log_level -lt 0 ]; then
FATAL "incompatible options: --quiet, --verbose"
fi
log_level=$((log_level+1))
return 0
;;
--version)
version # exits
;;
esac
return 1 # not a basic arg
}
# Redirect standard streams (or not) depending on “quiet” level. See table in
# FAQ.
quiet () {
if [ $log_level -lt -2 ]; then
"$@" 1>/dev/null 2>/dev/null
elif [ $log_level -lt -1 ]; then
"$@" 1>/dev/null
else
"$@"
fi
}
# Convert container registry path to filesystem compatible path.
#
# NOTE: This is used both to name user-visible stuff like tarballs as well as
# dig around in the ch-image storage directory.
tag_to_path () {
echo "$1" | tr '/:' '%+'
}
usage () {
echo "${usage:?}" 1>&2
exit "${1:-1}"
}
version () {
echo 1>&2 "$ch_version"
exit 0
}
# Set a variable and print its value, human readable description, and origin.
# Parameters:
#
# $1: string: variable name
# $2: string: command line argument value (1st priority)
# $3: string: environment variable value (2nd priority)
# $4: string: default value (3rd priority)
# $5: int: width of description (use -1 for natural width)
# $6: string: human readable description for stdout
# $7: boolean: if true, suppress chatter
#
# FIXME: Shouldn't export the variable, and no POSIX sh indirection available.
# There are safe eval solution out there, but I was too lazy to deal with it.
vset () {
var_name=$1
cli_value=$2
env_value=$3
def_value=$4
desc_width=$5
var_desc=$6
quiet=$7
if [ "$cli_value" ]; then
export "$var_name"="$cli_value"
value=$cli_value
method='command line'
elif [ "$env_value" ]; then
export "$var_name"="$env_value"
value=$env_value
method='environment'
else
export "$var_name"="$def_value"
value=$def_value
method='default'
fi
# FIXME: Kludge: Assume it's a boolean variable and the empty string means
# false. Print "no" instead of the empty string.
if [ -z "$value" ]; then
value=no
fi
if [ -z "$quiet" ]; then
var_desc="$var_desc:"
printf "%-*s %s (%s)\n" "$desc_width" "$var_desc" "$value" "$method"
fi
}
# Is Docker present, and if so, do we need sudo? If docker is a wrapper for
# podman, “docker info” hangs (#1656), so treat that as not found.
if ( ! command -v docker > /dev/null 2>&1 ) \
|| ( docker --help 2>&1 | grep -Fqi podman ); then
docker_ () {
echo 'docker not found; unreachable code reached' 1>&1
exit 1
}
elif docker info > /dev/null 2>&1; then
docker_ () {
docker "$@"
}
else
docker_ () {
sudo docker "$@"
}
fi
# Wrapper for rootless podman (for consistency w/ docker).
# The only thing we're really concerned with here is the trailing underscore,
# since we use it to construct function calls.
podman_ () {
podman "$@"
}
# Use parallel gzip if it's available.
if command -v pigz > /dev/null 2>&1; then
gzip_ () {
pigz "$@"
}
else
gzip_ () {
gzip "$@"
}
fi
# Use pv(1) to show a progress bar, if it’s available and the quiet level is
# less than one, otherwise cat(1). WARNING: You must pipe in the file because
# arguments are ignored if this is cat(1). (We also don’t want a progress bar if
# stdin is not a terminal, but pv takes care of that). Note that we put the if
# statement in the scope of the function because doing so ensures that it gets
# evaluated after “quiet” is assigned an appropriate value by “parse_basic_arg”.
pv_ () {
if command -v pv > /dev/null 2>&1 && [ "$log_level" -gt -1 ]; then
pv -pteb "$@"
else
cat
fi
}
|