Shell script
============
Directory layout
----------------
For builds that involve independent command-line tools, use `Make `__, and follow DataMade's `Making Data Guidelines `__ and Clark Grubb's `Makefile Style Guide `__, for example: `standard_profile_template `__
If a repository has scripts to set itself up and/or update itself, follow GitHub's `Scripts to Rule Them All `__, for example: `deploy `__ and `standard_profile_template `__
Filename conventions
~~~~~~~~~~~~~~~~~~~~
``sh`` and ``bash`` scripts should use the ``.sh`` extension, unless they are in a ``script/`` directory.
Shell options
-------------
Start shell scripts with `set -euo pipefail `__. If the script explicitly handles unset variables, omit ``-u``. To see which command failed due to the ``-e`` option, add ``-x``.
.. seealso::
`The Set Builtin `__
Code style
----------
Check ``/bin/sh`` scripts using `checkbashisms `__.
Check shell scripts using `shellcheck `__.
Style shell scripts using `shfmt `__: for example, ``shfmt -w -i 4 -sr (shfmt -f .)``.
Use:
- ``sh`` instead of ``bash``, where possible. Bash is needed for:
- `Shell Parameter Expansion `__ (``//``, ``##``, ``%%``, etc.)
- `Process Substitution `__ (``<()``)
- `The mapfile Builtin `__
- `The pipefail Option `__
- ``[ ]`` instead of ``test``
- ``[ ]`` instead of ``[[ ]]`` in Bash, `unless required `__
- ``$NAME`` instead of ``${NAME}``, unless followed by a word character
- Subshells to temporarily change directory, for example:
.. code-block:: bash
(
cd subdir/
mv x.txt y.txt
)
Instead of:
.. code-block:: bash
cd subdir/
mv x.txt y.txt
cd .. # AVOID
And:
- Avoid ``set -x`` in scripts run by continuous integration, because it will expand any secret variables
.. _shell-ci:
Continuous integration
----------------------
Create a ``.github/workflows/shell.yml`` file. As a base, use:
.. literalinclude:: samples/shell.yml
:language: yaml
.. tip::
In most cases, you can reuse the `shell `__ workflow. For example:
.. code-block:: yaml
jobs:
lint:
uses: open-contracting/.github/.github/workflows/shell.yml@main
permissions:
contents: read
with:
ignore: file.sh
Maintenance
~~~~~~~~~~~
Find repositories with shell scripts but without ``shell.yml`` files:
.. code-block:: bash
find . \( -path '*/script/*' -o -name '*.sh' \) ! -path '*/.mypy_cache/*' ! -path '*/node_modules/*' ! -path '*/vendor/*' -exec bash -c 'if [[ -z $(find $(echo {} | cut -d/ -f2) -name shell.yml) ]]; then echo {}; fi' \;
Reference
---------
- `Shell Command Language `__
- `Bash Conditional Constructs `__
- `Wizard Zines Bite Size series `__