Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and versions are tracked in the repo-root `VERSION` file.

## [Unreleased]

### Removed

- Removed early compatibility aliases `run`, `std_run_with_timeout`, and
`str_in_array`; use `std_run`, `std_run --timeout`, and `list_contains`
instead.

## [1.1.0] - 2026-07-03

### Added
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Requires Bash 4.2+. On macOS, use Homebrew Bash instead of the system `/bin/bash
inspection, update, and script freshness checks.
- [`lib/bash/str/lib_str.sh`](lib/bash/str/README.md)
String helpers built on the stdlib for case conversion, trimming,
predicates, splitting, joining, and array membership checks.
predicates, splitting, and joining.
- [`lib/bash/arg/lib_arg.sh`](lib/bash/arg/README.md)
Argument parsing helpers built on the stdlib for exact flag and value
options without hidden parser globals.
Expand Down
8 changes: 7 additions & 1 deletion lib/bash/list/tests/lib_list.bats
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,16 @@ create_script() {
[ "${values[3]}" = "" ]
}

@test "list_remove deletes matching values and preserves order" {
@test "list_remove deletes all matching values and preserves order" {
local -a values=("alpha" "beta" "alpha" "" "gamma")

list_remove values "alpha"

[ "${#values[@]}" -eq 3 ]
[ "${values[0]}" = "beta" ]
[ "${values[1]}" = "" ]
[ "${values[2]}" = "gamma" ]

list_remove values ""

[ "${#values[@]}" -eq 2 ]
Expand Down
10 changes: 2 additions & 8 deletions lib/bash/std/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,14 +273,8 @@ first:
std_run -- --command-name arg
```

`run` remains available as a compatibility wrapper for existing callers, but new
code should use `std_run` to avoid collisions with test frameworks and other
Bash libraries that define their own `run` helper.

`std_run_with_timeout` remains available as a compatibility wrapper, but new
code should use `std_run --timeout N ...`. Timeout execution prefers `timeout`
or `gtimeout` when available and otherwise uses a Bash fallback so scripts work
on macOS and Linux.
Timeout execution prefers `timeout` or `gtimeout` when available and otherwise
uses a Bash fallback so scripts work on macOS and Linux.

## Importing Other Bash Libraries

Expand Down
55 changes: 0 additions & 55 deletions lib/bash/std/lib_std.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@
# Core helpers:
# std_run [opts] cmd ...
# # Safe command runner with dry-run, timeout, retry & failure handling.
# std_run_with_timeout [opts] seconds cmd ...
# # Compatibility wrapper for std_run --timeout.
# exit_if_error rc msg... # Log + exit when rc != 0 (preserves original status).
# fatal_error msg... # Convenience wrapper: exit with last status or 1.
# std_register_cleanup_hook fn # Run a cleanup function from the shared EXIT trap.
Expand Down Expand Up @@ -1019,10 +1017,6 @@ std_run() {
__std_run_impl__ std_run "$@"
}

run() {
__std_run_impl__ run "$@"
}

__std_sleep_interval__() {
if [[ -x /bin/sleep ]]; then
/bin/sleep "$1"
Expand Down Expand Up @@ -1067,55 +1061,6 @@ __std_run_with_timeout_fallback__() {
return "$command_status"
}

#
# std_run_with_timeout - Safely executes a command with a timeout.
#
# This helper mirrors `std_run` option handling while bounding the command
# runtime. It prefers `timeout` or `gtimeout` when available and otherwise uses
# a Bash fallback so callers have portable behavior on macOS and Linux.
#
# Usage:
# std_run_with_timeout [--no-exit] [--quiet] <seconds> command [arg1] ...
#
std_run_with_timeout() {
local timeout_seconds
local run_options=()

while (($#)); do
case "${1-}" in
--no-exit)
run_options+=("--no-exit")
shift
;;
--quiet)
run_options+=("--quiet")
shift
;;
--)
shift
break
;;
*)
break
;;
esac
done

