Bash Code Reviews
Style Guide
Developers should follow Google's Bash Style Guide.
Code Analysis / Linting
Projects must check bash code with shellcheck as part of the CI process. Apart from linting, shfmt can be used to automatically format shell scripts. There are few vscode code extensions which are based on shfmt like shell-format which can be used to automatically format shell scripts.
Project Setup
vscode-shellcheck
Shellcheck extension should be used in VS Code, it provides static code analysis capabilities and auto fixing linting issues. To use vscode-shellcheck in vscode do the following:
Install shellcheck on your machine
For macOS
For Ubuntu:
Install shellcheck on vscode
Find the vscode-shellcheck extension in vscode and install it.
Automatic Code Formatting
shell-format
shell-format extension does automatic formatting of your bash scripts, docker files and several configuration files. It is dependent on shfmt which can enforce google style guide checks for bash. To use shell-format in vscode do the following:
Install shfmt(Requires Go 1.13 or later) on your machine
Install shell-format on vscode
Find the shell-format extension in vscode and install it.
Build Validation
To automate this process in Azure DevOps you can add the following snippet to you azure-pipelines.yaml
file. This will lint any scripts in the ./scripts/
folder.
- bash: |
echo "This checks for formatting and common bash errors. See wiki for error details and ignore options: https://github.com/koalaman/shellcheck/wiki/SC1000"
export scversion="stable"
wget -qO- "https://github.com/koalaman/shellcheck/releases/download/${scversion?}/shellcheck-${scversion?}.linux.x86_64.tar.xz" | tar -xJv
sudo mv "shellcheck-${scversion}/shellcheck" /usr/bin/
rm -r "shellcheck-${scversion}"
shellcheck ./scripts/*.sh
displayName: "Validate Scripts: Shellcheck"
Also, your shell scripts can be formatted in your build pipeline by using the shfmt
tool. To integrate shfmt
in your build pipeline do the following:
- bash: |
echo "This step does auto formatting of shell scripts"
shfmt -l -w ./scripts/*.sh
displayName: "Format Scripts: shfmt"
Unit testing using shunit2 can also be added to the build pipeline, using the following block:
- bash: |
echo "This step unit tests shell scripts by using shunit2"
./shunit2
displayName: "Format Scripts: shfmt"
Pre-Commit Hooks
All developers should run shellcheck and shfmt as pre-commit hooks.
Step 1- Install pre-commit
Run pip install pre-commit
to install pre-commit.
Alternatively you can run brew install pre-commit
if you are using homebrew.
Step 2- Add shellcheck and shfmt
Add .pre-commit-config.yaml file to root of the go project. Run shfmt on pre-commit by adding it to .pre-commit-config.yaml file like below.
- repo: git://github.com/pecigonzalo/pre-commit-fmt
sha: master
hooks:
- id: shell-fmt
args:
- --indent=4
Step 3
Run $ pre-commit install
to set up the git hook scripts
Dependencies
Bash scripts are often used to 'glue together' other systems and tools. As such, Bash scripts can often have numerous and/or complicated dependencies. Consider using Docker containers to ensure that scripts are executed in a portable and reproducible environment that is guaranteed to contain all the correct dependencies. To ensure that dockerized scripts are nevertheless easy to execute, consider making the use of Docker transparent to the script's caller by wrapping the script in a 'bootstrap' which checks whether the script is running in Docker and re-executes itself in Docker if it's not the case. This provides the best of both worlds: easy script execution and consistent environments.
if [[ "${DOCKER}" != "true" ]]; then
docker build -t my_script -f my_script.Dockerfile . > /dev/null
docker run -e DOCKER=true my_script "$@"
exit $?
fi
# ... implementation of my_script here can assume that all of its dependencies exist since it's always running in Docker ...
Code Review Checklist
In addition to the Code Review Checklist you should also look for these bash specific code review items
- Does this code use Built-in Shell Options like set -o, set -e, set -u for execution control of shell scripts ?
- Is the code modularized? Shell scripts can be modularized like python modules. Portions of bash scripts should be sourced in complex bash projects.
- Are all exceptions handled correctly? Exceptions should be handled correctly using exit codes or trapping signals.
- Does the code pass all linting checks as per shellcheck and unit tests as per shunit2 ?
- Does the code uses relative paths or absolute paths? Relative paths should be avoided as they are prone to environment attacks. If relative path is needed, check that the
PATH
variable is set. - Does the code take credentials as user input? Are the credentials masked or encrypted in the script?