Thank you for considering contributing to this project's development and/or documentation. Just a reminder: if you're new to this project or to OSS and want to find issues to work on, please check the following labels on issues:
To see all labels and their meanings, check this wiki page.
- Contributing Guidelines
- Gitter channel →: Feel free to come chat with us on Gitter
- README →
- Code of conduct →
- License information →
- Original repository →
- Issues →
- Pull requests →
- Milestones →
- Projects →
Harassment or rudeness of any kind will not be tolerated, period. For specifics, see the CODE_OF_CONDUCT file.
Please check the documentation or existing discussions and issues first.
If you cannot find an answer to your question, please feel free to hop on our Gitter.
We love documentation and people who love documentation!
If you love writing clear, accessible docs, please don't be shy about pull requests. Remember: docs are just as important as code.
Also: no typo is too small to fix! Really. Of course, batches of fixes are preferred, but even one nit is one nit too many.
To run all tests:
bin/bats test
To run a single test file:
bin/bats test/file.bats
When running from a terminal, Bats uses the pretty formatter by default.
However, to debug Bats you might need to see the raw test output.
The cat formatter is intended as an internal debugging tool because
it does not process test outputs.
To use it, run Bats with the --formatter cat
option.
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 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
The official bash logo is copyrighted by the Free Software Foundation, 2016 under the Free Art License
This guide borrows heavily from @mbland's go-script-bash (with some sections directly quoted), which in turn was drafted with tips from Wrangling Web Contributions: How to Build a CONTRIBUTING.md and with some inspiration from the Atom project's CONTRIBUTING.md file.