hack/dev.sh

March 26, 2026 · View on GitHub

hack/dev.sh is the main developer automation script for the shellx project. It provides a unified entry point for linting, formatting checks, and test execution.

Usage

./hack/dev.sh [--coverage] <task> [args...]

If invoked without arguments and fzf is available, an interactive menu is shown to pick a task.

Tasks

TaskDescription
lintRun shellcheck on all shell files under the project root
fmtCheck formatting of lib/** and shellx.sh with shfmt
test-unitRun the unit test suite
test-integrationRun the integration test suite
testRun unit + integration suites
test-actionsRun CI jobs locally via act (ubuntu runners only)
allRun lint + fmt + test sequentially

test-actions mode

test-actions runs CI jobs locally using act. Only ubuntu-based runners are supported — macos-latest jobs are skipped.

By default, all ubuntu-compatible jobs are executed sequentially:

./hack/dev.sh test-actions

To run a single specific job, pass its name as a positional argument:

./hack/dev.sh test-actions lint
./hack/dev.sh test-actions e2e-bash

Available jobs: lint, fmt, unit, integration, e2e-bash, e2e-zsh, e2e-fish.

Note: On Apple Silicon (arm64) the script automatically adds --container-architecture linux/amd64 to work around container compatibility issues.

Coverage mode

By default, tests run in fast mode — bashunit is invoked directly with no extra overhead.

Coverage mode enables:

  • HTML test report generation
  • Code coverage analysis with a minimum threshold of 80%
  • JUnit XML report output
  • All reports are written to target/<timestamp>/

Coverage mode can be activated in two ways:

Flag:

./hack/dev.sh --coverage test
./hack/dev.sh --coverage test-unit

Environment variable (useful for CI pipelines):

SHELLX_TEST_COVERAGE=1 ./hack/dev.sh test

The --coverage flag and the SHELLX_TEST_COVERAGE environment variable are equivalent and can be combined freely.

bashunit

The script requires bashunit to run tests. It is downloaded automatically on first use and cached at target/bashunit. If the file is already present, no download is performed.

The pinned version is controlled by the BASHUNIT_VERSION variable at the top of the script.

BASHUNIT_VERSION="0.33.0"

To upgrade, update that variable and delete target/bashunit so it is re-downloaded on next run.

Dependencies

ToolRequired forInstall
shellchecklintapt install shellcheck / brew install shellcheck
shfmtfmtgo install mvdan.cc/sh/v3/cmd/shfmt@latest / brew install shfmt
curlbashunit downloadpre-installed on most systems
fzfinteractive menu (optional)apt install fzf / brew install fzf

Output artifacts

All artifacts produced under coverage mode are written to a timestamped directory:

target/
└── 20260313-142500/
    ├── unit.html
    ├── unit.xml
    ├── unit-coverage/
    ├── integration.html
    ├── integration.xml
    └── integration-coverage/

The target/ directory is not tracked by git.