-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathexec.bash
222 lines (195 loc) · 5.57 KB
/
exec.bash
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
#!/bin/usr/env bash
import log
import dialog
exec__last_log=""
# run command without displaying any output when it success
exec::silent() {
if [ "$DEBUG" = 1 ]; then
"$@"
ret=$?
return $ret
else
if ! exec__last_log="$("$@" 2>&1)";then
log::error "Command $* failed"
log::error "$exec__last_log"
return 1
fi
return 0
fi
}
exec::escape() {
local p
for p in "$@";do
p="${p//\'/\"\'\"}"
p="${p//\\/\\\\}"
p="'${p}'"
p="${p//\'\|\'/|}"
echo -n "$p "
done
}
exec::last_log() {
echo "$exec__last_log"
}
exec::_exec_spinner_progress() {
local line
local last_line
local lineno=0
local spinner_pos
local spinner=( "⢀⠀" "⡀⠀" "⠄⠀" "⢂⠀" "⡂⠀" "⠅⠀" "⢃⠀" "⡃⠀" "⠍⠀" "⢋⠀" "⡋⠀" "⠍⠁" "⢋⠁" "⡋⠁" "⠍⠉" "⠋⠉" "⠋⠉" "⠉⠙" "⠉⠙" "⠉⠩" "⠈⢙" "⠈⡙" "⢈⠩" "⡀⢙" "⠄⡙" "⢂⠩" "⡂⢘" "⠅⡘" "⢃⠨" "⡃⢐" "⠍⡐" "⢋⠠" "⡋⢀" "⠍⡁" "⢋⠁" "⡋⠁" "⠍⠉" "⠋⠉" "⠋⠉" "⠉⠙" "⠉⠙" "⠉⠩" "⠈⢙" "⠈⡙" "⠈⠩" "⠀⢙" "⠀⡙" "⠀⠩" "⠀⢘" "⠀⡘" "⠀⠨" "⠀⢐" "⠀⡐" "⠀⠠" "⠀⢀" "⠀⡀" )
local spinner_len="${#spinner[@]}"
local status
while true;do
read -r -t 0.1 line && status=$? || status=$?
if [ "$status" != 0 ] && [ "$status" -le 128 ];then
break;
fi
if [ "$status" -gt 128 ];then
line="${last_line}"
else
echo "$line"
fi
lineno=$((lineno + 1))
spinner_pos=$((lineno % spinner_len))
log::status "${spinner[$spinner_pos]} $line"
last_line="$line"
done
log::status
}
exec::exec_preview() {
local ret
if [ "$DEBUG" = 1 ]; then
"$@"
ret=$?
return $ret
else
ret=0
exec__last_log="$("$@" 2>&1 | exec::_exec_spinner_progress; return "${PIPESTATUS[0]}")" || ret=$?
if [ "$ret" != "0" ];then
log::error "Command $* failed"
log::error "$exec__last_log"
fi
return "$ret"
fi
}
exec::dialog_silent() {
if [ "$DEBUG" = 1 ]; then
"$@"
ret=$?
return $ret
else
if ! exec__last_log="$("$@" 2>&1)";then
dialog::msg "Command $* failed:\n\n$(dialog::format_text "$exec__last_log")" 15 80
return 1
fi
return 0
fi
}
exec::dialog() {
local msg=$1
shift;
if [ "$DEBUG" = 1 ]; then
"$@"
ret=$?
return $ret
else
dialog::info "$msg"
if ! exec__last_log="$("$@" 2>&1)";then
dialog::msg "Command $* failed:\n\n$(dialog::format_text "$exec__last_log")" 15 80
return 1
fi
return 0
fi
}
exec::retried_exec() {
local retries=$1
local sleep=$2
shift;shift
local try
for ((try=0; try<retries; try++)) {
if exec__last_log="$("$@" 2>&1)" &>/dev/null;then
return 0
fi
if [ "$DEBUG" = "1" ];then
echo "$exec__last_log" >&2
fi
sleep "$sleep"
}
log::error "Command $* failed $retries times"
log::error "$exec__last_log"
return 1
}
exec__sudo_keeping=0
exec::sudo_keep_alive() {
exec::assert_cmd "sudo"
if [ "$exec__sudo_keeping" != "1" ];then
exec__sudo_keeping=1
if ! sudo -v;then
log::fatal "sudo authentication failed"
fi
while true; do sudo -n true; sleep 60; kill -0 "$$" || exit; done 2>/dev/null &
fi
}
exec::sudo() {
exec::sudo_keep_alive
sudo "$@"
}
exec::is_cmd_available() {
local cmd="$1"
if command -v "$cmd" &>/dev/null;then
return 0
fi
return 1
}
exec::is_fn() {
local fn="$1"
if [ "$(type -t "$fn")" = "function" ];then
return 0
fi
return 1
}
exec::assert_cmd() {
local cmd="$1"
if ! exec::is_cmd_available "$cmd";then
log::fatal "Command \`$cmd\` is not available, but is required by this application"
fi
}
exec_test::test_is_cmd_available() {
unit::assert_success "exec::is_cmd_available bash"
unit::assert_failed "exec::is_cmd_available this_for_sure_not_exists_bleh"
}
exec_test::test_output() {
local og_debug="$DEBUG"
DEBUG=0
set +eE
local should_be_silent
should_be_silent=$(exec::silent bash -c 'echo output && true' 2>&1)
unit::assert_eq "$should_be_silent" "" "shouldn't have output on success"
local should_have_output
should_have_output=$(exec::silent bash -c 'echo output && false' 2>&1 || true)
unit::assert_contain "$should_have_output" "output" "should have output on error"
DEBUG=1
local output_in_debug
output_in_debug=$(exec::silent bash -c 'echo output && true' 2>&1)
unit::assert_contain "$output_in_debug" "output" "should have output in debug"
DEBUG="$og_debug"
set -eE
}
exec_test::test_is_fn() {
# shellcheck disable=SC2034
local some_var=""
unit::assert_success exec::is_fn exec::is_fn
unit::assert_failed exec::is_fn exec_test::not_exists_for_sure
unit::assert_failed exec::is_fn some_var
}
exec_test::test_escape() {
local cmd_escaped
cmd_escaped="$(exec::escape bash -c "echo 'lol'; echo -e 'a\nb'" \| base64)"
echo "$cmd_escaped"
unit::assert_eq "$(sh -c "$cmd_escaped")" "$(bash -c "echo 'lol'; echo -e 'a\nb'" | base64)"
}
exec_test::all() {
unit::test exec_test::test_output
unit::test exec_test::test_is_cmd_available
unit::test exec_test::test_is_fn
unit::test exec_test::test_escape
}