Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
e374f8c
feat: lower minimum bash version requirement from 3.2 to 3.0
Chemaclass Feb 3, 2026
5100702
fix: complete Bash 3.0 compatibility fixes
Chemaclass Feb 4, 2026
3a10363
fix: Bash 3.0 compatibility for assert_contains_ignore_case
Chemaclass Feb 4, 2026
c2fbe67
ci: run Bash 3.0 tests in parallel jobs
Chemaclass Feb 4, 2026
01dba04
fix: resolve shellcheck and lint errors
Chemaclass Feb 4, 2026
05ffbe1
ci: remove read-only flag from Docker volume mount
Chemaclass Feb 4, 2026
032107b
ci: add procps package for ps command in Docker
Chemaclass Feb 4, 2026
bfd3d97
ci: trigger fresh build
Chemaclass Feb 4, 2026
46f94b6
test: skip mock/spy external script tests on Bash 3.0
Chemaclass Feb 4, 2026
9235356
fix: use local -a for safe Bash 3.0 array init, fix variable scoping
Chemaclass Feb 4, 2026
af54c95
Merge branch 'main' into feat/support-bash-3.0
Chemaclass Feb 9, 2026
e274b8a
Merge branch 'main' into feat/support-bash-3.0
Chemaclass Feb 10, 2026
313c540
fix: improve Bash 3.0 regex compatibility and assignment quoting
Chemaclass Feb 10, 2026
54a1ff1
fix: prevent variable leakage in loops across all source files
Chemaclass Feb 10, 2026
68941fc
fix: add local declarations for all remaining loop variables
Chemaclass Feb 10, 2026
d7cd950
fix: initialize all loop variables to prevent unbound variable errors…
Chemaclass Feb 10, 2026
d68762c
fix: make array iteration safe for strict mode (set -u)
Chemaclass Feb 10, 2026
de43885
fix: remove redundant array size check in benchmark print function
Chemaclass Feb 10, 2026
a0e86ea
fix(runner): expand test_file at trap definition time to prevent unbo…
Chemaclass Feb 10, 2026
9c8f621
fix(compat): harden regex matching, array expansion, and parameter subs
Chemaclass Feb 10, 2026
ec935c2
revert: remove unnecessary loop variable initializations from d7cd950
Chemaclass Feb 10, 2026
421fbee
fix(coverage): initialize lineno counter to prevent strict mode error
Chemaclass Feb 10, 2026
e9be71b
fix(compat): address PR feedback for Bash 3.0 compatibility
Chemaclass Feb 11, 2026
834bd32
fix(compat): convert all literal regex patterns to variables
Chemaclass Feb 11, 2026
ea2970a
fix(compat): move regex pattern outside loop in doc.sh
Chemaclass Feb 11, 2026
bc993e1
fix(compat): use glob pattern instead of regex for code fence
Chemaclass Feb 11, 2026
81d47c4
Update src/reports.sh
Chemaclass Feb 11, 2026
2915045
Update src/runner.sh
Chemaclass Feb 11, 2026
a8be738
Update src/reports.sh
Chemaclass Feb 11, 2026
4cc506e
Update src/helpers.sh
Chemaclass Feb 11, 2026
8758009
Update src/main.sh
Chemaclass Feb 11, 2026
ec1f282
Update src/helpers.sh
Chemaclass Feb 11, 2026
47ced2c
Update src/learn.sh
Chemaclass Feb 11, 2026
90ba80a
Update src/main.sh
Chemaclass Feb 11, 2026
1280a79
fix(compat): store literal regex patterns in variables for Bash 3.0
Chemaclass Feb 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Contributions are licensed under the [MIT License](https://github.com/TypedDevs/

### Prerequisites

- Bash 3.2+
- Bash 3.0+
- Git
- Make
- [ShellCheck](https://github.com/koalaman/shellcheck#installing)
Expand Down
101 changes: 101 additions & 0 deletions .github/workflows/tests-bash-3.0.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
name: Bash 3.0 Compatibility

on:
pull_request:
push:
branches:
- main

jobs:
build-image:
name: "Build Bash 3.0 Image"
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 1

- name: Build Bash 3.0 Docker image
run: |
docker build -t bashunit-bash3 -f - . <<'EOF'
FROM debian:bullseye-slim

RUN apt-get update && apt-get install -y \
build-essential \
curl \
ca-certificates \
bison \
git \
procps \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /tmp
RUN curl -LO https://ftp.gnu.org/gnu/bash/bash-3.0.tar.gz \
&& tar xzf bash-3.0.tar.gz \
&& cd bash-3.0 \
&& curl -fsSL -o support/config.guess 'https://raw.githubusercontent.com/gcc-mirror/gcc/master/config.guess' \
&& curl -fsSL -o support/config.sub 'https://raw.githubusercontent.com/gcc-mirror/gcc/master/config.sub' \
&& chmod +x support/config.guess support/config.sub \
&& ./configure --prefix=/opt/bash-3.0 \
&& make \
&& make install \
&& rm -rf /tmp/bash-3.0*

WORKDIR /bashunit
CMD ["/opt/bash-3.0/bin/bash", "--version"]
EOF

- name: Save Docker image
run: docker save bashunit-bash3 -o /tmp/bashunit-bash3.tar

- name: Upload Docker image artifact
uses: actions/upload-artifact@v4
with:
name: bashunit-bash3-image
path: /tmp/bashunit-bash3.tar
retention-days: 1

test:
name: "Bash 3.0 - ${{ matrix.name }}"
runs-on: ubuntu-latest
needs: build-image
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
include:
- name: "Sequential"
flags: ""
- name: "Parallel"
flags: "--parallel"
- name: "Simple"
flags: "--simple"
- name: "Simple Parallel"
flags: "--simple --parallel"
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 1

- name: Download Docker image artifact
uses: actions/download-artifact@v4
with:
name: bashunit-bash3-image
path: /tmp

- name: Load Docker image
run: docker load --input /tmp/bashunit-bash3.tar

- name: Verify Bash 3.0 version
run: docker run --rm bashunit-bash3 /opt/bash-3.0/bin/bash --version

- name: Run tests with Bash 3.0 (${{ matrix.name }})
run: |
docker run --rm \
-v "$(pwd)":/bashunit \
-w /bashunit \
bashunit-bash3 \
/opt/bash-3.0/bin/bash ./bashunit ${{ matrix.flags }} tests/
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

### Changed
- Lower minimum Bash version requirement from 3.2 to 3.0

### Added
- Add Claude Code configuration with custom skills, agents, and rules
- Custom skills for TDD workflow, test fixes, assertions, coverage, and releases
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ You can find the complete documentation for **bashunit** online, including insta

## Requirements

bashunit requires **Bash 3.2** or newer.
bashunit requires **Bash 3.0** or newer.

## Contribute

Expand Down
90 changes: 45 additions & 45 deletions bashunit
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail

declare -r BASHUNIT_MIN_BASH_VERSION="3.2"
declare -r BASHUNIT_MIN_BASH_VERSION="3.0"

function _check_bash_version() {
local current_version
Expand All @@ -16,10 +16,10 @@ function _check_bash_version() {
current_version="$(bash --version | head -n1 | cut -d' ' -f4 | cut -d. -f1,2)"
fi

local major minor
IFS=. read -r major minor _ <<< "$current_version"
local major
IFS=. read -r major _ <<<"$current_version"

if (( major < 3 )) || { (( major == 3 )) && (( minor < 2 )); }; then
if ((major < 3)); then
printf 'Bashunit requires Bash >= %s. Current version: %s\n' "$BASHUNIT_MIN_BASH_VERSION" "$current_version" >&2
exit 1
fi
Expand All @@ -41,16 +41,16 @@ export BASHUNIT_WORKING_DIR
# Early scan for flags that must be set before loading env.sh
for arg in "$@"; do
case "$arg" in
--skip-env-file)
export BASHUNIT_SKIP_ENV_FILE=true
;;
-l|--login)
export BASHUNIT_LOGIN_SHELL=true
;;
--no-color)
# shellcheck disable=SC2034
BASHUNIT_NO_COLOR=true
;;
--skip-env-file)
export BASHUNIT_SKIP_ENV_FILE=true
;;
-l | --login)
export BASHUNIT_LOGIN_SHELL=true
;;
--no-color)
# shellcheck disable=SC2034
BASHUNIT_NO_COLOR=true
;;
esac
done

