Skip to content

Latest commit

 

History

History
232 lines (179 loc) · 9.29 KB

CONTRIBUTING.md

File metadata and controls

232 lines (179 loc) · 9.29 KB

Contributing Guidelines

Welcome!

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.

Table of contents

Quick links

Code of conduct

Harassment or rudeness of any kind will not be tolerated, period. For specifics, see the CODE_OF_CONDUCT file.

Asking questions

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. Gitter

Updating documentation

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.

Testing

  • Continuous integration status: Tests

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.

Coding conventions

Use (shfmt)[https://github.com/mvdan/sh#shfmt] and ShellCheck. The CI will enforce this.

Use snake_case for all identifiers.

Function declarations

  • Declare functions without the function keyword.
  • Strive to always use return, never exit, 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.

Variable and parameter declarations

  • Declare all variables inside functions using local.
  • Declare temporary file-level variables using declare. Use unset 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 a local variable of the same name.
  • Don't use type flags with declare or local. 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.:
    format_summary() {
      local cmd_name="$1"
      local summary="$2"
      local longest_name_len="$3"
    For very short functions, this may not be necessary, e.g.:
    has_spaces() {
      [[ "$1" != "${1//[[:space:]]/}" ]]
    }

Command substitution

  • 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.

Process substitution

  • 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.

Conditionals and loops

  • 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.

Generating output

  • Use printf instead of echo. Both are Bash builtins, and there's no perceptible performance difference when running Bats under the time builtin. However, printf provides a more consistent experience in general, as echo has limitations to the arguments it accepts, and even the same version of Bash may produce different results for echo based on how the binary was compiled. See Stack Overflow: Why is printf better than echo? for excruciating details.

Signal names

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

Credits

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.