From 2cfcb308d8317a2724d21fbebea5494bf1118d5a Mon Sep 17 00:00:00 2001 From: bfs2021 <101915083+bfs2021@users.noreply.github.com> Date: Thu, 29 Oct 2026 05:05:12 +0800 Subject: [PATCH 1/2] fix(cli): quote the env lines init writes, and point the next run at a custom tests dir .env is sourced as shell, so an unquoted BASHUNIT_BOOTSTRAP turned a directory with a space into a command and broke every next run with an error naming a path the user never typed. The value is written double-quoted with the special characters escaped. A custom directory also needs BASHUNIT_DEFAULT_PATH, or the next bare bashunit finds no tests; the shared idempotent write moves into a helper so both lines get the same re-init treatment. --- CHANGELOG.md | 11 ++--- src/cli/init.sh | 64 +++++++++++++++++--------- tests/acceptance/bashunit_init_test.sh | 18 +++++++- 3 files changed, 61 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5bcfd50..f080bc4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,18 +2,13 @@ ## Unreleased -### Added - -- Blog posts for releases 0.43.0 through 0.52.0, including the 0.50.1 cleanup patch - ### Fixed -- Parallel runs count tests under relative hidden paths and keep their results separate from matching visible paths (#1393) +- `init` writes the bootstrap path and a custom tests directory to `.env` quoted, so a directory with a space runs afterwards instead of breaking every next run (#1411) -### Changed +### Added -- Documentation uses shorter, clearer wording across guides, reference pages and release posts -- Large provider and annotation maps use an index instead of scanning earlier entries, speeding up test files on Bash 3.0 and newer (#1392) +- Blog posts for releases 0.43.0 through 0.52.0, including the 0.50.1 cleanup patch ## [0.52.0](https://github.com/TypedDevs/bashunit/compare/0.51.0...0.52.0) - 2026-10-11 diff --git a/src/cli/init.sh b/src/cli/init.sh index 858ade35..ba108083 100644 --- a/src/cli/init.sh +++ b/src/cli/init.sh @@ -1,5 +1,33 @@ #!/usr/bin/env bash +# Appends `line` to .env, leaves the file alone when it already holds it, and +# comments an older, different `KEY=` out first. Commenting the line out and +# appending an identical one made every re-init add a dead line, so a third +# run left three (#1175); -Fx so a dot in the path is compared literally. +function bashunit::init::write_env_line() { + local env_file=".env" + local line=$1 + local key=${line%%=*} + if [ ! -f "$env_file" ]; then + printf '%s\n' "$line" >"$env_file" + echo "> Created $env_file" + return + fi + if grep -Fxq "$line" "$env_file"; then + echo "> $env_file already sets $key" + return + fi + if grep -q "^$key=" "$env_file"; then + if bashunit::check_os::is_macos; then + sed -i '' -e "s/^$key=/#&/" "$env_file" + else + sed -i -e "s/^$key=/#&/" "$env_file" + fi + fi + printf '%s\n' "$line" >>"$env_file" + echo "> Updated $env_file" +} + function bashunit::init::project() { local tests_dir="${1:-$BASHUNIT_DEFAULT_PATH}" mkdir -p "$tests_dir" @@ -47,29 +75,19 @@ SH echo "> Created $workflow_file" fi - local env_file=".env" - local env_line="BASHUNIT_BOOTSTRAP=$bootstrap_file" - if [ -f "$env_file" ]; then - # Already pointing where this run would point it: leave the file alone. - # Commenting the line out and appending an identical one made every re-init - # add a dead line, so a third run left three (#1175). -Fx so a dot in the - # path is compared literally. - if grep -Fxq "$env_line" "$env_file"; then - echo "> $env_file already sets BASHUNIT_BOOTSTRAP" - else - if grep -q "^BASHUNIT_BOOTSTRAP=" "$env_file"; then - if bashunit::check_os::is_macos; then - sed -i '' -e "s/^BASHUNIT_BOOTSTRAP=/#&/" "$env_file" - else - sed -i -e "s/^BASHUNIT_BOOTSTRAP=/#&/" "$env_file" - fi - fi - echo "$env_line" >>"$env_file" - echo "> Updated $env_file" - fi - else - echo "$env_line" >"$env_file" - echo "> Created $env_file" + # .env is sourced as shell, so values are written double-quoted and the + # characters that are special inside double quotes are escaped: a directory + # with a space stays one assignment instead of turning into a command. + local quoted="$tests_dir" + quoted=${quoted//\\/\\\\} + quoted=${quoted//\"/\\\"} + quoted=${quoted//\$/\\\$} + quoted=${quoted//\`/\\\`} + bashunit::init::write_env_line "BASHUNIT_BOOTSTRAP=\"$quoted/bootstrap.sh\"" + # A custom directory is otherwise invisible to the next bare `bashunit`, + # which searches BASHUNIT_DEFAULT_PATH ("tests"). + if [ "$tests_dir" != "$BASHUNIT_DEFAULT_PATH" ]; then + bashunit::init::write_env_line "BASHUNIT_DEFAULT_PATH=\"$quoted\"" fi echo "> bashunit initialized in $tests_dir" diff --git a/tests/acceptance/bashunit_init_test.sh b/tests/acceptance/bashunit_init_test.sh index fcea215e..6a776e87 100644 --- a/tests/acceptance/bashunit_init_test.sh +++ b/tests/acceptance/bashunit_init_test.sh @@ -64,10 +64,26 @@ function test_bashunit_init_updates_env() { assert_file_exists "custom/example_test.sh" assert_file_exists "custom/bootstrap.sh" assert_file_contains .env "#BASHUNIT_BOOTSTRAP=old/bootstrap.sh" - assert_file_contains .env "BASHUNIT_BOOTSTRAP=custom/bootstrap.sh" + assert_file_contains .env 'BASHUNIT_BOOTSTRAP="custom/bootstrap.sh"' popd >/dev/null } +# A directory with a space is a valid project directory. `.env` is sourced as +# shell, so an unquoted BASHUNIT_BOOTSTRAP turns the second word into a command +# and the next run dies with "No such file or directory" naming a path the user +# never typed. +function test_bashunit_init_with_a_space_in_the_directory_runs_afterwards() { + pushd "$TMP_DIR" >/dev/null + "$BASHUNIT_PATH" init "my tests" >/dev/null + + local output + output="$($BASHUNIT_PATH --no-color 2>&1)" + popd >/dev/null + + assert_not_contains "No such file or directory" "$output" + assert_contains "1 passed" "$output" +} + # `bashunit init` is run more than once in practice -- re-scaffolding, or a # setup script that is not guarded. The files it writes are idempotent; .env # was not: every run commented out the previous BASHUNIT_BOOTSTRAP and appended From 76c675d4f3cfa4a37a08e216f875470bad0813d1 Mon Sep 17 00:00:00 2001 From: Chemaclass Date: Wed, 28 Oct 2026 22:21:03 +0100 Subject: [PATCH 2/2] docs(cli): restore the changelog and describe the env lines init writes The merge with main dropped the Unreleased Changed entries and reordered the sections. The init reference now shows the quoted BASHUNIT_BOOTSTRAP line and the BASHUNIT_DEFAULT_PATH line written for a custom directory. --- CHANGELOG.md | 9 +++++++-- docs/command-line.md | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 06135b6e..ac02f8fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,15 +2,20 @@ ## Unreleased +### Added + +- Blog posts for releases 0.43.0 through 0.52.0, including the 0.50.1 cleanup patch + ### Fixed - `init` writes the bootstrap path and a custom tests directory to `.env` quoted, so a directory with a space runs afterwards instead of breaking every next run (#1411) - Failure source context stays inside a one-line test instead of listing the next function's assertions (#1420) - Parallel runs count tests under relative hidden paths and keep their results separate from matching visible paths (#1393) -### Added +### Changed -- Blog posts for releases 0.43.0 through 0.52.0, including the 0.50.1 cleanup patch +- Documentation uses shorter, clearer wording across guides, reference pages and release posts +- Large provider and annotation maps use an index instead of scanning earlier entries, speeding up test files on Bash 3.0 and newer (#1392) ## [0.52.0](https://github.com/TypedDevs/bashunit/compare/0.51.0...0.52.0) - 2026-10-11 diff --git a/docs/command-line.md b/docs/command-line.md index b5684134..bd116df3 100644 --- a/docs/command-line.md +++ b/docs/command-line.md @@ -1603,9 +1603,9 @@ Creates: - `tests/bootstrap.sh` - Setup file for test configuration - `tests/example_test.sh` - Sample test file to get started - `.github/workflows/tests.yml` - CI workflow using the official action -- `.env` with `BASHUNIT_BOOTSTRAP=tests/bootstrap.sh` +- `.env` with `BASHUNIT_BOOTSTRAP="tests/bootstrap.sh"`, plus `BASHUNIT_DEFAULT_PATH=""` for a custom directory so a bare `bashunit` runs it -An existing `BASHUNIT_BOOTSTRAP=` line in `.env` is commented out first, so the new value +An existing `BASHUNIT_BOOTSTRAP=` or `BASHUNIT_DEFAULT_PATH=` line in `.env` is commented out first, so the new value takes effect. ## upgrade