# 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:

- [help wanted][helpwantedlabel]
- [docs][docslabel]
- [good first issue][goodfirstissuelabel]

[docslabel]:           https://github.com/bats-core/bats-core/labels/docs
[helpwantedlabel]:     https://github.com/bats-core/bats-core/labels/help%20wanted
[goodfirstissuelabel]: https://github.com/bats-core/bats-core/labels/good%20first%20issue

To see all labels and their meanings, [check this wiki page][labelswiki].

[labelswiki]: https://github.com/bats-core/bats-core/wiki/GitHub-Issue-Labels

## Table of contents

* [Contributing Guidelines](#contributing-guidelines)
  * [Welcome!](#welcome)
  * [Table of contents](#table-of-contents)
  * [Quick links](#quick-links)
  * [Code of conduct](#code-of-conduct)
  * [Asking questions](#asking-questions)
  * [Updating documentation](#updating-documentation)
  * [Testing](#testing)
  * [Coding conventions](#coding-conventions)
      * [Function declarations](#function-declarations)
      * [Variable and parameter declarations](#variable-and-parameter-declarations)
      * [Command substitution](#command-substitution)
      * [Process substitution](#process-substitution)
      * [Conditionals and loops](#conditionals-and-loops)
      * [Generating output](#generating-output)
      * [Signal names](#signal-names)
      * [Gotchas](#gotchas)
  * [Open Source License](#open-source-license)
  * [Credits](#credits)

## Quick links

- [Gitter channel →][gitterurl]: Feel free to come chat with us on Gitter
- [README →][README]
- [Code of conduct →][CODE_OF_CONDUCT]
- [License information →][LICENSE]
- [Original repository →][repohome]
- [Issues →][repoissues]
- [Pull requests →][repoprs]
- [Milestones →][repomilestones]
- [Projects →][repoprojects]

[README]: https://github.com/bats-core/bats-core/blob/master/README.md
[CODE_OF_CONDUCT]: https://github.com/bats-core/bats-core/blob/master/docs/CODE_OF_CONDUCT.md
[LICENSE]: https://github.com/bats-core/bats-core/blob/master/LICENSE.md

## 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][documentation] or existing [discussions][] and [issues][repoissues] first.

If you cannot find an answer to your question, please feel free to hop on our 
[Gitter][gitterurl]. [![Gitter](https://badges.gitter.im/bats-core/bats-core.svg)](https://gitter.im/bats-core/bats-core)

## 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](https://github.com/bats-core/bats-core/workflows/Tests/badge.svg)](https://github.com/bats-core/bats-core/actions?query=workflow%3ATests)

To run all tests:
```sh
bin/bats test
```

To run a single test file:
```sh
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](https://www.shellcheck.net/). 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.:
  ```bash
  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.:
  ```bash
  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][win-slow]. See [bats-core/bats-core#8][pr-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.

[win-slow]: https://rufflewind.com/2014-08-23/windows-bash-slow
[pr-8]: https://github.com/bats-core/bats-core/pull/8

### 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?][printf-vs-echo] for
  excruciating details.

[printf-vs-echo]: https://unix.stackexchange.com/a/65819

### 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](https://en.wikipedia.org/wiki/Dotted_and_dotless_I)) lower 
case signal names cause Bash to error. An example of the problem:

```bash
$ 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](https://github.com/odb/official-bash-logo) is copyrighted
by the [Free Software Foundation](https://www.fsf.org/), 2016 under the [Free Art License](http://artlibre.org/licence/lal/en/)

This guide borrows **heavily** from [@mbland's go-script-bash][gsb] (with some 
sections directly quoted), which in turn was
drafted with tips from [Wrangling Web Contributions: How to Build
a CONTRIBUTING.md][moz] and with some inspiration from [the Atom project's
CONTRIBUTING.md file][atom].

[gsb]:  https://github.com/mbland/go-script-bash/blob/master/CONTRIBUTING.md
[moz]:  https://mozillascience.github.io/working-open-workshop/contributing/
[atom]: https://github.com/atom/atom/blob/master/CONTRIBUTING.md

[discussions]:    https://github.com/bats-core/bats-core/discussions
[documentation]:  https://bats-core.readthedocs.io/
[repoprojects]:   https://github.com/bats-core/bats-core/projects
[repomilestones]: https://github.com/bats-core/bats-core/milestones
[repoprs]:        https://github.com/bats-core/bats-core/pulls
[repoissues]:     https://github.com/bats-core/bats-core/issues
[repohome]:       https://github.com/bats-core/bats-core

[osmit]:          https://opensource.org/licenses/MIT

[gitterurl]:      https://gitter.im/bats-core/bats-core
