--- title: 'Contributing | Coding Style' --- ## Bash and Shell When refactoring, writing or altering scripts, that is Shell and bash scripts, in any way, adhere to these rules: 1. **Adjust your style of coding to the style that is already present**! Even if you do not like it, this is due to consistency. There was a lot of work involved in making all scripts consistent. 2. **Use `shellcheck` to check your scripts**! Your contributions are checked by GitHub Actions too, so you will need to do this. You can **lint your work with `make lint`** to check against all targets. 3. **Use the provided `.editorconfig`** file. 4. Use `/bin/bash` instead of `/bin/sh`. Adjust the style accordingly. 5. `setup.sh` provides a good starting point to look for. 6. When appropriate, use the `set` builtin. We recommend `set -euEo pipefail` or `set -uE`. ## Styling rules ### If-Else-Statements ```bash # when using braces, use double braces # remember you do not need "" when using [[ ]] if [[ ]] && [[ -f ${FILE} ]] then # when running commands, you don't need braces elif else fi # equality checks with numbers are done # with -eq/-ne/-lt/-ge, not != or == if [[ ${VAR} -ne 42 ]] || [[ ${SOME_VAR} -eq 6 ]] then fi ``` ### Variables & Braces !!! attention Variables are always uppercase. We always use braces. If you forgot this and want to change it later, you can use [this link][regex]. The used regex is `\$([^{("\\'\/])([a-zA-Z0-9_]*)([^}\/ \t'"\n.\]:(=\\-]*)`, where you should in practice be able to replace all variable occurrences without braces with occurrences with braces. ```bash # good local VAR="good" local NEW="${VAR}" # bad -> CI will fail var="bad" new=$var ``` ### Loops Like `if-else`, loops look like this ```bash for / while do done ``` ### Functions It's always nice to see the use of functions as it also provides a clear structure. If scripts are small, this is unnecessary, but if they become larger, please consider using functions. When doing so, provide `function _main`. ```bash function _ { # variables that can be local should be local local } ``` ### Error Tracing A construct to trace error in your scripts looks like this. Remember: Remove `set -x` in the end. This is for debugging purposes only. ```bash set -xeuEo pipefail trap '__log_err ${FUNCNAME[0]:-"?"} ${BASH_COMMAND:-"?"} ${LINENO:-"?"} ${?:-"?"}' ERR SCRIPT='name_of_this_script.sh' function __log_err { printf "\n––– \e[1m\e[31mUNCHECKED ERROR\e[0m\n%s\n%s\n%s\n%s\n\n" \ " – script = ${SCRIPT:-${0}}" \ " – function = ${1} / ${2}" \ " – line = ${3}" \ " – exit code = ${4}" 1>&2 } ``` ### Comments, Descriptiveness & An Example Comments should only describe non-obvious matters. Comments should start lowercase when they aren't sentences. Make the code **self-descriptive** by using meaningful names! Make comments not longer than approximately 80 columns, then wrap the line. A positive example, which is taken from `start-mailserver.sh`, would be ```bash function _setup_postfix_aliases { _notify 'task' 'Setting up Postfix Aliases' : >/etc/postfix/virtual : >/etc/postfix/regexp if [[ -f /tmp/docker-mailserver/postfix-virtual.cf ]] then # fixing old virtual user file if grep -q ",$" /tmp/docker-mailserver/postfix-virtual.cf then sed -i -e "s/, /,/g" -e "s/,$//g" /tmp/docker-mailserver/postfix-virtual.cf fi cp -f /tmp/docker-mailserver/postfix-virtual.cf /etc/postfix/virtual # the `to` is important, don't delete it # shellcheck disable=SC2034 while read -r FROM TO do # Setting variables for better readability UNAME=$(echo "${FROM}" | cut -d @ -f1) DOMAIN=$(echo "${FROM}" | cut -d @ -f2) # if they are equal it means the line looks like: "user1 other@domain.tld" [[ "${UNAME}" != "${DOMAIN}" ]] && echo "${DOMAIN}" >> /tmp/vhost.tmp done < <(grep -v "^\s*$\|^\s*\#" /tmp/docker-mailserver/postfix-virtual.cf || true) else _notify 'inf' "Warning 'config/postfix-virtual.cf' is not provided. No mail alias/forward created." fi ... } ``` ## YAML When formatting YAML files, use [Prettier][prettier], an opinionated formatter. There are many plugins for IDEs around. [semver]: https://semver.org/ [regex]: https://regex101.com/r/ikzJpF/7 [prettier]: https://prettier.io