diff --git a/README.md b/README.md index 5135e7b..2170e17 100644 --- a/README.md +++ b/README.md @@ -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 | | --- | --- | --- | --- | @@ -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). diff --git a/STANDARDS.md b/STANDARDS.md index f66f37e..47ba2a9 100644 --- a/STANDARDS.md +++ b/STANDARDS.md @@ -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 diff --git a/docs/README.md b/docs/README.md index 1c394ea..178e4bc 100644 --- a/docs/README.md +++ b/docs/README.md @@ -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) diff --git a/docs/discovery/awesome-bash.md b/docs/discovery/awesome-bash.md index 5115b67..ad3a07a 100644 --- a/docs/discovery/awesome-bash.md +++ b/docs/discovery/awesome-bash.md @@ -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. ``` diff --git a/docs/support-matrix.md b/docs/support-matrix.md index d40710f..4bec8a5 100644 --- a/docs/support-matrix.md +++ b/docs/support-matrix.md @@ -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 @@ -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. diff --git a/docs/v2-api-contract.md b/docs/v2-api-contract.md index 6513e9d..b551b9f 100644 --- a/docs/v2-api-contract.md +++ b/docs/v2-api-contract.md @@ -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. @@ -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 diff --git a/docs/v2/architecture.md b/docs/v2/architecture.md index e555cbc..c55609d 100644 --- a/docs/v2/architecture.md +++ b/docs/v2/architecture.md @@ -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, diff --git a/lib/bash/README.md b/lib/bash/README.md index ff5190b..fda437e 100644 --- a/lib/bash/README.md +++ b/lib/bash/README.md @@ -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 diff --git a/lib/bash/app/README.md b/lib/bash/app/README.md index 9e011f7..a346d3c 100644 --- a/lib/bash/app/README.md +++ b/lib/bash/app/README.md @@ -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`. diff --git a/lib/bash/std/README.md b/lib/bash/std/README.md index 0618ac9..2eff7b8 100644 --- a/lib/bash/std/README.md +++ b/lib/bash/std/README.md @@ -80,7 +80,7 @@ versions that support namerefs. - `base_require_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 ...`: sources package-relative modules from the loaded `lib/bash` root; returns a recoverable failure for missing, unsafe, cyclic, @@ -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 @@ -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 diff --git a/lib/bash/str/README.md b/lib/bash/str/README.md index f563bf7..fd9aabf 100644 --- a/lib/bash/str/README.md +++ b/lib/bash/str/README.md @@ -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. diff --git a/tests/docs-contract.sh b/tests/docs-contract.sh index dec83ac..921bb19 100755 --- a/tests/docs-contract.sh +++ b/tests/docs-contract.sh @@ -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' \ diff --git a/tests/validate.sh b/tests/validate.sh index f0d5669..54e3107 100755 --- a/tests/validate.sh +++ b/tests/validate.sh @@ -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 @@ -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