forked from coder/coder
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlib.sh
288 lines (251 loc) · 8.05 KB
/
lib.sh
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
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
#!/usr/bin/env bash
# This script is meant to be sourced by other scripts. To source this script:
# # shellcheck source=scripts/lib.sh
# source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
set -euo pipefail
# Avoid sourcing this script multiple times to guard against when lib.sh
# is used by another sourced script, it can lead to confusing results.
if [[ ${SCRIPTS_LIB_IS_SOURCED:-0} == 1 ]]; then
return
fi
# Do not export to avoid this value being inherited by non-sourced
# scripts.
SCRIPTS_LIB_IS_SOURCED=1
# realpath returns an absolute path to the given relative path. It will fail if
# the parent directory of the path does not exist. Make sure you are in the
# expected directory before running this to avoid errors.
#
# GNU realpath relies on coreutils, which are not installed or the default on
# Macs out of the box, so we have this mostly working bash alternative instead.
#
# Taken from https://stackoverflow.com/a/3915420 (CC-BY-SA 4.0)
realpath() {
local dir
local base
dir="$(dirname "$1")"
base="$(basename "$1")"
if [[ ! -d "$dir" ]]; then
error "Could not change directory to '$dir': directory does not exist"
fi
echo "$(
cd "$dir" || error "Could not change directory to '$dir'"
pwd -P
)"/"$base"
}
# We have to define realpath before these otherwise it fails on Mac's bash.
SCRIPT="${BASH_SOURCE[1]:-${BASH_SOURCE[0]}}"
SCRIPT_DIR="$(realpath "$(dirname "$SCRIPT")")"
function project_root {
# Nix sets $src in derivations!
[[ -n "${src:-}" ]] && echo "$src" && return
# Try to use `git rev-parse --show-toplevel` to find the project root.
# If this directory is not a git repository, this command will fail.
git rev-parse --show-toplevel 2>/dev/null && return
# This finds the Sapling root. This behavior is added so that @ammario
# and others can more easily experiment with Sapling, but we do not have a
# plan to support Sapling across the repo.
sl root 2>/dev/null && return
}
PROJECT_ROOT="$(cd "$SCRIPT_DIR" && realpath "$(project_root)")"
# pushd is a silent alternative to the real pushd shell command.
pushd() {
command pushd "$@" >/dev/null || error "Could not pushd to '$*'"
}
# popd is a silent alternative to the real popd shell command.
# shellcheck disable=SC2120
popd() {
command popd >/dev/null || error "Could not restore directory with popd"
}
# cdself changes directory to the directory of the current script. This should
# not be used in scripts that may be sourced by other scripts.
cdself() {
cd "$SCRIPT_DIR" || error "Could not change directory to '$SCRIPT_DIR'"
}
# cdroot changes directory to the root of the repository.
cdroot() {
cd "$PROJECT_ROOT" || error "Could not change directory to '$PROJECT_ROOT'"
}
# execrelative can be used to execute scripts as if you were in the parent
# directory of the current script. This should not be used in scripts that may
# be sourced by other scripts.
execrelative() {
pushd "$SCRIPT_DIR" || error "Could not change directory to '$SCRIPT_DIR'"
local rc=0
"$@" || rc=$?
popd
return $rc
}
dependency_check() {
local dep=$1
# Special case for yq that can be yq or yq4.
if [[ $dep == yq ]]; then
[[ -n "${CODER_LIBSH_YQ:-}" ]]
return
fi
command -v "$dep" >/dev/null
}
dependencies() {
local fail=0
for dep in "$@"; do
if ! dependency_check "$dep"; then
log "ERROR: The '$dep' dependency is required, but is not available."
if isdarwin; then
case "$dep" in
gsed | gawk)
log "- brew install $dep"
;;
esac
fi
fail=1
fi
done
if [[ "$fail" == 1 ]]; then
log
error "One or more dependencies are not available, check above log output for more details."
fi
}
requiredenvs() {
local fail=0
for env in "$@"; do
if [[ "${!env:-}" == "" ]]; then
log "ERROR: The '$env' environment variable is required, but is not set."
fail=1
fi
done
if [[ "$fail" == 1 ]]; then
log
error "One or more required environment variables are not set, check above log output for more details."
fi
}
gh_auth() {
if [[ -z ${GITHUB_TOKEN:-} ]]; then
if [[ -n ${GH_TOKEN:-} ]]; then
export GITHUB_TOKEN=${GH_TOKEN}
elif [[ ${CODER:-} == true ]]; then
if ! output=$(coder external-auth access-token github 2>&1); then
# TODO(mafredri): We could allow checking `gh auth token` here.
log "${output}"
error "Could not authenticate with GitHub using Coder external auth."
else
export GITHUB_TOKEN=${output}
fi
elif token="$(gh auth token --hostname github.com 2>/dev/null)"; then
export GITHUB_TOKEN=${token}
else
error "GitHub authentication is required to run this command, please set GITHUB_TOKEN or run 'gh auth login'."
fi
fi
}
# maybedryrun prints the given program and flags, and then, if the first
# argument is 0, executes it. The reason the first argument should be 0 is that
# it is expected that you have a dry_run variable in your script that is set to
# 0 by default (i.e. do not dry run) and set to 1 if the --dry-run flag is
# specified.
#
# Usage: maybedryrun 1 gh release create ...
# Usage: maybedryrun 0 docker push ghcr.io/coder/coder:latest
maybedryrun() {
if [[ "$1" == 1 ]]; then
shift
log "DRYRUN: $*"
else
shift
logrun "$@"
fi
}
# logrun prints the given program and flags, and then executes it.
#
# Usage: logrun gh release create ...
logrun() {
log $ "$*"
"$@"
}
# log prints a message to stderr.
log() {
echo "$*" 1>&2
}
# error prints an error message and returns an error exit code.
error() {
log "ERROR: $*"
exit 1
}
# isdarwin returns an error if the current platform is not darwin.
isdarwin() {
[[ "${OSTYPE:-darwin}" == *darwin* ]]
}
# issourced returns true if the script that sourced this script is being
# sourced by another.
issourced() {
[[ "${BASH_SOURCE[1]}" != "$0" ]]
}
# We don't need to check dependencies more than once per script, but some
# scripts call other scripts that also `source lib.sh`, so we set an environment
# variable after successfully checking dependencies once.
if [[ "${CODER_LIBSH_NO_CHECK_DEPENDENCIES:-}" != *t* ]]; then
libsh_bad_dependencies=0
if ((BASH_VERSINFO[0] < 4)); then
libsh_bad_dependencies=1
log "ERROR: You need at least bash 4.0 to run the scripts in the Coder repo."
if isdarwin; then
log "On darwin:"
log "- brew install bash"
# shellcheck disable=SC2016
log '- Add "$(brew --prefix bash)/bin" to your PATH'
log "- Restart your terminal"
fi
log
fi
# BSD getopt (which is installed by default on Macs) is not supported.
if [[ "$(getopt --version)" == *--* ]]; then
libsh_bad_dependencies=1
log "ERROR: You need GNU getopt to run the scripts in the Coder repo."
if isdarwin; then
log "On darwin:"
log "- brew install gnu-getopt"
# shellcheck disable=SC2016
log '- Add "$(brew --prefix gnu-getopt)/bin" to your PATH'
log "- Restart your terminal"
fi
log
fi
# The bash scripts don't call Make directly, but we want to make (ha ha)
# sure that make supports the features the repo uses. Notably, Macs have an
# old version of Make installed out of the box that doesn't support new
# features like ONESHELL.
#
# We have to disable pipefail temporarily to avoid ERRPIPE errors when
# piping into `head -n1`.
set +o pipefail
make_version="$(make --version 2>/dev/null | head -n1 | grep -oE '([[:digit:]]+\.){1,2}[[:digit:]]+')"
set -o pipefail
if [[ ${make_version//.*/} -lt 4 ]]; then
libsh_bad_dependencies=1
log "ERROR: You need at least make 4.0 to run the scripts in the Coder repo."
if isdarwin; then
log "On darwin:"
log "- brew install make"
# shellcheck disable=SC2016
log '- Add "$(brew --prefix make)/libexec/gnubin" to your PATH'
log "- Restart your terminal"
fi
log
fi
# Allow for yq to be installed as yq4.
if command -v yq4 >/dev/null; then
export CODER_LIBSH_YQ=yq4
elif command -v yq >/dev/null; then
if [[ $(yq --version) == *" v4."* ]]; then
export CODER_LIBSH_YQ=yq
fi
fi
if [[ "$libsh_bad_dependencies" == 1 ]]; then
error "Invalid dependencies, see above for more details."
fi
export CODER_LIBSH_NO_CHECK_DEPENDENCIES=true
fi
# Alias yq to the version we want by shadowing with a function.
if [[ -n ${CODER_LIBSH_YQ:-} ]]; then
yq() {
command $CODER_LIBSH_YQ "$@"
}
fi