Skip to content

Commit e952ba7

Browse files
committed
A lot of random changes
1 parent fe7a7f0 commit e952ba7

10 files changed

+260
-18
lines changed

cli.bash

+139
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
#!/bin/usr/env bash
2+
3+
import log
4+
5+
declare -Ag cli__options=()
6+
declare -Ag cli__descriptions=()
7+
cli__program_name="$0"
8+
cli__order=()
9+
10+
cli::reset() {
11+
cli__order=()
12+
declare -Ag cli__options=()
13+
declare -Ag cli__descriptions=()
14+
}
15+
16+
cli::set_program_name() {
17+
local program_name="$1"
18+
19+
cli__program_name="${program_name}"
20+
}
21+
22+
cli::_option_encode() {
23+
local option="$1"
24+
25+
log::debug "Encoding ${option}"
26+
27+
echo "${option}"
28+
}
29+
30+
cli::_option_decode() {
31+
local option="$1"
32+
33+
log::debug "Decoding ${option}"
34+
35+
echo "${option}"
36+
}
37+
38+
# if letter require argument, then subfix letter with ':'
39+
cli::add_option() {
40+
local letter="$1"
41+
local callback="$2"
42+
local description="$3"
43+
44+
cli__order+=("$letter")
45+
cli__options["${letter:0:1}"]="$callback"
46+
cli__descriptions[$letter]="$description"
47+
}
48+
49+
cli::print_help() {
50+
local usage="Usage: ${cli__program_name}"
51+
local description=""
52+
local description_formatted=""
53+
log::debug "${!cli__descriptions[@]}"
54+
log::debug "values: ${cli__descriptions[*]}"
55+
for letter in "${cli__order[@]}";do
56+
description_formatted="${cli__descriptions["$letter"]//\\n/\\n }"
57+
if [ "${letter:1:1}" = ":" ];then
58+
usage+=" [-${letter:0:1} ...]"
59+
description+=" -${letter:0:1} <...> ${description_formatted}\n"
60+
else
61+
usage+=" [-${letter:0:1}]"
62+
description+=" -${letter:0:1} ${description_formatted}\n"
63+
fi
64+
if [ "$(echo -e "$usage" | tail -n1 | wc -m )" -gt 70 ];then
65+
usage+="\n "
66+
fi
67+
done
68+
echo -e "${usage}\n\nOptions:\n${description}"
69+
}
70+
71+
cli::handle() {
72+
local all_options=""
73+
for letter in "${cli__order[@]}";do
74+
all_options+="${letter}"
75+
done
76+
77+
while getopts ":${all_options}" opt; do
78+
if [ "$opt" = ":" ];then
79+
log::fatal "Option -${OPTARG} require an argument"
80+
elif [ "$opt" = "?" ];then
81+
log::fatal "Invalid option -${OPTARG}"
82+
fi
83+
${cli__options[$opt]} "${OPTARG}"
84+
done
85+
}
86+
87+
cli_test__a=0
88+
cli_test__b=0
89+
cli_test__c=0
90+
91+
cli_test::a_cb() {
92+
cli_test__a=1
93+
}
94+
95+
cli_test::b_cb() {
96+
cli_test__b=$1
97+
}
98+
99+
cli_test::c_cb() {
100+
cli_test__c=1
101+
}
102+
103+
cli_test::test_handle() {
104+
cli::reset
105+
cli::set_program_name "example"
106+
cli::add_option "a" cli_test::a_cb "Example option a"
107+
cli::add_option "b:" cli_test::b_cb "Example option b"
108+
cli::add_option "c" cli_test::c_cb "Example option c"
109+
110+
cli::handle "-a" "-b" "sth"
111+
unit::assert_eq "${cli_test__a}" "1"
112+
unit::assert_eq "${cli_test__b}" "sth"
113+
unit::assert_eq "${cli_test__c}" "0"
114+
}
115+
116+
cli_test::test_help_gen() {
117+
cli::reset
118+
cli::set_program_name "example"
119+
cli::add_option "a" cli_test::a_cb "Example option a"
120+
cli::add_option "b:" cli_test::b_cb "Example option b"
121+
cli::add_option "c" cli_test::c_cb "Example option c"
122+
123+
local expected_output
124+
expected_output="Usage: example [-a] [-b ...] [-c]
125+
126+
Options:
127+
-a Example option a
128+
-b <...> Example option b
129+
-c Example option c"
130+
local output
131+
output=$(cli::print_help)
132+
133+
unit::assert_eq "${output}" "${expected_output}"
134+
}
135+
136+
cli_test::all() {
137+
unit::test cli_test::test_help_gen
138+
unit::test cli_test::test_handle
139+
}

