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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[![Tests](https://img.shields.io/github/actions/workflow/status/basefoundry/base-bash-libs/tests.yml?branch=main&label=tests)](https://github.com/basefoundry/base-bash-libs/actions/workflows/tests.yml)
[![Release](https://img.shields.io/github/v/release/basefoundry/base-bash-libs?sort=semver&label=release)](https://github.com/basefoundry/base-bash-libs/releases)
[![Bash](https://img.shields.io/badge/Bash-4.2%2B-4EAA25?logo=gnubash&logoColor=white)](docs/support-matrix.md)
[![Bash](https://img.shields.io/badge/Bash-4.2.53%2B-4EAA25?logo=gnubash&logoColor=white)](docs/support-matrix.md)

| Version | License | Install | Release notes |
| --- | --- | --- | --- |
Expand All @@ -19,7 +19,7 @@ paths, cleanup hooks, and import conventions. It is extracted from
independently through Homebrew, source checkouts, vendored copies, or git
submodules.

Requires Bash 4.2+. On macOS, use Homebrew Bash instead of the system `/bin/bash`.
Requires Bash 4.2.53+. On macOS, use Homebrew Bash instead of the system `/bin/bash`.
The shared Base ecosystem boundary is maintained in the [Base ecosystem
platform, license, and release policy](https://github.com/basefoundry/base/blob/main/docs/ecosystem-policy.md).

Expand Down
3 changes: 3 additions & 0 deletions STANDARDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ physical `.sh` file at its library boundary:
- `lib/bash/str/lib_str.sh`
- `lib/bash/arg/lib_arg.sh`
- `lib/bash/list/lib_list.sh`
- `lib/bash/process/lib_process.sh`
- `lib/bash/cli/lib_cli.sh`
- `lib/bash/app/lib_app.sh`

Do not split one library into internal concern files such as separate logging,
path, string, prompt, or command-runner fragments. That kind of split adds a
Expand Down
4 changes: 4 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ when updating an existing script.
- [Testing, vendoring, bundling, and release](v2/architecture.md#delivery)
- [Bash support and validation quickstart](bash-validation-quickstart.md)
- [Consumer-kit contribution path](consumer-kit-contribution.md)
- [Pinned consumption](pinned-consumption.md), [vendor workflow](vendor-workflow.md),
and [single-file distribution](single-file-distribution.md)
- [Integrations](integrations.md) and [community guidance](community.md)
- [Versioning policy](versioning-policy.md) and [release process](release-process.md)
- [CI and default-branch policy](ci-policy.md)
- [Support and threat model](support-policy.md) · [security reporting](../SECURITY.md)

Expand Down
2 changes: 1 addition & 1 deletion docs/discovery/awesome-bash.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ the following links are public and stable. This record was last reviewed on

```text
- [base-bash-libs](https://github.com/basefoundry/base-bash-libs) - A
namespaced Bash 4.2+ runtime and project kit for tested, vendored, and
namespaced Bash 4.2.53+ runtime and project kit for tested, vendored, and
distributable command-line applications.
```

Expand Down
32 changes: 16 additions & 16 deletions docs/support-matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,42 +7,42 @@ unreported pass.

| Dimension | Supported contract | Evidence |
| --- | --- | --- |
| Bash | 4.2.53 minimum; representative 4.4.23 and 5.0.18/5.2.37; current 5.x | Pinned networkless Bash containers, current runner validation, \`tests/compatibility-matrix.sh\`, deterministic property, artifact, and concurrency contracts |
| Bash | 4.2.53 minimum; representative 4.4.23 and 5.0.18/5.2.37; current 5.x | Pinned networkless Bash containers, current runner validation, `tests/compatibility-matrix.sh`, deterministic property, artifact, and concurrency contracts |
| macOS | Pinned GitHub-hosted macOS 14 with Homebrew Bash; system Bash 3.2 is rejected with remediation | macOS validation and unsupported-system-Bash smoke |
| Linux/glibc | Pinned Ubuntu 24.04 runner and Bash 4.2/4.4/5.0/5.2 containers | Ubuntu validation and compatibility workflow |
| Linux/musl | Alpine/musl syntax and option-contract probe when the runner provides Docker | \`tests/compatibility-matrix.sh --container alpine\` |
| Linux/glibc | Pinned Ubuntu 24.04 runner and Bash 4.2.53/4.4.23/5.0.18/5.2.37 containers | Ubuntu validation and compatibility workflow |
| Linux/musl | Alpine/musl syntax and option-contract probe when the runner provides Docker | `tests/compatibility-matrix.sh --container alpine` |
| BSD userland | Best-effort portability checks; no release guarantee until a maintained CI runner is available | Explicitly reported as advisory |
| Locale | UTF-8 and \`C\` locale behavior for parsing, sorting, and diagnostics | Option and parser tests; caller owns locale selection |
| Locale | UTF-8 and `C` locale behavior for parsing, sorting, and diagnostics | Option and parser tests; caller owns locale selection |
| Filesystem | Local POSIX filesystem; symlink and race checks are fail-closed | cleanup, import, bundle, vendor, marker, and artifact-contract tests |
| Network | Core tests are networkless; GitHub/Homebrew integrations are optional and bounded | read-only workflow permissions, Docker \`--network none\`, retry tests |
| Network | Core tests are networkless; GitHub/Homebrew integrations are optional and bounded | read-only workflow permissions, Docker `--network none`, retry tests |

## Strict-option combinations

Sourceable modules must preserve caller state under the supported combinations
\`(none)\`, \`-e\`, \`-u\`, \`pipefail\`, \`-eu\`, \`-ep\`, \`-up\`, and \`-eup\`.
The authoritative probe is \`tests/bash-option-contract.sh\`; release validation
runs it under the pinned Bash 4.2 image as well as the current runner Bash.
`(none)`, `-e`, `-u`, `pipefail`, `-eu`, `-ep`, `-up`, and `-eup`.
The authoritative probe is `tests/bash-option-contract.sh`; release validation
runs it under the pinned Bash 4.2.53 image as well as the current runner Bash.

## Artifact modes

The same checks apply to source checkouts, Homebrew-style installed roots,
verified vendored roots, generated project kits, and deterministic standalone
bundles. \`tests/artifact-contract.sh\` runs all eight supported caller-option
bundles. `tests/artifact-contract.sh` runs all eight supported caller-option
combinations through the source, generated, vendored, and standalone paths;
\`scripts/library-bundle verify\` and \`scripts/vendor verify\` must pass before
`scripts/library-bundle verify` and `scripts/vendor verify` must pass before
an artifact is described as release-ready.

## Reproducible adversarial coverage

\`tests/property-contract.sh\` runs 128 deterministic, seeded cases covering
`tests/property-contract.sh` runs 128 deterministic, seeded cases covering
argv quoting, empty and glob-like fields, repeatable options, marker edits, and
command-like data. The seed is reported on failure so a downstream report can
replay the exact case without network access or a package manager.

\`tests/concurrency-contract.sh\` runs sixteen independent import and cleanup
`tests/concurrency-contract.sh` runs sixteen independent import and cleanup
workers in parallel and verifies unique managed temporary directories. Signal,
process-tree, and launcher cleanup behavior is covered by the launcher BATS
suite; benchmark evidence is checked by \`tests/benchmark-contract.sh\`.
suite; benchmark evidence is checked by `tests/benchmark-contract.sh`.

Workflow formatting and action syntax are required in the separate Quality
workflow. Its shfmt and actionlint images are full-digest pinned and run with
Expand All @@ -52,7 +52,7 @@ users. See [the CI policy](ci-policy.md) for the branch-protection contract.
## Caller responsibilities

Applications must select a supported Bash, avoid mutating framework-owned
\`BASE_BASH_LIBS_*\` state, provide permissions for requested filesystem changes,
and install optional commands such as \`git\`, \`gh\`, \`bats\`, \`shellcheck\`, and
\`shfmt\` when using integrations that require them. The framework never treats
`BASE_BASH_LIBS_*` state, provide permissions for requested filesystem changes,
and install optional commands such as `git`, `gh`, `bats`, `shellcheck`, and
`shfmt` when using integrations that require them. The framework never treats
the presence of an optional command as a reason to weaken core guarantees.
6 changes: 4 additions & 2 deletions docs/v2-api-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@ semantics.
malformed launcher invocations write an explanation and usage to stderr and
return `2`. Application failures are not converted into usage errors.

The launcher itself requires Bash 4.2 or newer. On macOS Bash 3.2, a launcher
The launcher itself requires Bash 4.2 or newer; the tested minimum is Bash 4.2.53.
On macOS Bash 3.2, a launcher
invocation that runs an application searches the supported candidate paths and
re-execs itself with the first usable candidate. `--help`, `--version`, and
`check` remain diagnostic commands and do not run the application.
Expand Down Expand Up @@ -238,7 +239,8 @@ validated, or repeatable (only as the final positional).
direct caller-owned output contract. It is not silently replaced. Bashly,
Argc, Argbash, and similar generators may adapt their build output into the
native model, but they are optional build-time adapters and never runtime
dependencies. The runtime stays Bash 4.2-compatible, avoids `eval`, and has no
dependencies. The runtime stays compatible with Bash 4.2 or newer; the tested
minimum is Bash 4.2.53. It avoids `eval` and has no
mandatory Python, Ruby, Node, or `jq` dependency.

### Application policy contract
Expand Down
2 changes: 1 addition & 1 deletion docs/v2/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ cutover follow [`docs/release-process.md`](../release-process.md) and the
## Platform notes

The supported minimum is Bash 4.2.53. macOS `/bin/bash` 3.2 is outside the
contract; install Homebrew Bash and use `base-bash` or a Bash 4.2+ shebang.
contract; install Homebrew Bash and use `base-bash` or a Bash 4.2.53+ shebang.
Ubuntu/glibc and Alpine/musl paths are exercised where the release workflow has
the required runner/container. BSD and other userlands are advisory until a
consumer runs the compatibility matrix there. Locale, filesystem permissions,
Expand Down
5 changes: 3 additions & 2 deletions lib/bash/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@ modules.

## Caller Runtime Contract

All public modules support Bash 4.2 or newer with every combination of caller-
selected `errexit`, `nounset`, and `pipefail`. Sourcing a module is passive: it
All public modules support Bash 4.2 or newer; the tested minimum is Bash 4.2.53.
They support every combination of caller-selected `errexit`, `nounset`, and
`pipefail`. Sourcing a module is passive: it
does not change those settings, any other `set` or `shopt` option, `IFS`,
`OPTIND`, the working directory, the umask, traps, exports, or ordinary
positional arguments. After sourcing `lib_std.sh`, callers explicitly invoke
Expand Down
2 changes: 1 addition & 1 deletion lib/bash/app/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ The deterministic precedence is `CLI > environment > project > user >
default`. `base_app_config_report` prints `key`, source, and effective value
as tab-separated records and redacts values declared with `secret=true`.
Backslashes, tabs, carriage returns, and newlines in fields are escaped as
`\\`, `\\t`, `\\r`, and `\\n` so each record remains one safe line. See the
`\`, `\t`, `\r`, and `\n` so each record remains one safe line. See the
[shared TSV field escaping contract](../str/README.md#shared-tsv-field-escaping).
`base_app_config_set_cli` is a programmatic equivalent of `--cli key=value`.

Expand Down
15 changes: 8 additions & 7 deletions lib/bash/std/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ versions that support namerefs.
- `base_require_version <version>`: returns `1` when the loaded package version
is older and `2` for malformed usage/version values.
- `base_std_check_bash_version`: returns zero for Bash 4.2 or newer and reports the
required version otherwise.
required version otherwise. The tested minimum is Bash 4.2.53.
- `base_std_is_interactive`: returns zero when stdin is attached to an interactive TTY.
- `base_std_import <path>...`: sources package-relative modules from the loaded
`lib/bash` root; returns a recoverable failure for missing, unsafe, cyclic,
Expand Down Expand Up @@ -216,9 +216,9 @@ main() {

Base entrypoints preload this library through Base's own runtime bootstrap. The
`base-bash` launcher provides the same stdlib preload pattern without Base
runtime state. Callers should run on Bash 4.2 or newer; the library has passive
Bash version helpers, but sourcing it does not prompt, install packages, or
re-exec the caller.
runtime state. Callers should run on Bash 4.2 or newer; the tested minimum is
Bash 4.2.53. The library has passive Bash version helpers, but sourcing it does
not prompt, install packages, or re-exec the caller.

## Initialization Contract

Expand Down Expand Up @@ -274,9 +274,10 @@ initializer. A launcher may pass `--source` directly; `BASE_BASH_LIBS_BOOTSTRAP_
is only a fallback for callers that cannot provide that option.

The library preserves caller-selected `errexit`, `nounset`, and `pipefail`
settings and supports every combination on Bash 4.2 or newer. It does not
enable or disable those options for the caller. A top-level interactive or
`bash -c` source has no outer `BASH_SOURCE` frame; without a bootstrap override,
settings and supports every combination on Bash 4.2 or newer; the tested minimum
is Bash 4.2.53. It does not enable or disable those options for the caller. A
top-level interactive or `bash -c` source has no outer `BASH_SOURCE` frame;
without a bootstrap override,
`BASE_BASH_LIBS_SCRIPT_DIR` and `base_std_get_my_source_dir` use the current working directory in
that case. Predicate helpers can intentionally return nonzero, so callers using
`errexit` should invoke them in `if`, `while`, `&&`, or another normal Bash
Expand Down
2 changes: 1 addition & 1 deletion lib/bash/str/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ helpers are available.

Modules that emit line-oriented tab-delimited records share the internal
`__base_bash_libs_str_escape_tsv_field__` primitive. It escapes backslashes,
tabs, newlines, and carriage returns as `\\`, `\\t`, `\\n`, and `\\r` in that
tabs, newlines, and carriage returns as `\`, `\t`, `\n`, and `\r` in that
order, keeping each record on one physical line without changing ordinary
values. The Git and application modules import `lib_str.sh` automatically when
needed; the internal helper is not application API.
Expand Down
30 changes: 30 additions & 0 deletions tests/docs-contract.sh
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,36 @@ for link in SECURITY.md docs/support-policy.md docs/threat-model.md; do
}
done

if grep -nF '\`' docs/support-matrix.md > /dev/null; then
printf 'Support-matrix Markdown must not escape code-span backticks.\n' >&2
exit 1
fi
if grep -nF '\\t' lib/bash/str/README.md lib/bash/app/README.md > /dev/null; then
printf 'String and application docs must show one backslash in escape sequences.\n' >&2
exit 1
fi
for module in process cli app; do
grep -F -- "lib/bash/$module/lib_$module.sh" STANDARDS.md > /dev/null || {
printf 'STANDARDS.md is missing the %s module.\n' "$module" >&2
exit 1
}
done
for link in pinned-consumption.md vendor-workflow.md single-file-distribution.md \
integrations.md community.md versioning-policy.md release-process.md; do
grep -F "$link" docs/README.md > /dev/null || {
printf 'Documentation index is missing: %s\n' "$link" >&2
exit 1
}
done
grep -F 'Bash-4.2.53%2B' README.md > /dev/null || {
printf 'README must advertise the 4.2.53 Bash floor.\n' >&2
exit 1
}
grep -F 'Requires Bash 4.2.53+' README.md > /dev/null || {
printf 'README must state the 4.2.53 Bash floor.\n' >&2
exit 1
}

release_process=docs/release-process.md
for release_contract_text in \
'lib/bash/base-bash-libs.release' \
Expand Down
6 changes: 3 additions & 3 deletions tests/validate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ if ! printf '%s\n' "$readme_head" | grep -F "[![Release](https://img.shields.io/
printf 'README.md is missing the GitHub release badge.\n' >&2
exit 1
fi
if ! printf '%s\n' "$readme_head" | grep -F "[![Bash](https://img.shields.io/badge/Bash-4.2%2B-4EAA25?logo=gnubash&logoColor=white)](docs/support-matrix.md)" > /dev/null; then
if ! printf '%s\n' "$readme_head" | grep -F "[![Bash](https://img.shields.io/badge/Bash-4.2.53%2B-4EAA25?logo=gnubash&logoColor=white)](docs/support-matrix.md)" > /dev/null; then
printf 'README.md is missing the supported Bash version badge.\n' >&2
exit 1
fi
Expand Down Expand Up @@ -356,8 +356,8 @@ if [[ "$release_status" == pending-ga-asset ]]; then
fi
fi

if ! sed -n '1,30p' README.md | grep -F 'Requires Bash 4.2+' > /dev/null; then
printf 'README.md must state the Bash 4.2+ requirement near the top-level entry point.\n' >&2
if ! sed -n '1,30p' README.md | grep -F 'Requires Bash 4.2.53+' > /dev/null; then
printf 'README.md must state the Bash 4.2.53+ requirement near the top-level entry point.\n' >&2
exit 1
fi

Expand Down
Loading