From f5fa6be785273b580b5fc5113b3f1da84db66a7e Mon Sep 17 00:00:00 2001 From: Chemaclass Date: Fri, 26 Jun 2026 12:30:10 +0200 Subject: [PATCH 1/2] fix(runner): bundle src/watch.sh into build so watch subcommand works The distributable single-file binary called bashunit::watch::run but never defined it: build::dependencies omitted src/watch.sh, and the build strips all source lines. Add watch.sh to the dependency list and guard the whole class of regression with a build test asserting every src file sourced by the dev entrypoint is bundled, plus a smoke test on the built binary. Closes #735 --- CHANGELOG.md | 3 +++ build.sh | 1 + tests/unit/build_test.sh | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 tests/unit/build_test.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 23bb8f7b..7f62c1e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +### Fixed +- `watch` subcommand failed with `bashunit::watch::run: command not found` in the released binary because `src/watch.sh` was missing from the build; it is now bundled (#735) + ### Changed - Faster test execution by removing subprocess forks from hot paths (no behaviour change) diff --git a/build.sh b/build.sh index 6963c94a..b622f331 100755 --- a/build.sh +++ b/build.sh @@ -107,6 +107,7 @@ function build::dependencies() { "src/helpers.sh" "src/test_title.sh" "src/upgrade.sh" + "src/watch.sh" "src/assertions.sh" "src/reports.sh" "src/runner.sh" diff --git a/tests/unit/build_test.sh b/tests/unit/build_test.sh new file mode 100644 index 00000000..e01c61fe --- /dev/null +++ b/tests/unit/build_test.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash + +ROOT_DIR="" + +function set_up_before_script() { + ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +} + +# Every src file the dev entrypoint sources (except dev-only helpers) must also be +# listed in build::dependencies, otherwise its functions are missing from the +# distributable single-file binary (regressions: bench #0.31.0, watch #735). +function test_build_dependencies_cover_every_sourced_src_file() { + local entry_sources build_deps missing="" + entry_sources=$(grep -oE 'src/[a-zA-Z0-9_/]+\.sh' "$ROOT_DIR/bashunit" | grep -v '^src/dev/') + build_deps=$(grep -oE 'src/[a-zA-Z0-9_/]+\.sh' "$ROOT_DIR/build.sh") + + local file + for file in $entry_sources; do + if ! echo "$build_deps" | grep -qx "$file"; then + missing="$missing $file" + fi + done + + assert_empty "$missing" +} + +function test_built_binary_defines_watch_run() { + local out_dir + out_dir="$(mktemp -d)" + + bash "$ROOT_DIR/build.sh" "$out_dir" >/dev/null 2>&1 + + assert_file_exists "$out_dir/bashunit" + assert_equals "1" "$(grep -c 'function bashunit::watch::run()' "$out_dir/bashunit")" + + rm -rf "$out_dir" +} From 4de0bb6ffd0e0e0c7da2036daaef074adf1cda89 Mon Sep 17 00:00:00 2001 From: Chemaclass Date: Fri, 26 Jun 2026 12:44:29 +0200 Subject: [PATCH 2/2] test(runner): simplify build coverage guard with comm and auto-cleaned temp dir --- tests/unit/build_test.sh | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/tests/unit/build_test.sh b/tests/unit/build_test.sh index e01c61fe..8c52416e 100644 --- a/tests/unit/build_test.sh +++ b/tests/unit/build_test.sh @@ -6,32 +6,31 @@ function set_up_before_script() { ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" } +function src_files_sourced_by_entrypoint() { + # Dev-only helpers under src/dev/ are intentionally excluded from the build. + grep -oE 'src/[a-zA-Z0-9_/]+\.sh' "$ROOT_DIR/bashunit" | grep -v '^src/dev/' | sort -u +} + +function src_files_in_build() { + grep -oE 'src/[a-zA-Z0-9_/]+\.sh' "$ROOT_DIR/build.sh" | sort -u +} + # Every src file the dev entrypoint sources (except dev-only helpers) must also be -# listed in build::dependencies, otherwise its functions are missing from the -# distributable single-file binary (regressions: bench #0.31.0, watch #735). -function test_build_dependencies_cover_every_sourced_src_file() { - local entry_sources build_deps missing="" - entry_sources=$(grep -oE 'src/[a-zA-Z0-9_/]+\.sh' "$ROOT_DIR/bashunit" | grep -v '^src/dev/') - build_deps=$(grep -oE 'src/[a-zA-Z0-9_/]+\.sh' "$ROOT_DIR/build.sh") - - local file - for file in $entry_sources; do - if ! echo "$build_deps" | grep -qx "$file"; then - missing="$missing $file" - fi - done +# bundled by build.sh, otherwise its functions are missing from the distributable +# single-file binary (regressions: bench #0.31.0, watch #735). +function test_build_bundles_every_src_file_sourced_by_entrypoint() { + local missing + missing=$(comm -23 <(src_files_sourced_by_entrypoint) <(src_files_in_build)) assert_empty "$missing" } function test_built_binary_defines_watch_run() { - local out_dir - out_dir="$(mktemp -d)" - - bash "$ROOT_DIR/build.sh" "$out_dir" >/dev/null 2>&1 + local build_dir + build_dir=$(bashunit::temp_dir) - assert_file_exists "$out_dir/bashunit" - assert_equals "1" "$(grep -c 'function bashunit::watch::run()' "$out_dir/bashunit")" + (cd "$ROOT_DIR" && bash build.sh "$build_dir") >/dev/null 2>&1 - rm -rf "$out_dir" + assert_file_exists "$build_dir/bashunit" + assert_equals "1" "$(grep -c 'function bashunit::watch::run()' "$build_dir/bashunit")" }