exec.bash

+30-2
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,44 @@ exec::silent() {
2020
fi
2121
}
2222

23+
exec::retried_exec() {
24+
local retries=$1
25+
local sleep=$2
26+
shift;shift
27+
local try
28+
local log
29+
for ((try=0; try<retries; try++)) {
30+
if log="$("$@" 2>&1)" &>/dev/null;then
31+
return 0
32+
fi
33+
if [ "$DEBUG" = "1" ];then
34+
echo "$log" >&2
35+
fi
36+
sleep "$sleep"
37+
}
38+
log::error "Command $* failed $retries times"
39+
log::error "$log"
40+
return 1
41+
}
42+
2343
exec__sudo_keeping=0
2444

2545
exec::sudo_keep_alive() {
46+
exec::assert_cmd "sudo"
2647
if [ "$exec__sudo_keeping" != "1" ];then
2748
exec__sudo_keeping=1
28-
sudo -v
49+
if ! sudo -v;then
50+
log::fatal "sudo authentication failed"
51+
fi
2952
while true; do sudo -n true; sleep 60; kill -0 "$$" || exit; done 2>/dev/null &
3053
fi
3154
}
3255

56+
exec::sudo() {
57+
exec::sudo_keep_alive
58+
sudo "$@"
59+
}
60+
3361
exec::is_cmd_available() {
3462
local cmd="$1"
3563
if command -v "$cmd" &>/dev/null;then
@@ -49,7 +77,7 @@ exec::is_fn() {
4977
exec::assert_cmd() {
5078
local cmd="$1"
5179
if ! exec::is_cmd_available "$cmd";then
52-
log::panic "Command \`$cmd\` is not available, but is required by this application"
80+
log::fatal "Command \`$cmd\` is not available, but is required by this application"
5381
fi
5482
}
5583

fetch.bash

+9
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,15 @@ fetch::download_stream() {
2323
curl -sfL "$url"
2424
}
2525

26+
fetch::file_exists() {
27+
local url=$1
28+
if curl --output /dev/null --silent --head --fail -L "$url";then
29+
return 0
30+
else
31+
return 1
32+
fi
33+
}
34+
2635
fetch::verify() {
2736
local sha=$1
2837
local file=$2

github.bash

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ github::latest_release() {
55
local _repo=$1
66
local -n output_version=$2
77
local _url
8-
_url=$(curl -w "%{url_effective}" -I -L -s -S "https://github.com/$_repo/releases/latest" -o /dev/null)
8+
_url=$(curl -f -w "%{url_effective}" -I -L -s -S "https://github.com/$_repo/releases/latest" -o /dev/null)
99
if [ "$_url" != "https://github.com/$_repo/releases" ];then
1010
# shellcheck disable=SC2034
1111
output_version=$(echo -n "$_url"| sed -e 's|.*/||')
@@ -33,7 +33,7 @@ github::latest_commit() {
3333
local -n output_sha=$3
3434
exec::assert_cmd jq
3535
local _json
36-
_json=$(curl -s -L "https://api.github.com/repos/$_repo/commits/$_branch")
36+
_json=$(curl -f -s -L "https://api.github.com/repos/$_repo/commits/$_branch")
3737
local _sha=""
3838
if _sha=$(echo -n "$_json" | jq '.sha' -re);then
3939
# shellcheck disable=SC2034

installer.bash

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ installer::copy_file() {
8585
if [ -f "$dst" ];then
8686
local backup=""
8787
tmp::create_persistent_file backup "backup" "$installer__files_dir"
88-
cp "$dst" "$backup"
88+
cp -p "$dst" "$backup"
8989
local relative_backup="${backup##$installer__dir/}"
9090
installer::_add_sha_item "$installer__dir" "$relative_backup"
9191
installer::_write_line "log 'Restoring $dst'"

log.bash

+64-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
#!/usr/bib/env bash
22

33
log__debug=0
4+
log__colors=-1
5+
6+
log::detect_colors() {
7+
if [ -t 1 ];then
8+
log__colors=1
9+
else
10+
log__colors=0
11+
fi
12+
}
413

514
log::enable_debug() {
615
log__debug=1
@@ -10,10 +19,27 @@ log::disable_debug() {
1019
log__debug=0
1120
}
1221

22+
log::disable_colors() {
23+
log__colors=0
24+
log::debug "Colors forcefuly disabled"
25+
}
26+
27+
log::enable_colors() {
28+
log__colors=1
29+
log::debug "Colors forcefuly enabled"
30+
}
31+
1332
log::debug() {
1433
local pid=$BASHPID
1534
if [ "$log__debug" = "1" ] || [ "$DEBUG" = "1" ];then
16-
echo -e "\e[90m$(printf "%-6s %-30s" "$pid" "${FUNCNAME[1]}"): $*\e[0m" >&2
35+
if [ "$log__colors" = "1" ];then
36+
echo -e "\e[90m$(printf "%-6s %-30s" "$pid" "${FUNCNAME[1]}"): $*\e[0m" >&2
37+
elif [ "$log__colors" = "-1" ];then
38+
log::detect_colors
39+
log::debug "$@"
40+
else
41+
echo -e "[DEBUG ] $(printf "%-6s %-30s" "$pid" "${FUNCNAME[1]}"): $*" >&2
42+
fi
1743
fi
1844
}
1945

@@ -22,18 +48,51 @@ log::panic() {
2248
stack::print 1 1
2349
}
2450

51+
log::fatal() {
52+
log::error "$*"
53+
exit 1
54+
}
55+
2556
log::success() {
26-
echo -e "\e[32m$*\e[0m" >&2
57+
if [ "$log__colors" = "1" ];then
58+
echo -e "\e[32m$*\e[0m" >&2
59+
elif [ "$log__colors" = "-1" ];then
60+
log::detect_colors
61+
log::success "$@"
62+
else
63+
echo -e "[SUCCESS] $*" >&2
64+
fi
2765
}
2866

2967
log::info() {
30-
echo -e "\e[36m$*\e[0m" >&2
68+
if [ "$log__colors" = "1" ];then
69+
echo -e "\e[36m$*\e[0m" >&2
70+
elif [ "$log__colors" = "-1" ];then
71+
log::detect_colors
72+
log::info "$@"
73+
else
74+
echo -e "[INFO ] $*" >&2
75+
fi
3176
}
3277

3378
log::error() {
34-
echo -e "\e[31m$*\e[0m" >&2
79+
if [ "$log__colors" = "1" ];then
80+
echo -e "\e[31m$*\e[0m" >&2
81+
elif [ "$log__colors" = "-1" ];then
82+
log::detect_colors
83+
log::error "$@"
84+
else
85+
echo -e "[ERROR ] $*" >&2
86+
fi
3587
}
3688

3789
log::warn() {
38-
echo -e "\e[33m$*\e[0m" >&2
90+
if [ "$log__colors" = "1" ];then
91+
echo -e "\e[33m$*\e[0m" >&2
92+
elif [ "$log__colors" = "-1" ];then
93+
log::detect_colors
94+
log::warn "$@"
95+
else
96+
echo -e "[WARN ] $*" >&2
97+
fi
3998
}

stack.bash

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ stack::print() {
55
set +o xtrace
66
local code="${1:-1}"
77
local up=${2:-0}
8+
# Ignore errors detected in bashdb
9+
if [[ ${FUNCNAME[$up+1]} == _Dbg_* ]];then
10+
return
11+
fi
812
echo -e "\n\e[91mRuntime failure at ${BASH_SOURCE[$up+1]}:${BASH_LINENO[$up]} exit code $err\n"
913
stack::point_line "${BASH_SOURCE[$up+1]}" "${BASH_LINENO[$up]}"
1014
if [ ${#FUNCNAME[@]} -gt $((up+2)) ];then
@@ -41,6 +45,7 @@ stack::point_line() {
4145

4246
trap stack::print ERR
4347
set -o errtrace
48+
set -E
4449

4550
import traps
4651
traps::add_err_trap "stack::print 1 1"

test.sh

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import dirdb
1010
import tmp
1111
import installer
1212
import daemon
13+
import cli
1314

1415
test_fails() {
1516
unit::assertEq 1 2 "this is error"
@@ -23,3 +24,4 @@ dirdb_test::all
2324
tmp_test::all
2425
installer_test::all
2526
daemon_test::all
27+
cli_test::all

traps.bash

+3-2
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,18 @@ traps::_handle_int() {
4141
}
4242

4343
traps::_handle_err() {
44+
local err=$?
4445
log::debug "Handling ERR traps"
4546
if [ "$traps__err_ignore" = "1" ];then
4647
return 0
4748
fi
4849
for cb in "${traps__err_trap[@]}";do
49-
$cb
50+
$cb "$err"
5051
done
5152
}
5253

5354
traps::_init() {
54-
if [ "$traps__set_up" != "$BASHPID" ] && [ -z "$BASH_LIB_UNDER_TEST" ];then
55+
if [ "$traps__set_up" = "0" ] && [ -z "$BASH_LIB_UNDER_TEST" ];then
5556
log::debug "Setting up traps for $BASHPID"
5657
traps__set_up=$BASHPID
5758
traps__exit_trap=()

0 commit comments

Comments
 (0)