Skip to content

Commit c28c32f

Browse files
committed
docs: Create automated docs with shdoc
1 parent 80dec2d commit c28c32f

File tree

6 files changed

+589
-17
lines changed

6 files changed

+589
-17
lines changed

Bakefile.sh

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# shellcheck shell=bash
2+
3+
task.test() {
4+
bats tests
5+
}
6+
7+
task.docs() {
8+
shdoc > './docs/api.md' < <(
9+
for f in ./pkg/lib/public/*.sh; do
10+
cat "$f"
11+
done
12+
)
13+
}

bake

Lines changed: 365 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,365 @@
1+
#!/usr/bin/env bash
2+
3+
# @name Bake
4+
# @brief Bake: A Bash-based Make alternative
5+
# @description Bake is a dead-simple task runner used to quickly cobble together shell scripts
6+
#
7+
# In a few words, Bake lets you call the following 'print' task with './bake print'
8+
#
9+
# ```bash
10+
# #!/usr/bin/env bash
11+
# task.print() {
12+
# printf '%s\n' 'Contrived example'
13+
# }
14+
# ```
15+
#
16+
# Learn more about it [on GitHub](https://github.com/hyperupcall/bake)
17+
18+
if [ "$0" != "${BASH_SOURCE[0]}" ] && [ "$BAKE_INTERNAL_CAN_SOURCE" != 'yes' ]; then
19+
printf '%s\n' "Error: This file should not be sourced" >&2
20+
return 1
21+
fi
22+
23+
# @description Prints `$1` formatted as an error and the stacktrace to standard error,
24+
# then exits with code 1
25+
# @arg $1 string Text to print
26+
bake.die() {
27+
if [ -n "$1" ]; then
28+
__bake_error "$1. Exiting"
29+
else
30+
__bake_error 'Exiting'
31+
fi
32+
__bake_print_big '<- ERROR'
33+
34+
__bake_print_stacktrace
35+
36+
exit 1
37+
}
38+
39+
# @description Prints `$1` formatted as a warning to standard error
40+
# @arg $1 string Text to print
41+
bake.warn() {
42+
if __bake_is_color; then
43+
printf "\033[1;33m%s:\033[0m %s\n" 'Warn' "$1"
44+
else
45+
printf '%s: %s\n' 'Warn' "$1"
46+
fi
47+
} >&2
48+
49+
# @description Prints `$1` formatted as information to standard output
50+
# @arg $1 string Text to print
51+
bake.info() {
52+
if __bake_is_color; then
53+
printf "\033[0;34m%s:\033[0m %s\n" 'Info' "$1"
54+
else
55+
printf '%s: %s\n' 'Info' "$1"
56+
fi
57+
}
58+
59+
# @description Dies if any of the supplied variables are empty. Deprecated in favor of 'bake.assert_not_empty'
60+
# @arg $@ string Variable names to print
61+
# @see bake.assert_not_empty
62+
bake.assert_nonempty() {
63+
__bake_internal_warn "Function 'bake.assert_nonempty' is deprecated. Please use 'bake.assert_not_empty' instead"
64+
bake.assert_not_empty "$@"
65+
}
66+
67+
# @description Dies if any of the supplied variables are empty
68+
# @arg $@ string Variable names to print
69+
bake.assert_not_empty() {
70+
local variable_name=
71+
for variable_name; do
72+
local -n ____variable="$variable_name"
73+
74+
if [ -z "$____variable" ]; then
75+
bake.die "Failed because variable '$variable_name' is empty"
76+
fi
77+
done; unset -v variable_name
78+
}
79+
80+
# @description Dies if a command cannot be found
81+
# @arg $1 string Command to test for existence
82+
bake.assert_cmd() {
83+
local cmd=$1
84+
85+
if [ -z "$cmd" ]; then
86+
bake.die "Argument must not be empty"
87+
fi
88+
89+
if ! command -v "$cmd" &>/dev/null; then
90+
bake.die "Failed to find command '$cmd'. Please install it before continuing"
91+
fi
92+
}
93+
94+
# @description Edit configuration that affects the behavior of Bake
95+
# @arg $1 string Configuration option to change
96+
# @arg $2 string Value of configuration property
97+
bake.cfg() {
98+
local cfg=$1
99+
local value=$2
100+
101+
case $cfg in
102+
stacktrace)
103+
__bake_cfg_stacktrace=$value
104+
esac
105+
}
106+
107+
# @description Prints stacktrace
108+
# @internal
109+
__bake_print_stacktrace() {
110+
if [ "$__bake_cfg_stacktrace" = 'yes' ]; then
111+
if __bake_is_color; then
112+
printf '\033[4m%s\033[0m\n' 'Stacktrace:'
113+
else
114+
printf '%s\n' 'Stacktrace:'
115+
fi
116+
117+
local i=
118+
for ((i=0;i<${#FUNCNAME[@]}-1;i++)); do
119+
local __bash_source=${BASH_SOURCE[$i]}; __bash_source="${__bash_source##*/}"
120+
printf '%s\n' " in ${FUNCNAME[$i]} ($__bash_source:${BASH_LINENO[$i-1]})"
121+
done; unset -v i __bash_source
122+
fi
123+
} >&2
124+
125+
# @description Function 'trap' calls on 'ERR'
126+
# @internal
127+
__bake_trap_err() {
128+
local error_code=$?
129+
130+
__bake_print_big "<- ERROR"
131+
__bake_internal_error "Your 'Bakefile.sh' did not exit successfully"
132+
__bake_print_stacktrace
133+
134+
exit $error_code
135+
} >&2
136+
137+
# @description Test whether color should be outputed
138+
# @exitcode 0 if should print color
139+
# @exitcode 1 if should not print color
140+
# @internal
141+
__bake_is_color() {
142+
if [[ -v NO_COLOR || $TERM == dumb ]]; then
143+
return 1
144+
else
145+
return 0
146+
fi
147+
}
148+
149+
# @description Prints `$1` formatted as an internal Bake error to standard error
150+
# @arg $1 Text to print
151+
# @internal
152+
__bake_internal_error() {
153+
if __bake_is_color; then
154+
printf "\033[0;31m%s:\033[0m %s\n" "Error (bake)" "$1"
155+
else
156+
printf '%s: %s\n' 'Error (bake)' "$1"
157+
fi
158+
} >&2
159+
160+
# @description Prints `$1` formatted as an internal Bake warning to standard error
161+
# @arg $1 Text to print
162+
# @internal
163+
__bake_internal_warn() {
164+
if __bake_is_color; then
165+
printf "\033[0;33m%s:\033[0m %s\n" "Warn (bake)" "$1"
166+
else
167+
printf '%s: %s\n' 'Warn (bake)' "$1"
168+
fi
169+
} >&2
170+
171+
# @description Calls `__bake_internal_error` and terminates with code 1
172+
# @arg $1 string Text to print
173+
# @internal
174+
__bake_internal_die() {
175+
__bake_internal_error "$1. Exiting"
176+
exit 1
177+
}
178+
179+
# @description Prints `$1` formatted as an error to standard error
180+
# @arg $1 string Text to print
181+
# @internal
182+
__bake_error() {
183+
if __bake_is_color; then
184+
printf "\033[0;31m%s:\033[0m %s\n" 'Error' "$1"
185+
else
186+
printf '%s: %s\n' 'Error' "$1"
187+
fi
188+
} >&2
189+
190+
# @description Nicely prints all 'Basalt.sh' tasks to standard output
191+
# @internal
192+
__bake_print_tasks() {
193+
# shellcheck disable=SC1007,SC2034
194+
local regex="^(([[:space:]]*function[[:space:]]*)?task\.(.*?)\(\)).*"
195+
local line=
196+
printf '%s\n' 'Tasks:'
197+
while IFS= read -r line || [ -n "$line" ]; do
198+
if [[ "$line" =~ $regex ]]; then
199+
printf '%s\n' " -> ${BASH_REMATCH[3]}"
200+
fi
201+
done < "$BAKE_FILE"; unset -v line
202+
} >&2
203+
204+
# @description Prints text that takes up the whole terminal width
205+
# @arg $1 string Text to print
206+
# @internal
207+
__bake_print_big() {
208+
local print_text="$1"
209+
210+
# shellcheck disable=SC1007
211+
local _stty_height= _stty_width=
212+
read -r _stty_height _stty_width < <(
213+
if command -v stty &>/dev/null; then
214+
stty size
215+
else
216+
printf '%s\n' '20 80'
217+
fi
218+
)
219+
220+
local separator_text=
221+
# shellcheck disable=SC2183
222+
printf -v separator_text '%*s' $((_stty_width - ${#print_text} - 1))
223+
printf -v separator_text '%s' "${separator_text// /=}"
224+
if __bake_is_color; then
225+
printf '\033[1m%s %s\033[0m\n' "$print_text" "$separator_text"
226+
else
227+
printf '%s %s\n' "$print_text" "$separator_text"
228+
fi
229+
} >&2
230+
231+
__bake_set_vars() {
232+
unset REPLY; REPLY=
233+
local -i total_shifts=0
234+
235+
local __bake_arg=
236+
for arg; do case $arg in
237+
-f)
238+
BAKE_FILE=$2
239+
if [ -z "$BAKE_FILE" ]; then
240+
__bake_internal_die 'File must not be empty. Exiting'
241+
fi
242+
((total_shifts += 2))
243+
if ! shift 2; then
244+
__bake_internal_die 'Failed to shift'
245+
fi
246+
247+
if [ ! -e "$BAKE_FILE" ]; then
248+
__bake_internal_die "Specified file '$BAKE_FILE' does not exist"
249+
fi
250+
if [ ! -f "$BAKE_FILE" ]; then
251+
__bake_internal_die "Specified path '$BAKE_FILE' is not actually a file"
252+
fi
253+
;;
254+
-h)
255+
local flag_help='yes'
256+
if ! shift; then
257+
__bake_internal_die 'Failed to shift'
258+
fi
259+
esac done
260+
261+
if [ -n "$BAKE_FILE" ]; then
262+
BAKE_ROOT=$(
263+
# shellcheck disable=SC1007
264+
CDPATH= cd -- "${BAKE_FILE%/*}"
265+
printf '%s\n' "$PWD"
266+
)
267+
BAKE_FILE="$BAKE_ROOT/${BAKE_FILE##*/}"
268+
else
269+
if ! BAKE_ROOT=$(
270+
while [ ! -f 'Bakefile.sh' ] && [ "$PWD" != / ]; do
271+
if ! cd ..; then
272+
exit 1
273+
fi
274+
done
275+
276+
if [ "$PWD" = / ]; then
277+
exit 1
278+
fi
279+
280+
printf '%s' "$PWD"
281+
); then
282+
__bake_internal_die "Could not find 'Bakefile.sh'"
283+
fi
284+
BAKE_FILE="$BAKE_ROOT/Bakefile.sh"
285+
fi
286+
287+
if [ "$flag_help" = 'yes' ]; then
288+
cat <<-EOF
289+
Usage: bake [-h] [-f <Bakefile>] [var=value ...] <task> [args ...]
290+
EOF
291+
__bake_print_tasks
292+
exit
293+
fi
294+
295+
REPLY=$total_shifts
296+
}
297+
298+
__bake_main() {
299+
__bake_cfg_stacktrace='yes'
300+
301+
set -Eeo pipefail
302+
shopt -s dotglob extglob globasciiranges globstar lastpipe shift_verbose
303+
export LANG='C' LC_CTYPE='C' LC_NUMERIC='C' LC_TIME='C' LC_COLLATE='C' LC_MONETARY='C' LC_MESSAGES='C' \
304+
LC_PAPER='C' LC_NAME='C' LC_ADDRESS='C' LC_TELEPHONE='C' LC_MEASUREMENT='C' LC_IDENTIFICATION='C' LC_ALL='C'
305+
trap '__bake_trap_err' 'ERR'
306+
307+
# Set `BAKE_{ROOT,FILE}`
308+
BAKE_ROOT=; BAKE_FILE=
309+
__bake_set_vars "$@"
310+
if ! shift "$REPLY"; then
311+
__bake_internal_die 'Failed to shift'
312+
fi
313+
314+
# shellcheck disable=SC1007
315+
local __bake_key= __bake_value=
316+
local __bake_arg=
317+
for __bake_arg; do case $__bake_arg in
318+
*=*)
319+
IFS='=' read -r __bake_key __bake_value <<< "$__bake_arg"
320+
321+
declare -g "$__bake_key"
322+
local -n __bake_variable="$__bake_key"
323+
__bake_variable="$__bake_value"
324+
325+
if ! shift; then
326+
__bake_internal_die 'Failed to shift'
327+
fi
328+
;;
329+
*) break
330+
esac done; unset -v __bake_arg
331+
# Note: Don't unset '__bake_variable' or none of the variables will stay set
332+
unset -v __bake_key __bake_value
333+
334+
local __bake_task="$1"
335+
if [ -z "$__bake_task" ]; then
336+
__bake_internal_error "No valid task supplied"
337+
__bake_print_tasks
338+
exit 1
339+
fi
340+
if ! shift; then
341+
__bake_internal_die 'Failed to shift'
342+
fi
343+
344+
if ! cd "$BAKE_ROOT"; then
345+
__bake_internal_die "Failed to cd"
346+
fi
347+
348+
# shellcheck disable=SC2097,SC1007,SC1090
349+
__bake_task= source "$BAKE_FILE"
350+
351+
if declare -f task."$__bake_task" >/dev/null 2>&1; then
352+
__bake_print_big "-> RUNNING TASK '$__bake_task'"
353+
if declare -f init >/dev/null 2>&1; then
354+
init "$__bake_task"
355+
fi
356+
task."$__bake_task" "$@"
357+
__bake_print_big "<- DONE"
358+
else
359+
__bake_internal_error "Task '$__bake_task' not found"
360+
__bake_print_tasks
361+
exit 1
362+
fi
363+
}
364+
365+
__bake_main "$@"

0 commit comments

Comments
 (0)