-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
load
executable file
·269 lines (235 loc) · 8.32 KB
/
load
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
#! /bin/bash
# version 0.6.0
# executed by scripts that use `libbash`
# task perform library initialization
if [[ ! -v __LIBBASH__IS_LOADED ]]; then
# ### Show an Error
#
# Indicates an error has happend in `libbash`. Used to display
# errors even when the log module is not (or has not yet been)
# loaded.
#
# #### Attention
#
# Underscored functions are not unset at the end of the sourcing
# process, but they should only be used by `libbash` modules, not
# by applications / other libraries using `libbash`.
function __libbash__show_error() {
printf "%s \e[91mERROR\e[0m %s: %s\n" \
"$(date +"%Y-%m-%dT%H:%M:%S.%6N%:z" || :)" "${SCRIPT:-$(basename "${0}")}" "${*}" >&2
}
export -f __libbash__show_error
readonly -f __libbash__show_error
# ### Exit With Conditions
#
# This function will exit when not running interactively or when
# 'LIBBASH_EXIT_IN_INTERACTIVE_MODE' is set to '1'.
#
# #### Attention
#
# Underscored functions are not unset at the end of the sourcing
# process, but they should only be used by `libbash` modules, not
# by applications / other libraries using `libbash`.
function __libbash__exit_checked() {
local CODE=${1:-1}
if [[ ${-} != *i* ]] \
|| [[ ${LIBBASH_EXIT_IN_INTERACTIVE_MODE:-0} -eq 1 ]] \
|| [[ ${*} == *--force* ]]; then
if [[ ${CODE} =~ ^[0-9]$ ]]; then
exit "${CODE}"
else
log 'error' "Supplied non-number code ('${CODE}') to __libbash__exit_checked"
exit 1
fi
else
if [[ ${CODE} =~ ^[0-9]$ ]]; then
return "${CODE}"
else
log 'error' "Supplied non-number code ('${CODE}') to __libbash__exit_checked"
return 1
fi
fi
}
export -f __libbash__exit_checked
readonly -f __libbash__exit_checked
# ### Provides Call Stack Information
#
# This function prints the call stack to identify where an error
# originated.
#
# This function will only print a call stack if the stack is
# deeper than 2, because otherwise the error did not originate
# in a function but at global scope.
function __libbash__show_call_stack() {
local STACK_DEPTH CUTOFF=1
[[ ${*} == *--internal* ]] && CUTOFF=2
[[ ${#FUNCNAME[@]} -gt ${CUTOFF} ]] || return 0
STACK_DEPTH=$(( ${#FUNCNAME[@]} - CUTOFF ))
printf '\ncall stack (most recent call first):\n' >&2
for FUNCTION in "${FUNCNAME[@]:${CUTOFF}:${STACK_DEPTH}}"; do
printf " %s\n" "${FUNCTION}" >&2
done
printf '\n' >&2
}
export -f __libbash__show_call_stack
readonly -f __libbash__show_call_stack
# ### Show an Error and Exit
#
# Display
function __libbash__exit_with_error_and_callstack() {
__libbash__show_error "${1:-Error message not supplied (this is a bug in libbash)}"
__libbash__show_call_stack --internal
__libbash__exit_checked "${2:-1}"
}
export -f __libbash__exit_with_error_and_callstack
readonly -f __libbash__exit_with_error_and_callstack
# ### Provide Debug Information
#
# Shows some debug information that `libbash` has collected.
function libbash__debug() {
if [[ ! -v __LIBBASH__IS_LOADED_LOG ]]; then
__libbash__exit_with_error_and_callstack "Debug functionality requires the log module to be loaded and log level set to 'debug' or 'trace'"
return 1
fi
log 'debug' "Showing 'libbash' debug information"
log 'debug' " -> version: ${LIBBASH_VERSION}"
log 'debug' " -> Loaded modules: ${LIBBASH_LOADED_MODULES[*]}"
}
export -f libbash__debug
export LIBBASH_EXIT_IN_INTERACTIVE_MODE=${LIBBASH_EXIT_IN_INTERACTIVE_MODE:-0}
export __LIBBASH__IS_LOADED=1
readonly __LIBBASH__IS_LOADED
fi # if ! ${__LIBBASH__IS_LOADED:-false}
# ### The Start of `libbash`
#
# This function calls the initialization routines for `libbash`
# in proper order.
function __libbash__main() {
# We cannot use the log at this point in the setup procedure. Hence,
# we need to be very careful what we can call and what has not yet
# been set up.
function __libbash__run_preflight_checks() {
if [[ ${BASH_VERSINFO[0]} -lt 5 ]]; then
__libbash__show_error "libbash only supports Bash version >= 5.0"
return 1
fi
}
function __libbash__parse_arguments() {
while [[ ${#} -gt 0 ]]; do
case "${1:-}" in
( '--online' )
LIBBASH_ONLINE=1
LIBBASH_ONLINE_VERSION="v${2:?Version is required when specifying \'--online\'}"
shift 2
;;
( * )
MODULES+=("${1}")
shift 1
;;
esac
done
}
# ### Go Through Arguments
#
# Iterates through arguments to load the corresponding module.
# If the `--online` flag was supplied, this function `source`s
# the files from the GitHub repository via `curl`.
function __libbash__source_files() {
local MODULE TMP_DIR
TMP_DIR=$(mktemp --directory)
for MODULE in "${MODULES[@]}"; do
if [[ ${LIBBASH_LOADED_MODULES[*]} =~ ${MODULE} ]]; then
__libbash__exit_with_error_and_callstack "module '${MODULE}' loaded more than once" 3
fi
if [[ ${LIBBASH_ONLINE} -eq 1 ]]; then
if ! curl --silent --show-error --fail --location -o "${TMP_DIR}/${MODULE}" \
"https://github.com/raw/georglauterbach/libbash/${LIBBASH_ONLINE_VERSION}/modules/${MODULE}.sh"; then
__libbash__show_error "Curl did not succeed. Wrong version? Does the module exist?"
rm -rf "${TMP_DIR}"
return 5
fi
# shellcheck source=/dev/null
source "${TMP_DIR}/${MODULE}"
else
local MODULE_PATH
MODULE_PATH="${LIBBASH_DIRECTORY}/modules/${MODULE}.sh"
if [[ -e "${MODULE_PATH}" ]]; then
# shellcheck source=/dev/null
source "${MODULE_PATH}"
else
__libbash__exit_with_error_and_callstack "module '${MODULE}' not found" 4
fi
fi
LIBBASH_LOADED_MODULES+=("${MODULE}")
done
rm -rf "${TMP_DIR}"
}
# ### Fallback `log`
#
# If the `log` module was not sourced, provide a fallback
# `log` implementation, but only for error messages.
function __libbash__setup_default_notify_error() {
function log() {
if [[ ${1:-} != 'error' ]]; then
__libbash__exit_with_error_and_callstack \
"log module was not loaded but 'log' was called with log level not 'error' (arguments were: ${*})"
return 1
fi
shift 1
__libbash__show_error "${*}"
}; export -f log
}
# if SCRIPT is already set, save it so we can restore it later
[[ -v SCRIPT ]] && __LIBBASH_SCRIPT_TMP=${SCRIPT}
SCRIPT='libbash init'
LOG_LEVEL=${LOG_LEVEL:-info}
LIBBASH_DIRECTORY="$(realpath -eL "$(dirname "${BASH_SOURCE[0]}")")"
LIBBASH_LOADED_MODULES=()
# ! export all variables here first, even if not
# ! all modules are sourced to satisfy shellcheck
# ! in applications that use `libbash`
export SCRIPT LOG_LEVEL CRI
export LIBBASH_DIRECTORY LIBBASH_VERSION LIBBASH_LOADED_MODULES
local LIBBASH_ONLINE=0 LIBBASH_ONLINE_VERSION='' MODULES=()
__libbash__run_preflight_checks || return ${?}
__libbash__parse_arguments "${@}" || return ${?}
__libbash__source_files || return ${?}
if [[ ${LIBBASH_ONLINE} -eq 0 ]]; then
LIBBASH_VERSION='local'
if command -v git &>/dev/null; then
LIBBASH_VERSION+="@$(cd "${LIBBASH_DIRECTORY}" && git rev-parse --short HEAD)"
fi
else
LIBBASH_VERSION=${LIBBASH_ONLINE_VERSION}
fi
# this has to be set up last
if [[ ${LIBBASH_LOADED_MODULES[*]} != *log* ]]; then
__libbash__setup_default_notify_error
return 0
fi
log 'trace' "Finished 'libbash' initialization"
}
# Independently of whether we finished initialization successfully or not,
# we want to clean up. This function performs exactly this functionality.
function __libbash__post_init() {
unset __libbash__main
unset __libbash__run_preflight_checks
unset __libbash__parse_arguments
unset __libbash__source_files
unset __libbash__setup_default_notify_error
unset __libbash__post_init
if [[ -v __LIBBASH_SCRIPT_TMP ]]; then
SCRIPT=${__LIBBASH_SCRIPT_TMP}
unset __LIBBASH_SCRIPT_TMP
else
SCRIPT='prompt or not inside a function'
fi
}
if __libbash__main "${@}"; then
__libbash__post_init
else
__LIBBASH__RETURN_CODE_INIT=${?}
__libbash__post_init
# ensure the last command has the exit status of __libbash__main
( exit "${__LIBBASH__RETURN_CODE_INIT}" ; )
fi