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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- `--profile` reports the slowest tests after a run (count via `BASHUNIT_PROFILE_COUNT`, default 10); works in sequential and parallel mode (#678)
- Snapshot mismatches show a readable line diff even when `git` is unavailable (expected lines prefixed `-`, actual `+`) (#679)
- Failure output now includes the originating test `file:line` (`at <file>:<line>`) (#680)
- Project config file `.bashunitrc` (`KEY=value` lines); precedence is CLI flag > env var / `.env` > `.bashunitrc` > default; honors `--skip-env-file` (#681)

### Fixed
- `bashunit learn` and coverage now create temp directories via `mktemp -d` (no predictable PID-based paths under `/tmp`)
Expand Down
24 changes: 24 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,30 @@ You need to create a `.env` file in the root directory,
but you can give it another name if you pass it as an argument to the command with
`--env` [option](/command-line#environment).

## Config file (.bashunitrc)

As an alternative to a `.env` file, you can place a `.bashunitrc` file in the
project root with `KEY=value` lines (blank lines and `#` comments are ignored):

```bash
# .bashunitrc
BASHUNIT_SHOW_HEADER=false
BASHUNIT_PARALLEL_RUN=true
BASHUNIT_PROFILE=true
```

It is meant for committing sensible project defaults. Precedence, from highest
to lowest:

1. CLI flags (e.g. `--simple`)
2. Environment variables and the `.env` file
3. `.bashunitrc`
4. Built-in defaults

`.bashunitrc` only fills values that are not already set, so an exported
environment variable or a `.env` entry always wins. `--skip-env-file` skips
`.bashunitrc` as well.

## Default path

> `BASHUNIT_DEFAULT_PATH=directory|file`
Expand Down
45 changes: 45 additions & 0 deletions src/env.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,53 @@

# shellcheck disable=SC2034

##
# Loads a project config file of `KEY=value` lines (comments with `#` and blank
# lines are ignored). Each key is only applied when not already set in the
# environment, so real env vars and CLI flags keep precedence over the file.
# Surrounding single/double quotes and an optional `export ` prefix are stripped.
# Arguments: $1 path to the config file
##
function bashunit::env::load_config_file() {
local file=$1
[ -f "$file" ] || return 0

local line key val
while IFS= read -r line || [ -n "$line" ]; do
# Trim leading whitespace
line=${line#"${line%%[![:space:]]*}"}
case "$line" in
'' | '#'*) continue ;;
esac
case "$line" in export\ *) line=${line#export } ;; esac
case "$line" in
*=*) ;;
*) continue ;;
esac

key=${line%%=*}
val=${line#*=}

# Only accept valid shell identifiers (defends the eval below)
case "$key" in
'' | *[!A-Za-z0-9_]* | [0-9]*) continue ;;
esac

# Strip surrounding matching quotes
case "$val" in
\"*\") val=${val#\"} val=${val%\"} ;;
\'*\') val=${val#\'} val=${val%\'} ;;
esac

# Apply only when unset: env var / CLI flag > config file
eval "export $key=\"\${$key:-\$val}\""
done <"$file"
}

# Load project config (lower precedence than env vars, .env and CLI flags).
# Load .env file (skip if --skip-env-file is used to keep shell environment intact)
if [ "${BASHUNIT_SKIP_ENV_FILE:-false}" != "true" ]; then
bashunit::env::load_config_file ".bashunitrc"
set -o allexport
# shellcheck source=/dev/null
[ -f ".env" ] && source .env
Expand Down
50 changes: 50 additions & 0 deletions tests/acceptance/bashunit_bashunitrc_test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/usr/bin/env bash

function set_up() {
BIN="$PWD/bashunit"
# The outer run inherits an exported BASHUNIT_SHOW_HEADER; clear it so the
# child process starts without it and the config file can take effect.
unset BASHUNIT_SHOW_HEADER
WORKDIR="$(bashunit::temp_dir)/rc_project"
mkdir -p "$WORKDIR"
cat >"$WORKDIR/a_test.sh" <<'TEST'
function test_pass() { assert_same 1 1; }
TEST
}

function test_bashunitrc_config_is_applied() {
printf 'BASHUNIT_SHOW_HEADER=false\n' >"$WORKDIR/.bashunitrc"

local output
output=$(cd "$WORKDIR" && "$BIN" --no-parallel --no-color a_test.sh 2>&1) || true

assert_not_contains "bashunit -" "$output"
assert_contains "All tests passed" "$output"
}

function test_without_bashunitrc_header_is_shown() {
rm -f "$WORKDIR/.bashunitrc"

local output
output=$(cd "$WORKDIR" && "$BIN" --no-parallel --no-color a_test.sh 2>&1) || true

assert_contains "bashunit -" "$output"
}

function test_env_var_overrides_bashunitrc() {
printf 'BASHUNIT_SHOW_HEADER=false\n' >"$WORKDIR/.bashunitrc"

local output
output=$(cd "$WORKDIR" && BASHUNIT_SHOW_HEADER=true "$BIN" --no-parallel --no-color a_test.sh 2>&1) || true

assert_contains "bashunit -" "$output"
}

function test_skip_env_file_skips_bashunitrc() {
printf 'BASHUNIT_SHOW_HEADER=false\n' >"$WORKDIR/.bashunitrc"

local output
output=$(cd "$WORKDIR" && "$BIN" --no-parallel --no-color --skip-env-file a_test.sh 2>&1) || true

assert_contains "bashunit -" "$output"
}
Loading