Expand Down Expand Up @@ -88,39 +88,39 @@ bashunit::clock::init
_SUBCOMMAND=""

case "${1:-}" in
test|bench|doc|init|learn|upgrade|assert)
_SUBCOMMAND="$1"
shift
;;
-v|--version)
bashunit::console_header::print_version
exit 0
;;
-h|--help)
bashunit::console_header::print_help
exit 0
;;
-*)
# Flag without subcommand → assume "test"
_SUBCOMMAND="test"
;;
"")
# No arguments → assume "test" (uses BASHUNIT_DEFAULT_PATH)
_SUBCOMMAND="test"
;;
*)
# Path argument → assume "test"
_SUBCOMMAND="test"
;;
test | bench | doc | init | learn | upgrade | assert)
_SUBCOMMAND="$1"
shift
;;
-v | --version)
bashunit::console_header::print_version
exit 0
;;
-h | --help)
bashunit::console_header::print_help
exit 0
;;
-*)
# Flag without subcommand → assume "test"
_SUBCOMMAND="test"
;;
"")
# No arguments → assume "test" (uses BASHUNIT_DEFAULT_PATH)
_SUBCOMMAND="test"
;;
*)
# Path argument → assume "test"
_SUBCOMMAND="test"
;;
esac

# Route to subcommand handler
case "$_SUBCOMMAND" in
test) bashunit::main::cmd_test "$@" ;;
bench) bashunit::main::cmd_bench "$@" ;;
doc) bashunit::main::cmd_doc "$@" ;;
init) bashunit::main::cmd_init "$@" ;;
learn) bashunit::main::cmd_learn "$@" ;;
upgrade) bashunit::main::cmd_upgrade "$@" ;;
assert) bashunit::main::cmd_assert "$@" ;;
test) bashunit::main::cmd_test "$@" ;;
bench) bashunit::main::cmd_bench "$@" ;;
doc) bashunit::main::cmd_doc "$@" ;;
init) bashunit::main::cmd_init "$@" ;;
learn) bashunit::main::cmd_learn "$@" ;;
upgrade) bashunit::main::cmd_upgrade "$@" ;;
assert) bashunit::main::cmd_assert "$@" ;;
esac
Loading
Loading