Installation
bashunit ships as a single-file executable. Pick the option that fits your project: install.sh (universal), npm (Node.js projects), Brew (macOS/Linux global), MacPorts, Nix, or bashdep.
Requirements
bashunit requires Bash 3.0 or newer.
On Windows, install WSL (wsl --install from an elevated PowerShell, then reboot) and run every command below inside the WSL shell.
Everything else is optional, but some features need it:
| Tool | Needed for | Without it |
|---|---|---|
jq | JSON assertions | the test is skipped, not failed |
git | --changed, --coverage-diff, upgrade, failure diffs | those flags error out; diffs print plain values |
inotifywait (Linux) / fswatch (macOS) | watch | falls back to polling |
install.sh
There is a tool that will generate an executable with the whole library in a single file:
curl -s https://bashunit.com/install.sh | bashThis will create a file inside a lib folder, such as lib/bashunit.
Automatic checksum verification
install.sh verifies the download against the release checksum asset and aborts on a mismatch, so a tampered or corrupted download never lands. There are three states:
- unset (default): verifies, aborts on a mismatch, and only warns and continues when the checksum asset or a sha256 tool is unavailable
BASHUNIT_VERIFY_CHECKSUM=true: also aborts when verification is impossibleBASHUNIT_VERIFY_CHECKSUM=false: skips verification entirely
The GitHub Action always passes the variable, so verify-checksum: 'true' (its default) is the strict mode. The manual check below is only needed when you skip verification.
Verify
# Verify the sha256sum for latest stable: 0.48.0
DIR="lib"; KNOWN_HASH="9e27d930a505fcdc46e0c3275ca943d412e5df4b51dc1f5b5219d794d3b1893d"; FILE="$DIR/bashunit"; [ "$(shasum -a 256 "$FILE" | awk '{ print $1 }')" = "$KNOWN_HASH" ] && echo -e "✓ \033[1mbashunit\033[0m verified." || { echo -e "✗ \033[1mbashunit\033[0m corrupt"; rm "$FILE"; }TIP
You can find the checksum for each version inside GitHub's releases. E.g.:
https://github.com/TypedDevs/bashunit/releases/download/0.48.0/checksumDefine custom tag and folder
The installation script can receive arguments (in any order):
curl -s https://bashunit.com/install.sh | bash -s [dir] [version][dir]: the destiny directory to save the executable bashunit;libby default[version]: the release to download, for instance0.48.0;latestby default.
TIP
You can use beta as [version] to get the next non-stable preview release. We try to keep it stable, but there is no promise that we won't change functions or their signatures without prior notice.
TIP
Committing (or not) this file to your project it's up to you. In the end, it is a dev dependency.
npm
bashunit on npm is the recommended option for Node.js projects.
# Install as dev dependency
npm install --save-dev bashunit
# Run via npx (resolves node_modules/.bin/bashunit)
npx bashunit tests/# Install on PATH
npm install -g bashunit
# Run directly, no npx needed
bashunit tests/# No install, runs the latest release
npx bashunit@latest tests/Per-project: package.json script
Only relevant for the per-project install (global and one-shot don't touch package.json). Define a script so contributors and CI share one command:
{
"scripts": {
"test": "bashunit tests/"
},
"devDependencies": {
"bashunit": "^0.48.0"
}
}Then run:
npm test
# or any custom script name
npm run <script-name>Windows (native) not supported
The npm package declares "os": ["darwin", "linux"], so npm install bashunit on native Windows (PowerShell / cmd) fails with EBADPLATFORM:
npm error code EBADPLATFORM
npm error notsup Unsupported platform for bashunit@x.y.z: wanted {"os":"darwin,linux"} (current: {"os":"win32"})Fix: install WSL and run npm install --save-dev bashunit inside the WSL shell. Alternatively use the install.sh route from WSL.
WARNING
The npm package only ships the prebuilt single-file binary (no src/ tree). You cannot source internals from node_modules/bashunit/ - use the bashunit command. To vendor or extend the framework, use install.sh or clone the repository.
Brew
You can install bashunit globally on macOS or Linux using brew.
brew install bashunitMacPorts
On macOS, you can also install bashunit via MacPorts:
sudo port install bashunitNix
bashunit is packaged in nixpkgs, so no install step is needed to try it:
# Run without installing anything
nix-shell -p bashunit --run "bashunit tests/"nix run nixpkgs#bashunit -- tests/# Install for your user
nix profile install nixpkgs#bashunitTo pin it for a project, add it to a shell.nix:
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
buildInputs = [ pkgs.bashunit ];
}nixpkgs lags the latest release
The nixpkgs version trails this project's releases, and channels lag further still. Run nix-shell -p bashunit --run "bashunit --version" to see what you would get. When you need a specific version, use install.sh with an explicit [version] argument instead — it is unaffected by channel lag.
bashdep
You can manage your dependencies using bashdep, a simple dependency manager for bash.
# Ensure bashdep is installed
[ ! -f lib/bashdep ] && {
mkdir -p lib
curl -fsSLo lib/bashdep \
https://github.com/Chemaclass/bashdep/releases/latest/download/bashdep
chmod +x lib/bashdep
}
# Add latest bashunit release to your dependencies
DEPENDENCIES=(
"https://github.com/TypedDevs/bashunit/releases/download/0.48.0/bashunit"
)
# Load, configure and run bashdep
source lib/bashdep
bashdep::setup dir="lib" silent=false
bashdep::install "${DEPENDENCIES[@]}"# Run this inside the WSL shell, see Requirements above.
# Ensure bashdep is installed
[ ! -f lib/bashdep ] && {
mkdir -p lib
curl -fsSLo lib/bashdep \
https://github.com/Chemaclass/bashdep/releases/latest/download/bashdep
chmod +x lib/bashdep
}
# Add latest bashunit release to your dependencies
DEPENDENCIES=(
"https://github.com/TypedDevs/bashunit/releases/download/0.48.0/bashunit"
)
# Load, configure and run bashdep
source lib/bashdep
bashdep::setup dir="lib" silent=false
bashdep::install "${DEPENDENCIES[@]}"Downloading 'bashunit' to 'lib'...
> bashunit installed successfully in 'lib'
> installed 1, skipped 0, failed 0Installs are idempotent: bashdep records what it fetched in lib/.bashdep.lock, so a second run prints > bashunit already exists in 'lib', skipping. and only re-downloads when you bump the version in the URL.
Pin bashdep itself for reproducible builds
releases/latest/download always fetches the newest bashdep. For byte-identical installs across machines and CI, swap it for an explicit tag and verify it against the published checksum:
curl -fsSLo lib/bashdep \
https://github.com/Chemaclass/bashdep/releases/download/0.9.0/bashdep
curl -fsSLo checksum \
https://github.com/Chemaclass/bashdep/releases/download/0.9.0/checksum
( cd lib && shasum -a 256 -c ../checksum ) && chmod +x lib/bashdepGitHub Actions
The official TypedDevs/bashunit action installs the binary in one step. Pin it to the floating major tag @v0 to track the latest release within a major, or to a commit SHA for an immutable, supply-chain-safe install (keeps static analyzers such as zizmor happy):
# .github/workflows/bashunit-tests.yml
name: Tests
on: [pull_request, push]
jobs:
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
# @v0 tracks the latest release within the v0 major.
# For an immutable pin use a commit SHA: TypedDevs/bashunit@<sha> # 0.48.0
- uses: TypedDevs/bashunit@v0
with:
version: '0.48.0' # omit for the version pinned at this ref
directory: lib # optional, "lib" by default
add-to-path: 'true' # optional, "true" by default
verify-checksum: 'true' # optional, "true" by default
annotations: auto # optional, "auto" by default ("never" to turn off)
# add-to-path puts the binary on $PATH, so just call "bashunit":
- run: bashunit tests# .github/workflows/bashunit-tests.yml
name: Tests
on: [pull_request, push]
jobs:
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
# Install and run the suite in a single step via the `args` input.
- uses: TypedDevs/bashunit@v0
with:
version: '0.48.0'
args: tests/ --strict# .github/workflows/bashunit-tests.yml
name: Tests
on: [pull_request, push]
jobs:
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- run: curl -s https://bashunit.com/install.sh | bash
- run: ./lib/bashunit tests# .github/workflows/bashunit-tests.yml
name: Tests
on: [pull_request, push]
jobs:
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with: { node-version: 22 }
- run: npm ci
- run: npx bashunit tests/Inputs: version (default: the release pinned at this action ref — @v0 tracks the newest release at the time it was moved; pass latest to always take the newest), directory (default lib), add-to-path (default true), verify-checksum (default true), args (default empty — when set, runs bashunit <args> after installing), annotations (default auto — GitHub Actions annotations for failing tests; never turns them off, always forces them). Outputs: path (binary path relative to the workspace), version (installed version).
verify-checksum validates the downloaded binary against the release checksum asset (sha256) and fails the install on any mismatch. Set it to false only when pinning a release published before checksum assets existed.
Keep the SHA pin fresh automatically
A commit-SHA pin is the most secure, but bumping it by hand is tedious. Let a bot do it and keep the # 0.48.0 comment as the human-readable tracker.
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:recommended"],
"packageRules": [
{
"matchManagers": ["github-actions"],
"matchPackageNames": ["TypedDevs/bashunit"],
"pinDigests": true
}
]
}version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weeklyRenovate updates the pinned SHA and refreshes the trailing # tag comment in the same PR. Dependabot bumps github-actions pins on the schedule you set.
Either way you get bashunit updates as routine pull requests — no manual re-pinning or curl | bash bumps to remember. Review the PR, let CI run, merge.
TIP
See the action's own end-to-end test for a real example, covering version, directory, add-to-path and args: https://github.com/TypedDevs/bashunit/blob/main/.github/workflows/test-action.yml
bashunit's tests.yml runs the in-repo entrypoint instead, so it is not an install example.
Updating
For the install.sh, GitHub Action and bashdep routes, upgrade the binary in place:
./lib/bashunit upgradeIt downloads the newest release over the same file, and prints > You are already on latest version when there is nothing to do.
Package-manager installs update through their own manager instead: brew upgrade bashunit, sudo port upgrade bashunit, npm install --save-dev bashunit@latest.
Shell completion
bashunit ships tab-completion scripts for bash and zsh under completions/ — subcommands, all test flags (with value hints like --jobs auto and --output tap), and the assertion names after bashunit assert.
# With bash-completion installed (path may vary by OS):
cp completions/bashunit.bash /usr/local/etc/bash_completion.d/bashunit
# Or source it directly from your ~/.bashrc:
source /path/to/bashunit/completions/bashunit.bash# Copy into any directory in your $fpath, e.g.:
cp completions/_bashunit /usr/local/share/zsh/site-functions/_bashunit
# then restart zsh (or reinitialize completions):
autoload -Uz compinit && compinitThe scripts are kept honest by an anti-drift test in CI: adding a flag to bashunit without updating the completions fails the build.
Related
- Quickstart - write and run your first test
- Command line - CLI flags and options
- Configuration - env vars and config files
- Project overview - repo layout and contributor workflow