if (($# < 2)); then
log_error "std_run_with_timeout: usage: std_run_with_timeout [--no-exit] [--quiet] <seconds> command [arg1] ..."
return 1
fi

timeout_seconds="$1"
shift
if ! __std_is_positive_integer__ "$timeout_seconds"; then
log_error "std_run_with_timeout: timeout seconds must be a positive integer."
return 1
fi

__std_run_impl__ std_run_with_timeout "${run_options[@]}" --timeout "$timeout_seconds" -- "$@"
}

############################################## FILE AND DIRECTORY HANDLING ############################################

#
Expand Down
120 changes: 18 additions & 102 deletions lib/bash/std/tests/lib_std.bats
Original file line number Diff line number Diff line change
Expand Up @@ -829,9 +829,24 @@ EOF
[[ "$output" == *"fatal boom"* ]]
}

@test "std_run is the preferred command runner and run remains compatible" {
[ "$(type -t std_run)" = "function" ]
[ "$(type -t run)" = "function" ]
@test "stdlib exposes std_run without compatibility aliases" {
local script="$TEST_TMPDIR/std-run-api.sh"

create_script "$script" <<EOF
#!/usr/bin/env bash
source "$STDLIB_PATH"
printf 'std_run=%s\n' "\$(type -t std_run || true)"
printf 'run=%s\n' "\$(type -t run || true)"
printf 'std_run_with_timeout=%s\n' "\$(type -t std_run_with_timeout || true)"
EOF

bats_run bash "$script"

[ "$status" -eq 0 ]
[[ "$output" == *"std_run=function"* ]]
[[ "$output" == *$'run=\n'* ]]
[[ "$output" == *"std_run_with_timeout="* ]]
[[ "$output" != *"std_run_with_timeout=function"* ]]
}

@test "std_run returns an error when no command is provided" {
Expand Down Expand Up @@ -1117,105 +1132,6 @@ EOF
[[ "$(cat "$stderr_file")" == *"2s retry delay"* ]]
}

@test "std_run_with_timeout runs commands and preserves arguments" {
local output_file="$TEST_TMPDIR/timeout-output.txt"

std_run_with_timeout 5 bash -c 'printf "%s\n" "$1" > "$2"' _ "hello world" "$output_file"

[ "$(cat "$output_file")" = "hello world" ]
}

@test "std_run_with_timeout --no-exit returns 124 when the command times out" {
local stderr_file="$TEST_TMPDIR/timeout.err"
local rc

if std_run_with_timeout --no-exit --quiet 1 sleep 2 2>"$stderr_file"; then
rc=0
else
rc=$?
fi

[ "$rc" -eq 124 ]
[ ! -s "$stderr_file" ]
}

@test "std_run_with_timeout exits on command failure by default" {
local script="$TEST_TMPDIR/timeout-fail.sh"

create_script "$script" <<EOF
#!/usr/bin/env bash
source "$STDLIB_PATH"
std_run_with_timeout 5 bash -c 'exit 6'
echo "after"
EOF

bats_run bash "$script"

[ "$status" -eq 6 ]
[[ "$output" == *"Command failed (exit 6)"* ]]
[[ "$output" != *"after"* ]]
}

@test "std_run_with_timeout honors dry-run mode without executing the command" {
local target="$TEST_TMPDIR/timeout-dry-run.txt"
DRY_RUN=true

std_run_with_timeout 1 touch "$target"

[ "$?" -eq 0 ]
[ ! -e "$target" ]
}

@test "std_run_with_timeout falls back when timeout binaries are absent" {
local fake_bin="$TEST_TMPDIR/no-timeout-bin"
local output_file="$TEST_TMPDIR/timeout-fallback-output.txt"
local script="$TEST_TMPDIR/timeout-fallback.sh"

mkdir -p "$fake_bin"
ln -s "$(command -v mktemp)" "$fake_bin/mktemp"
ln -s "$(command -v sleep)" "$fake_bin/sleep"

create_script "$script" <<EOF
#!/usr/bin/env bash
source "$STDLIB_PATH"
PATH="$fake_bin"
std_run_with_timeout --no-exit 5 /bin/echo fallback > "$output_file"
EOF

bats_run bash "$script"

[ "$status" -eq 0 ]
[ "$(cat "$output_file")" = "fallback" ]
}

@test "std_run_with_timeout rejects invalid timeouts" {
local stderr_file="$TEST_TMPDIR/timeout-invalid.err"
local rc

if std_run_with_timeout --no-exit nope bash -c 'exit 0' 2>"$stderr_file"; then
rc=0
else
rc=$?
fi

[ "$rc" -eq 1 ]
[[ "$(cat "$stderr_file")" == *"std_run_with_timeout: timeout seconds must be a positive integer."* ]]
}

@test "run compatibility wrapper delegates to std_run behavior" {
local stderr_file="$TEST_TMPDIR/run-compat.err"
local rc

if run --no-exit --quiet bash -c 'exit 7' 2>"$stderr_file"; then
rc=0
else
rc=$?
fi

[ "$rc" -eq 7 ]
[ ! -s "$stderr_file" ]
}

@test "safe_mkdir creates directories and tolerates existing paths with -p" {
local first="$TEST_TMPDIR/a"
local second="$TEST_TMPDIR/b/c"
Expand Down
4 changes: 2 additions & 2 deletions lib/bash/str/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ helpers are available.
Split a string by a delimiter into a caller-provided array variable.
- `str_join`
Join a caller-provided array variable into a caller-provided result variable.
- `str_in_array`
Return success when a named array contains an exact string value.

## Usage

Expand Down Expand Up @@ -63,6 +61,8 @@ str_join joined "|" parts
not print output.
- `str_split` preserves empty fields between repeated delimiters.
- `str_join` preserves empty array elements, including trailing empty elements.
- Use `list_contains` from `lib/bash/list/lib_list.sh` for indexed-array
membership checks.
- Named string, result, and array arguments must be valid Bash variable names.
- Array arguments and array result variables must already be declared as indexed
arrays, for example with `declare -a parts=()`.
Expand Down
17 changes: 0 additions & 17 deletions lib/bash/str/lib_str.sh
Original file line number Diff line number Diff line change
Expand Up @@ -119,20 +119,3 @@ str_join() {

printf -v "$result_name" '%s' "$__str_join_joined"
}

str_in_array() {
local needle="${1-}" array_name="${2-}" item

assert_arg_count "$#" 2
assert_variable_name "$array_name"
assert_indexed_array "$array_name"

local -a __str_in_array_values=()
eval "__str_in_array_values=(\"\${${array_name}[@]}\")"

for item in "${__str_in_array_values[@]}"; do
[[ "$item" == "$needle" ]] && return 0
done

return 1
}
42 changes: 2 additions & 40 deletions lib/bash/str/tests/lib_str.bats
Original file line number Diff line number Diff line change
Expand Up @@ -209,34 +209,8 @@ EOF
[[ "$output" != *"not-valid"* ]]
}

@test "str_in_array checks membership in a named array" {
local -a values=("alpha" "beta gamma" "")

str_in_array "alpha" values
str_in_array "beta gamma" values
str_in_array "" values

if str_in_array "delta" values; then
return 1
fi
}

@test "str_in_array rejects invalid array variable names" {
local script="$TEST_TMPDIR/str-in-array-invalid.sh"

create_script "$script" <<EOF
#!/usr/bin/env bash
source "$BASE_BASH_DIR/std/lib_std.sh"
source "$BASE_BASH_DIR/str/lib_str.sh"
secret="not-valid"
str_in_array "alpha" "\$secret"
EOF

bats_run bash "$script"

[ "$status" -eq 1 ]
[[ "$output" == *"assert_variable_name expects valid Bash variable names"* ]]
[[ "$output" != *"not-valid"* ]]
@test "lib_str does not define list membership aliases" {
[ "$(type -t str_in_array || true)" = "" ]
}

@test "string array helpers reject non-indexed arrays" {
Expand Down Expand Up @@ -269,16 +243,4 @@ EOF
[ "$status" -eq 1 ]
[[ "$output" == *"must be an indexed array declared by the caller"* ]]

create_script "$script" <<EOF
#!/usr/bin/env bash
source "$BASE_BASH_DIR/std/lib_std.sh"
source "$BASE_BASH_DIR/str/lib_str.sh"
values="alpha"
str_in_array "alpha" values
EOF

bats_run bash "$script"

[ "$status" -eq 1 ]
[[ "$output" == *"must be an indexed array declared by the caller"* ]]
}
Loading