Contributing Guidelines

page md_tests_scripts_bats_docs_CONTRIBUTING

Welcome!

Thank you for considering contributing to the development of 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.

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.

Table of contents

Quick links 🔗

Contributor License Agreement

Per the GitHub Terms of Service, be aware that by making a contribution to this project, you agree:

  • to license your contribution under the same terms as this project’s license, and

  • that you have the right to license your contribution under those terms.

See also: “Does my project need an additional contributor agreement? Probably not.”

Code of conduct

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

Asking questions and reporting issues

Asking questions

Please check the README or existing issues first.

If you cannot find an answer to your question, please feel free to hop on our gitter or via IRC (#bats on freenode).

Reporting issues

Before reporting an issue, please use the search feature on the issues page to see if an issue matching the one you’ve observed has already been filed.

Updating or filing a new issue

Information to include

Try to be as specific as possible about your environment and the problem you’re observing. At a minimum, include:

Installation issues

  1. State the version of Bash you’re using bash --version

  1. State your operating system and its version

  1. If you’re installing through homebrew, run brew doctor, and attach the output of brew info bats-core

Bugs/usage issues

  1. State the version of Bash you’re using bash --version

  1. State your operating system and its version

  1. Command line steps or code snippets that reproduce the issue

  1. Any apparently relevant information from the Bash changelog

Also consider using:

  • Bash’s time builtin to collect running times

  • a regression test to add to the suite

  • memory usage as reported by a tool such as memusg

On existing issues

  1. DO NOT add a +1 comment: Use the reactions provided instead

  1. DO add information if you’re facing a similar issue to someone else, but within a different context (e.g. different steps needed to reproduce the issue than previous stated, different version of Bash or BATS, different OS, etc.) You can read on how to do that here: [Information to include][#information-to-include]

  1. DO remember that you can use the Subscribe button on the right side of the page to receive notifications of further conversations or a resolution.

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.

Environment setup

Make sure you have Bash installed per the Environment setup in the README.

Workflow

The basic workflow for submitting changes resembles that of the GitHub Git Flow (a.k.a. GitHub Flow), except that you will be working with your own fork of the repository and issuing pull requests to the original.

  1. Fork the repo on GitHub (look for the “Fork” button)

  1. Clone your forked repo to your local machine

  1. Create your feature branch (git checkout -b my-new-feature)

  1. Develop and your changes as necessary.

  1. Commit your changes (git commit -am ‘Add some feature)

  1. Push to the branch (git push origin my-new-feature`)

  1. Create a new GitHub pull request for your feature branch based against the original repository’s master branch

  1. If your request is accepted, you can delete your feature branch and pull the updated master branch from the original repository into your fork. You may even delete your fork if you don’t anticipate making further changes.

Testing

  • Continuous integration status:

Coding conventions

Formatting

  • Keep all files 80 characters wide.

  • Indent using two spaces.

  • Enclose all variables in double quotes when used to avoid having them interpreted as glob patterns (unless the variable contains a glob pattern) and to avoid word splitting when the value contains spaces. Both scenarios can introduce errors that often prove difficult to diagnose.

    • This is especially important when the variable is used to generate a glob pattern, since spaces may appear in a path value.

    • If the variable itself contains a glob pattern, make sure to set IFS=$ ‘` before using it so that the pattern itself and any matching file names containing spaces are not split apart.

    • Exceptions: Quotes are not required within math contexts, i.e. (( )) or $(( )), and must not be used for variables on the right side of the =~ operator.

  • Enclose all string literals in single quotes.

    • Exception: If the string contains an apostrophe, use double quotes.

  • Use quotes around variables and literals even inside of [[ ]] conditions.

    • This is because strings that contain ‘[’ or ‘]’ characters may fail to compare equally when they should.

    • Exception: Do not quote variables that contain regular expression patterns appearing on the right side of the =~ operator.

  • Only quote arguments to the right of =~ if the expression is a literal match without any metacharacters.

The following are intended to prevent too-compact code:

  • Declare only one item per declare, local, export, or readonly call.

    • Note: This also helps avoid subtle bugs, as trying to initialize one variable using the value of another declared in the same statement will not do what you may expect. The initialization of the first variable will not yet be complete when the second variable is declared, so the first variable will have an empty value.

  • Do not use one-line if, for, while, until, case, or select statements.

  • Do not use && or || to avoid writing if statements.

  • Do not write functions entirely on one line.

  • For case statements: put each pattern on a line by itself; put each command on a line by itself; put the ;; terminator on a line by itself.

Naming

  • 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

  • Gotcha: Never initialize an array on the same line as an export or declare -g statement. See the Gotchas section below for more details.

  • 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 specfies 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 substituion, 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

Gotchas

Open Source License

This software is made available under the MIT License. For the text of the license, see the LICENSE file.

Credits