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
80 changes: 80 additions & 0 deletions docs/why-base-bash-libs.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,86 @@ Beacon is useful evidence because it consumes only the public API committed in
| Repeat checked directory, file, and command handling | `base_std_safe_mkdir`, `base_std_safe_truncate`, and `base_std_run` provide consistent failure behavior | Bundle layout, manifest format, and domain-specific diagnostics |
| Display the dependency that actually ran | `base_require_version` and the immutable `BASE_BASH_LIBS_VERSION`, `BASE_BASH_LIBS_COMMIT`, `BASE_BASH_LIBS_DIRTY_STATE`, and `BASE_BASH_LIBS_PROVENANCE` values expose package identity | Beacon's independent application version and release lifecycle |

## Code-level before and after

The following examples are illustrative, not Beacon's history. They show the
kind of plumbing a small Bash application would otherwise have to maintain.
The `before` version is intentionally short but runnable; the `after` version
uses the same public contracts that Beacon exercises.

<!-- BEGIN BEFORE AFTER CLI -->
### Command parsing and dispatch

Before, an application has to keep option parsing, usage text, validation, and
dispatch consistent by hand:

```bash
set -eu

usage() {
printf 'usage: %s [-o PATH] {status|collect}\n' "${0##*/}"
}

output=''
while getopts ':o:h' option; do
case "$option" in
o) output="$OPTARG" ;;
h) usage; exit 0 ;;
:) printf 'missing value for -%s\n' "$OPTARG" >&2; usage >&2; exit 2 ;;
\?) printf 'unknown option: -%s\n' "$OPTARG" >&2; usage >&2; exit 2 ;;
esac
done
shift "$((OPTIND - 1))"

case "${1-}" in
status) printf 'status=ready\n' ;;
collect) printf 'collecting=%s\n' "${output:-default}" ;;
*) usage >&2; exit 2 ;;
esac
```

After, the CLI contract generates help, option validation, and dispatch from
one declaration boundary:

```bash
base_cli_model_init beacon name=beacon version=0.1.0 \
description="Offline support-bundle collector" handler=beacon_dispatch
base_cli_command beacon status "Show fixture and framework readiness" handler=beacon_dispatch
base_cli_command beacon collect "Create a redacted support bundle" handler=beacon_dispatch
base_cli_option beacon "" output value --output help="Support bundle directory" metavar=PATH
```
<!-- END BEFORE AFTER CLI -->

<!-- BEGIN BEFORE AFTER CLEANUP -->
### Temporary-directory cleanup

Before, an application owns a trap and must preserve it when another cleanup
path or signal handler is added later:

```bash
stage="$(mktemp -d)"
cleanup_stage() {
rm -rf -- "$stage"
}
trap cleanup_stage EXIT
printf 'staging=%s\n' "$stage"
```

After, Base Bash composes cleanup ownership and lets the application transfer a
published path explicitly:

```bash
base_std_make_temp_dir stage beacon-demo
printf 'staging=%s\n' "$stage"
base_std_unregister_cleanup_path "$stage"
```

The lifecycle contract is executable evidence, not just prose:
`bats --filter 'cleanup evidence failure' tests/lifecycle.bats` demonstrates
that a cleanup-log failure does not replace the application's successful exit
status.
<!-- END BEFORE AFTER CLEANUP -->

For example, `beacon_collect` asks `base_std_make_temp_dir` for a staging
directory. The framework registers that path with its composed cleanup
lifecycle. Beacon can return from any later failure without adding a second
Expand Down
37 changes: 37 additions & 0 deletions tests/docs-before-after.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/usr/bin/env bash

set -euo pipefail

docs_before_after_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd -P)"
docs_before_after_source="$docs_before_after_root/docs/why-base-bash-libs.md"
docs_before_after_tmp="$(mktemp -d "${TMPDIR:-/tmp}/beacon-before-after.XXXXXX")"

docs_before_after_cleanup() {
rm -rf -- "$docs_before_after_tmp"
}
trap docs_before_after_cleanup EXIT

docs_before_after_extract() {
local marker_start="$1" marker_end="$2" destination="$3"
awk -v marker_start="$marker_start" -v marker_end="$marker_end" '
$0 == marker_start { in_region = 1; next }
$0 == marker_end { exit }
in_region && /^```bash$/ { in_code = 1; next }
in_region && /^```$/ { in_code = 0; next }
in_region && in_code { print }
' "$docs_before_after_source" > "$destination"
[[ -s "$destination" ]]
/usr/bin/env bash -n "$destination"
shellcheck --shell=bash "$destination"
}

docs_before_after_extract \
'<!-- BEGIN BEFORE AFTER CLI -->' \
'<!-- END BEFORE AFTER CLI -->' \
"$docs_before_after_tmp/cli.sh"
docs_before_after_extract \
'<!-- BEGIN BEFORE AFTER CLEANUP -->' \
'<!-- END BEFORE AFTER CLEANUP -->' \
"$docs_before_after_tmp/cleanup.sh"

printf 'Before-and-after documentation examples passed ShellCheck.\n'
4 changes: 4 additions & 0 deletions tests/docs-contracts.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ grep -Fq '[adoption decision guide](should-i-use-base-bash-libs.md)' "$docs_cont
grep -Fq '[five-minute Beacon tutorial](five-minute-tutorial.md)' "$docs_contract_decision"
grep -Fq '[Beacon documentation and recommended reading path](docs/README.md)' "$docs_contract_readme"
grep -Fq '[use Base Bash in your project](docs/use-in-your-project.md)' "$docs_contract_readme"
grep -Fq '## Code-level before and after' "$docs_contract_why"
grep -Fq 'BEGIN BEFORE AFTER CLI' "$docs_contract_why"
grep -Fq 'BEGIN BEFORE AFTER CLEANUP' "$docs_contract_why"
grep -Fq "bats --filter 'cleanup evidence failure' tests/lifecycle.bats" "$docs_contract_why"

docs_contract_expected_example="$(
sed -n '/^<!-- BEGIN MINIMAL CONSUMER -->$/,/^<!-- END MINIMAL CONSUMER -->$/p' \
Expand Down
2 changes: 2 additions & 0 deletions tests/docs-examples.sh
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,6 @@ shellcheck --shell=bash "$example_script"
/usr/bin/env bash "$example_script"
)

/usr/bin/env bash "$docs_root/tests/docs-before-after.sh"

printf 'Runnable documentation examples passed.\n'
2 changes: 1 addition & 1 deletion tests/validate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ command -v shellcheck > /dev/null 2>&1 || {
printf 'shellcheck is required.\n' >&2
exit 1
}
shellcheck bin/beacon examples/minimal-cli lib/beacon.sh scripts/release-artifact scripts/verify-vendor scripts/resolve-framework-ref tests/validate.sh tests/bash-42-smoke.sh tests/candidate-smoke.sh tests/docs-examples.sh tests/docs-contracts.sh tests/external-consumer-walkthrough.sh tests/minimal-consumer.sh tests/release-artifact.sh || exit $?
shellcheck bin/beacon examples/minimal-cli lib/beacon.sh scripts/release-artifact scripts/verify-vendor scripts/resolve-framework-ref tests/validate.sh tests/bash-42-smoke.sh tests/candidate-smoke.sh tests/docs-examples.sh tests/docs-before-after.sh tests/docs-contracts.sh tests/external-consumer-walkthrough.sh tests/minimal-consumer.sh tests/release-artifact.sh || exit $?

./tests/docs-examples.sh || exit $?
./tests/docs-contracts.sh || exit $?
Expand Down
Loading