-
Notifications
You must be signed in to change notification settings - Fork 3
/
hook.sh
125 lines (103 loc) · 3.18 KB
/
hook.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
#!/usr/bin/env bash
export HOOK_LOG="${HOOK_LOG:-false}"
export HOOK_LOG_FUNC="${HOOK_LOG_FUNC:-_hook_log_msg}"
export HOOK_PREFIX=${HOOK_PREFIX:-hook_}
export HOOK_GLOBAL_PREFIX=${HOOK_GLOBAL_PREFIX:-global_}
export HOOK_FILE="${HOOK_FILE:-./hooks.sh}"
export HOOK_GLOBAL_FILE="${HOOK_GLOBAL_FILE:-}"
export HOOK_LAST_EXIT_CODE
hook() {
local -a functions_before_hook
local -a functions_after_hook
HOOK_LAST_EXIT_CODE=''
mapfile -t functions_before_hook < <(declare -F | sed 's/declare -f //g')
if [[ ${HOOK_GLOBAL_FILE} != "" ]]; then
if [[ -f "${HOOK_GLOBAL_FILE:?}" ]]; then
_hook_log DEBUG "sourcing global hook file (${HOOK_GLOBAL_FILE:?})"
# shellcheck source=/dev/null
source "${HOOK_GLOBAL_FILE:?}"
else
_hook_log WARN "global hook file not found (${HOOK_GLOBAL_FILE:?})"
fi
else
_hook_log TRACE "no global hook file defined"
fi
if [[ -f "${HOOK_FILE:?}" ]]; then
_hook_log DEBUG "sourcing hook file (${HOOK_FILE:?})"
# shellcheck source=/dev/null
source "${HOOK_FILE:?}"
else
_hook_log WARN "hook file not found (${HOOK_FILE:?})"
fi
mapfile -t functions_after_hook < <(declare -F | sed 's/declare -f //g')
_hook "${HOOK_PREFIX:?}pre" "$@"
"$@"
HOOK_LAST_EXIT_CODE=$?
_hook "${HOOK_PREFIX:?}post" "$@"
for f in "${functions_after_hook[@]}"; do
if [[ "${f:-}" =~ ^hook_.* ]]; then
if ! _hook_array_contains "${f:-}" "${functions_before_hook[@]}"; then
unset -f "${f:-}"
fi
fi
done
return ${HOOK_LAST_EXIT_CODE:?}
}
_hook() {
local prefix="${1:?'A prefix must be given'}"
shift
local index
local -a args
local arg
local rest_args_start
local rest_args_end
local -a rest_args
local hook_func
args=("$@")
hook_func="${prefix}"
_hook_exec "${hook_func:?}" "${args[@]}"
for ((index = 0; index < ${#args[@]}; index++)); do
arg="${args[index]}"
if [[ "${arg}" != '' ]]; then
rest_args_start=${index+1}
rest_args_end=${#args[@]}
rest_args=("${args[@]:${rest_args_start:?}:${rest_args_end:?}}")
hook_func="${hook_func:?}_${arg:?}"
_hook_exec "${hook_func:?}" "${rest_args[@]}"
fi
done
}
_hook_exec() {
local hook_func="${1:?'A hook function must be provided!'}"
shift
if [[ "$(type -t "${HOOK_GLOBAL_PREFIX:?}${hook_func:?}")" == 'function' ]]; then
_hook_log INFO "executing \"${HOOK_GLOBAL_PREFIX:?}${hook_func}\" (${HOOK_GLOBAL_FILE})"
"${HOOK_GLOBAL_PREFIX:?}${hook_func:?}" "$@"
_hook_log DEBUG "finished executing \"${HOOK_GLOBAL_PREFIX:?}${hook_func}\" (${HOOK_GLOBAL_FILE})"
fi
if [[ "$(type -t "${hook_func:?}")" == 'function' ]]; then
_hook_log INFO "executing \"${hook_func}\" (${HOOK_FILE:?})"
"${hook_func:?}" "$@"
_hook_log DEBUG "finished executing \"${hook_func}\" (${HOOK_FILE:?})"
fi
}
_hook_array_contains() {
local -r needle="$1"
shift
local -ra haystack=("$@")
local item
for item in "${haystack[@]}"; do
if [[ "$item" == "$needle" ]]; then
return 0
fi
done
return 1
}
_hook_log() {
if [[ "$1" == 'ERROR' || "${HOOK_LOG}" == 'true' && "${HOOK_LOG_FUNC}" != '' ]]; then
"${HOOK_LOG_FUNC:?}" "$@" || true
fi
}
_hook_log_msg() {
echo "[hook] [$1] ${*:2}"
}