Thanks for thinking about helping us!
After cloning the repository, then you'll want to bootstrap your system:
script/bootstrap
This will ensure your system has all the programs needed to test everything.
To run all lint checks:
script/lint
To run all tests:
script/test
To run all Bats tests:
bats tests
To run specific Bats tests:
bats tests/<file>.bats
These coding conventions were initially copied from bats-core's coding conventions, but I expect them to diverge over time.
Use shfmt
and ShellCheck. The CI will enforce this.
Use snake_case
for all identifiers.
- Declare functions without the
function
keyword. - Strive to always use
return
, neverexit
, unless an error condition is severe enough to warrant it.- Calling
exit
makes it difficult for the caller to recover from an error, or to compose new commands from existing ones.
- Calling
- Declare all variables inside functions using
local
. - Declare temporary file-level variables using
declare
. Useunset
to remove them when finished. - Don't use
local -r
, as a readonly local variable in one scope can cause a conflict when it calls a function that declares alocal
variable of the same name. - Don't use type flags with
declare
orlocal
. Assignments to integer variables in particular may behave differently, and it has no effect on array variables. - For most functions, the first lines should use
local
declarations to assign the original positional parameters to more meaningful names, e.g.:For very short functions, this may not be necessary, e.g.:format_summary() { local cmd_name="$1" local summary="$2" local longest_name_len="$3"
has_spaces() { [[ "$1" != "${1//[[:space:]]/}" ]] }
- If possible, don't. While this capability is one of Bash's core strengths, every new process created by Bats makes the framework slower, and speed is critical to encouraging the practice of automated testing. (This is especially true on Windows, where process creation is one or two orders of magnitude slower. See https://github.com/bats-core/bats-core#8 for an illustration of the difference avoiding subshells makes.) Bash is quite powerful; see if you can do what you need in pure Bash first.
- If you need to capture the output from a function, store the output using
printf -v
instead if possible.-v
specifies the name of the variable into which to write the result; the caller can supply this name as a parameter. - If you must use command substitution, use
$()
instead of backticks, as it's more robust, more searchable, and can be nested.
- If possible, don't use it. See the advice on avoiding subprocesses and using
printf -v
in the Command substitution section above. - Use wherever necessary and possible, such as when piping input into a
while
loop (which avoids having the loop body execute in a subshell) or running a command taking multiple filename arguments based on output from a function or pipeline (e.g.diff
). - Warning: It is impossible to directly determine the exit status of a process substitution; emitting an exit status as the last line of output is a possible workaround.
- Always use
[[
and]]
for evaluating variables. Per the guideline under Formatting, quote variables and strings within the brackets, but not regular expressions (or variables containing regular expressions) appearing on the right side of the=~
operator.
- Use
printf
instead ofecho
. Both are Bash builtins, and there's no perceptible performance difference when running Bats under thetime
builtin. However,printf
provides a more consistent experience in general, asecho
has limitations to the arguments it accepts, and even the same version of Bash may produce different results forecho
based on how the binary was compiled. See Stack Overflow: Why is printf better than echo? for excruciating details.
Always use upper case signal names (e.g. trap - INT EXIT
) to avoid locale
dependent errors. In some locales (for example Turkish, see
Turkish dotless i) lower
case signal names cause Bash to error. An example of the problem:
$ echo "tr_TR.UTF-8 UTF-8" >> /etc/locale.gen && locale-gen tr_TR.UTF-8 # Ubuntu derivatives
$ LC_CTYPE=tr_TR.UTF-8 LC_MESSAGES=C bash -c 'trap - int && echo success'
bash: line 0: trap: int: invalid signal specification
$ LC_CTYPE=tr_TR.UTF-8 LC_MESSAGES=C bash -c 'trap - INT && echo success'
success
This project uses several tools to ensure high quality: