Server Tools v2 follows the Composable Building Blocks pattern: each library consists of atomic functions (building blocks) that are composed by high-level operations. The VHost module is additionally built to be webserver-agnostic: vhost.sh holds validation, rollback and logging, while Apache- and Nginx-specific mechanics (config syntax, enabling/disabling sites, reload) live in their own backend modules, dispatched at runtime via $ST_WEBSERVER.
| Layer | Modules | Purpose |
|---|---|---|
| Foundation | core, config, security | Logging, configuration, input validation |
| Infrastructure | backup, install | Backup creation, system install/uninstall |
| Services | database, vhost, ssl, cron, firewall, fail2ban, log, status, user | Business operations |
| Webserver Backend | webserver/apache, webserver/nginx | Config generation, site activation, reload - dispatched via $ST_WEBSERVER |
| Interface | cli, menu | User interaction (CLI or TUI) |
Using database.sh as an example:
Building blocks are pure functions without side effects (no logging, no validation). High-level operations add validation, logging, and rollback. This makes the blocks individually testable and reusable.
Every webserver-specific operation goes through _ws_dispatch <function> <args>, which at runtime forwards to apache_<function> or nginx_<function> based on $ST_WEBSERVER:
SSL/Certbot management and
vhost auditare currently Apache-specific implementations. On Nginx backends these commands print a clear notice instead of an error, but take no action.
server-tools-v2/
βββ bin/
β βββ server-tools # Entry point (~300 LOC)
βββ lib/
β βββ core.sh # Logging, error handling, dependencies
β βββ config.sh # Configuration, defaults, validation
β βββ security.sh # Input validation, escaping, audit
β βββ backup.sh # Backup creation and cleanup
β βββ install.sh # System install/uninstall (one implementation for make + bin/server-tools)
β βββ database.sh # MySQL CRUD
β βββ vhost.sh # VHost orchestration (webserver-agnostic)
β βββ webserver/
β β βββ apache.sh # Apache backend: config, a2en*/a2dis*, reload
β β βββ nginx.sh # Nginx backend: config, site symlinks, reload
β βββ ssl.sh # Let's Encrypt, wildcard SSL (Apache-only)
β βββ cron.sh # Cronjob management via /etc/cron.d
β βββ firewall.sh # UFW firewall
β βββ fail2ban.sh # Fail2Ban jails
β βββ log.sh # Log viewer
β βββ status.sh # Service status, system resources
β βββ user.sh # Per-domain SSH user management
β βββ cli.sh # CLI interface
β βββ menu.sh # TUI interface
βββ conf/
β βββ server-tools.conf.example # Config template
βββ tests/
β βββ test_helper.bash # BATS helper with mock system
β βββ unit/ # 17 test files, 510 tests
βββ .github/workflows/
β βββ ci.yml # CI: ShellCheck + shfmt + BATS + install smoke test
βββ Makefile
βββ README.md
βββ CHANGELOG.md
βββ CONTRIBUTING.md
Each module protects against multiple loading:
[[ -n "${_DATABASE_SOURCED:-}" ]] && return
_DATABASE_SOURCED=1
| Exit Code | Meaning |
|---|---|
| 0 | Success |
| 1 | General error / validation failed |
Server Tools uses
set -euo pipefailin the entry point. All errors go throughdie()which exits with code 1 and logs the error.
Tests use BATS (Bash Automated Testing System) with:
bats-support - Base assertionsbats-assert - Extended assertionsbats-file - File assertions510 tests in 17 files cover all modules:
| Test File | Topic |
|---|---|
cli.bats |
CLI argument parsing, routing, help texts |
install.bats |
install_tools/uninstall_tools: paths, shortcuts, .version marker, safety guards |
database.bats |
MySQL building blocks, credentials, validation |
security.bats |
Input validation (all 13 types), escaping, audit, password |
vhost.bats |
VHost orchestration, redirects, validation |
webserver_apache.bats |
Apache backend: config generation, site activation, audit |
webserver_nginx.bats |
Nginx backend: config generation, site symlinks |
vhost_webserver_router.bats |
_ws_dispatch routing between Apache and Nginx |
ssl.bats |
Certbot integration, wildcard, email fallback |
config.bats |
Config loading, validation, defaults |
backup.bats |
Backup creation, cleanup, auto-backup |
core.bats |
Logging, error handling, root check, mysql_available, mysql_bin |
cron.bats |
Cron name sanitizing, content generation, validation |
log.bats |
tail/grep, log paths, cross-log search |
firewall.bats |
UFW wrapper, port/protocol validation |
fail2ban.bats |
Jail parsing, ban/unban, IP validation |
status.bats |
Service checks, system resources |
# Run all tests
make test
# Test single module
bats tests/unit/database.bats
# With verbose output
bats --verbose-run tests/unit/
Each test runs in an isolated TEST_TMPDIR with mocked system commands. No test touches the real system - install.bats, for example, exercises the real install_tools/uninstall_tools functions against a temp path via ST_INSTALL_DIR/ST_BIN_DIR, not /usr/local.
The GitHub Actions pipeline runs on every push and pull request:
.sh files-bn -ci)sudo make install/uninstall and sudo ./bin/server-tools install/uninstall against real /usr/local paths, including a check that --version never silently falls back to dev| Target | Description |
|---|---|
make help |
Show help |
make install |
System-wide installation (root) - delegates to ./bin/server-tools install |
make uninstall |
Uninstall (root) - delegates to ./bin/server-tools uninstall |
make setup-tests |
Install BATS dependencies |
make test |
Run tests |
make test-verbose |
Run tests with trace output |
make lint |
Run ShellCheck |
make format |
Auto-format with shfmt |
make format-check |
Format check without changes |
make check |
lint + format-check + test |
make clean |
Remove test artifacts |