|
| 1 | +--- |
| 2 | +applyTo: "**/*.sh" |
| 3 | +--- |
| 4 | +# Shell Script Conventions |
| 5 | + |
| 6 | +Target: **bash**. Must work on macOS, Linux, Windows (WSL/Git Bash). |
| 7 | + |
| 8 | +## Header |
| 9 | + |
| 10 | +- **First line: description comment** — used by [SCRIPTS.md](../../SCRIPTS.md). Format: `# Description sentence. Requires "other_script.sh".` |
| 11 | +- Include required env vars, required tools, usage examples in description or `usage()` function |
| 12 | +- Blank line after initial comment before code starts |
| 13 | + |
| 14 | +## Shebang and Strict Mode |
| 15 | + |
| 16 | +- First executable line: `#!/usr/bin/env bash` |
| 17 | +- Enable strict mode immediately after: `set -euo pipefail` |
| 18 | +- Set safe word-splitting: `IFS=$'\n\t'` |
| 19 | +- `pipefail` propagates pipe failures — `set -e` bypassed in subshells and command groups; document where this applies |
| 20 | + |
| 21 | +## Style |
| 22 | + |
| 23 | +- POSIX-compliant as reasonable |
| 24 | +- Always `"${variable}"` — curly braces + double quotes on every expansion |
| 25 | +- Use `$(...)` for command substitution — not backticks |
| 26 | +- No abbreviations. Write out variable and function names. `i`, `j`, `k` for indices OK |
| 27 | +- Prefer named functions over code block comments. Comments explain *why*, not *what* |
| 28 | +- Split into meaningful functions — ideally pure, easy to reason about |
| 29 | +- Apply basic functional programming where it improves readability (piping, composition) |
| 30 | + |
| 31 | +## Naming |
| 32 | + |
| 33 | +- No generic names — no "util", "helper", "common", "shared" |
| 34 | +- DRY only for domain-related concerns. Don't extract one-off operations |
| 35 | + |
| 36 | +## Variables and Scope |
| 37 | + |
| 38 | +- `local` for function-scoped variables |
| 39 | +- `readonly` for constants |
| 40 | +- `${VAR:-default}` for defaults |
| 41 | +- `[[ ]]` over `[ ]` |
| 42 | +- Quote all paths: `"${directory}/${filename}"` |
| 43 | + |
| 44 | +## Argument Parsing |
| 45 | + |
| 46 | +- Provide `usage()` — list parameters, flags, env vars, copy-pastable examples with expected output |
| 47 | +- Support at minimum `--help` (print `usage()`, exit 0) |
| 48 | +- Support `--dry-run` for destructive or irreversible operations |
| 49 | + |
| 50 | +## Dependency Checks |
| 51 | + |
| 52 | +- Check required tools early: `command -v <tool> || { echo "Install <tool>: <url>"; exit 1; }` |
| 53 | + |
| 54 | +## Portability |
| 55 | + |
| 56 | +- Prefer POSIX idioms; guard GNU vs BSD differences explicitly |
| 57 | +- `sed -i` requires empty suffix on macOS: `sed -i ''` vs Linux `sed -i` |
| 58 | +- `date -d` GNU only — use `date -v` on macOS or detect OS and branch |
| 59 | + |
| 60 | +## Error Handling and Exit Codes |
| 61 | + |
| 62 | +- Trap `ERR`/`EXIT` for cleanup and meaningful error messages |
| 63 | +- Return meaningful non-zero exit codes — document semantics in `usage()` |
| 64 | + |
| 65 | +## Security |
| 66 | + |
| 67 | +- No `eval` — use arrays, named variables, or functions |
| 68 | +- Sanitize external inputs before use in commands or filenames |
| 69 | +- Never log or write secrets to temp files |
| 70 | +- Use `mktemp` for temp files; restrict permissions with `chmod 600` or `umask 077` |
| 71 | +- Clean up temp files in `trap ... EXIT` |
| 72 | + |
| 73 | +## Linting, Formatting, and CI |
| 74 | + |
| 75 | +- Verify all scripts with `shellcheck`; run in CI |
| 76 | +- Format with `shfmt`; run in CI |
| 77 | +- Document `shellcheck` exceptions inline with justification: `# shellcheck disable=SC2xxx — reason` |
| 78 | +- Test scripts: name with prefix `test` (e.g. `testMyFeature.sh`) — `runTests.sh` auto-discovers in `scripts/` and `domains/` dirs |
0 commit comments