From bd38dd0ebdd0d10d4a2f5fb10afb5aac2f953033 Mon Sep 17 00:00:00 2001 From: Wes Dean Date: Tue, 18 Aug 2026 16:18:14 -0400 Subject: [PATCH 01/15] Document multi-flavor release artifacts --- ...lopment-Stripped-and-Minified-Artifacts.md | 316 ++++++++++++++++++ 1 file changed, 316 insertions(+) create mode 100644 doc/adr/ADR-052-Publish-Development-Stripped-and-Minified-Artifacts.md diff --git a/doc/adr/ADR-052-Publish-Development-Stripped-and-Minified-Artifacts.md b/doc/adr/ADR-052-Publish-Development-Stripped-and-Minified-Artifacts.md new file mode 100644 index 0000000..fd21357 --- /dev/null +++ b/doc/adr/ADR-052-Publish-Development-Stripped-and-Minified-Artifacts.md @@ -0,0 +1,316 @@ +# ADR-052: Publish Development, Stripped, and Minified Artifacts + +Date: 2026-08-18 + +## Status + +Accepted + +## Intent and Documentation Posture + +This ADR defines Bootstrap's generated release-artifact flavors and the build-time +use of Bash-Minifier. It preserves the repository's modular-source architecture +while producing three executable representations of the same Bootstrap program. + +This decision changes build, test, checksum, and release behavior. It does not +change Bootstrap's runtime package-management contract or add a runtime dependency +on Bash-Minifier, bashdeps, `dependencies.txt`, or `vendor/`. + +## Context + +ADR-009 and ADR-010 established a deliberately small distribution surface: modular +maintained source is assembled into one executable `dist/bootstrap.bash` artifact. +That artifact historically removed full-line comments from maintained source while +retaining a small generated header. + +There are now three useful audiences for generated Bootstrap artifacts: + +1. contributors and reviewers who benefit from a fully commented assembled script; +2. ordinary consumers who benefit from the familiar comment-stripped standalone + script; and +3. consumers who value the smallest practical standalone representation and are + willing to trade source readability for size. + +A single generated representation cannot optimize for all three purposes. The +source tree should remain the maintained source of truth, while the build can +produce multiple equivalent consumer representations from the same ordered source +set. + +Love Borgström's Bash-Minifier project provides a state-aware Bash minifier that +can consume a script from standard input and emit a minified Bash script to +standard output. Bootstrap already centralizes ordinary external build/development +artifacts through bashdeps under ADR-051, so Bash-Minifier fits the established +manifest-managed dependency boundary. + +Introducing Bash-Minifier also changes one repository-specific consequence of +ADR-051. At the time ADR-051 was accepted, Bootstrap's `make build` required no +external artifact and therefore succeeded from a clean checkout without a +`vendor/` tree. A minified build flavor necessarily requires an already-prepared +minifier. The network boundary remains unchanged: `make build` still does not +acquire or repair dependencies, but it now fails clearly when the prepared +minifier is absent. + +## Decision Drivers + +- Preserve one maintained modular source tree while serving different artifact + readability and size needs. +- Keep the existing `bootstrap.bash` filename as the ordinary/default flavor. +- Make all released flavors derive from the complete assembled program, including + all imported project libraries. +- Keep build-time dependency acquisition explicit and centralized through + bashdeps. +- Pin Bash-Minifier to immutable reviewed bytes rather than a moving branch. +- Keep `make build` network-free and non-repairing. +- Apply the same functional regression suite to every executable flavor. +- Publish a checksum for every released executable artifact. +- Preserve executable permissions across all three flavors. +- Keep Bash-Minifier completely outside Bootstrap's runtime dependency surface. + +## Decision + +### Build three executable flavors + +`make build` SHALL produce these executable files: + +```text +dist/bootstrap.dev.bash +dist/bootstrap.bash +dist/bootstrap.min.bash +``` + +The files represent the same Bootstrap program at different transformation +levels. + +`bootstrap.dev.bash` is the development artifact. It SHALL contain the generated +release metadata and the complete ordered maintained source with comments intact. +No project-source comment-stripping or minification step is applied to this +artifact. + +`bootstrap.bash` remains the ordinary/default artifact. It SHALL be derived from +the complete assembled development artifact by removing full-line comments while +preserving the shebang. Inline comments that are part of executable lines are not +the responsibility of this stripping stage. + +`bootstrap.min.bash` SHALL be produced by passing the complete comment-stripped +`bootstrap.bash` bytes through the pinned Bash-Minifier artifact. Minification is +therefore applied after assembly and after full-line comment stripping, so code +originating in any project library receives the same transformation as code from +the entry point. + +All three generated scripts SHALL be executable. The build SHALL publish each +artifact only after its corresponding transformation succeeds. + +### Build six release files including checksums + +`make build` SHALL also produce one SHA-256 checksum file for each executable: + +```text +dist/bootstrap.dev.bash.256 +dist/bootstrap.bash.256 +dist/bootstrap.min.bash.256 +``` + +Each `.256` file SHALL describe the exact bytes of its corresponding executable. +The build SHALL use the repository's supported SHA-256 capability (`sha256sum` or +`shasum -a 256`) and fail clearly when neither implementation is available. + +The existing `checksums` Make target MAY remain as a compatibility/convenience +alias, but checksum generation belongs to the ordinary `build` result. A +successful `make build` therefore means that all six expected distribution files +exist. + +### Manage Bash-Minifier through bashdeps + +Bootstrap SHALL add the Bash-Minifier source artifact to `dependencies.txt`. + +The selected upstream bytes are: + +```text +repository: Zuzzuc/Bash-minifier +commit: 9c824e20815a5bca2153ec25ecc02a4edea1430e +source: Minify.sh +destination: vendor/bash-minifier.bash +sha256: 93cb422360db4cc410d19b068eb074da020a4a743f0eebc9c442d1e5acd90e9b +``` + +The manifest URL SHALL be commit-pinned rather than branch-pinned. The destination +name is a Bootstrap consumer convention; it does not imply that the upstream file +has that name. + +Bash-Minifier remains an ordinary manifest-managed development dependency under +ADR-051. Make SHALL NOT add a second direct download path for it. + +### Preserve the Make dependency boundary + +`make deps` MAY use the network and SHALL materialize the pinned Bash-Minifier +artifact through bashdeps together with the repository's other declared external +artifacts. + +`make deps-check` SHALL continue to verify prepared dependency state without +network access or repair. + +`make build` SHALL NOT invoke `deps`, `deps-check`, curl, wget, or any other +acquisition mechanism. It SHALL require an already-present +`vendor/bash-minifier.bash` and fail with actionable guidance when that dependency +is missing. + +A fresh checkout therefore has two supported preparation paths: + +```text +make all +``` + +or: + +```text +make deps +make build +``` + +`make all` SHALL preserve ADR-051 ordering by completing dependency synchronization +before invoking the network-free `build` target. + +This decision supersedes ADR-051 only where ADR-051 states that Bootstrap's clean +`make build` succeeds without a vendor tree. The general ADR-051 dependency and +network boundaries remain in force. + +### Test every executable flavor + +The behavior-oriented Bats suite SHALL be applied to all three executable build +artifacts rather than treating minification as an untested packaging-only step. + +Tests SHOULD assert behavior rather than formatting that is intentionally changed +by minification. Assertions that depend on metadata occupying separate physical +lines, comments being present, or other representation details SHALL be replaced +with behavior- or byte-contract assertions where appropriate. + +CI SHALL exercise the same Make test interface used locally and SHALL retain +separate report output when needed so failures can be associated with an artifact +flavor. + +Build-specific regression coverage SHALL verify at least: + +- a fresh `make build` does not acquire missing Bash-Minifier state and fails + clearly when that state is absent; +- prepared dependency state allows `make build` to create all six files; +- all three scripts are executable; +- each `.256` file verifies its corresponding script; +- the development artifact retains full-line source comments; +- the ordinary artifact removes full-line comments while preserving the shebang; +- the minified artifact is produced from the stripped artifact and remains + executable and behaviorally valid; and +- all three consumer artifacts remain functional after `vendor/` is removed. + +### Publish all six files in GitHub releases + +Release automation SHALL prepare and verify dependencies before building. It SHALL +publish all six generated distribution files and include all six in release +provenance/attestation scope where the workflow supports attestation. + +The three executable artifacts are peer release flavors. `bootstrap.bash` remains +the conventional/default filename so existing download URLs and wrapper examples +continue to refer to the ordinary comment-stripped representation unless a user +chooses another flavor explicitly. + +### Runtime isolation remains mandatory + +None of the three released executable scripts may require Bash-Minifier, +`bashdeps.bash`, `dependencies.txt`, or `vendor/` after construction. + +Bash-Minifier transforms build output only. Its implementation is not embedded as +runtime tooling and it is not invoked by Bootstrap during normal execution. + +## Considered Alternatives + +### Replace bootstrap.bash with only a minified artifact + +This would minimize one download but would make the ordinary artifact materially +harder to inspect and would remove the established readable distribution flavor. +The project instead publishes minification as an explicit additional flavor. + +### Keep only the development and minified artifacts + +This would eliminate the historical stripped artifact. It was rejected because +`bootstrap.bash` is already the conventional consumer filename and provides a +useful middle ground between fully commented and aggressively minified output. + +### Minify the maintained source files individually before assembly + +Per-file minification would couple transformation behavior to internal source +layout and could produce different semantics at file boundaries. The project +instead assembles one complete program first, strips that complete program, then +minifies the complete stripped artifact. + +### Let Bash-Minifier perform both comment stripping and minification from the development artifact + +Bash-Minifier itself removes comments as part of minification, but the requested +artifact model distinguishes the ordinary stripped representation from the +minified representation. Explicitly deriving the minified artifact from +`bootstrap.bash` makes that lineage observable and testable. + +### Download Bash-Minifier directly from Make + +This would violate ADR-051 by duplicating ordinary dependency acquisition policy +outside bashdeps. Bash-Minifier fits the existing manifest contract and therefore +belongs in `dependencies.txt`. + +### Make build invoke make deps automatically + +This would make a fresh build convenient but would silently add network access and +repository mutation to the ordinary build target. The project retains explicit +preparation through `deps` or `all`. + +### Test only bootstrap.bash and perform smoke checks on the other flavors + +A minifier is a semantic source-to-source transformation. Treating the minified +artifact as packaging-only would leave the highest-risk transformation with the +weakest behavioral validation. The full behavior suite therefore applies to each +flavor. + +## Consequences + +Bootstrap releases become larger in aggregate because they contain three +executable representations and three checksum files. Individual consumers can +still download only the flavor they want. + +The build now depends on prepared Bash-Minifier state, so `make build` from a fresh +checkout fails until `make deps` has been run. This is intentional and preserves +the explicit network boundary established by ADR-051. + +CI work increases because the behavior suite runs against three generated +representations. That additional cost buys direct evidence that comment stripping +and minification preserve behavior. + +The default `bootstrap.bash` release URL remains stable for existing consumers. +Users who want maximum inspectability can choose `bootstrap.dev.bash`; users who +want the smallest representation can choose `bootstrap.min.bash`. + +The project takes a build-time dependency on the behavior of a specific reviewed +Bash-Minifier commit. Updating that dependency requires an ordinary +`dependencies.txt` URL/digest change and the same multi-artifact test suite. + +## Open Questions and Follow-Ups + +Future projects may choose the same three-flavor convention. Bootstrap's ADR does +not require other repositories to share identical source assembly mechanics; it +records the artifact semantics and dependency boundary used here. + +If future Bash-Minifier releases introduce a stable release/tag policy that is +preferable to commit pinning, Bootstrap may move to an immutable release reference +through an explicit dependency update without changing the architectural boundary. + +If one flavor eventually acquires materially different runtime semantics, this ADR +should be revisited rather than allowing flavor names to imply equivalence that +validation no longer supports. + +## Related Decisions + +- Supersedes in part: ADR-009 (single published executable artifact) +- Supersedes in part: ADR-010 (single generated distribution artifact) +- Refines: ADR-012 (Make as the orchestration interface) +- Related to: ADR-029 (reproducible and verifiable releases) +- Related to: ADR-039 (observable-behavior testing) +- Related to: ADR-040 (deterministic behavior) +- Related to: ADR-042 (minimize the trusted computing base) +- Related to: ADR-046 (documentation-driven, test-second development) +- Supersedes in part: ADR-051 (clean build requires no prepared vendor state) From 43e4608dcdf36eb18c1500ec0f32944c9adffcfa Mon Sep 17 00:00:00 2001 From: Wes Dean Date: Tue, 18 Aug 2026 16:19:10 -0400 Subject: [PATCH 02/15] Build development stripped and minified artifacts --- Makefile | 139 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 108 insertions(+), 31 deletions(-) diff --git a/Makefile b/Makefile index 94a8f59..28e9b5f 100644 --- a/Makefile +++ b/Makefile @@ -4,14 +4,18 @@ # project. # # Developers edit the modular source files listed in SOURCE_FILES. Users consume -# the generated dist/bootstrap.bash artifact produced by make build or make all. +# one of the generated dist/bootstrap*.bash artifacts produced by make build or +# make all. SHELL := /bin/bash .SHELLFLAGS := -eu -o pipefail -c DIST_DIR := dist +DIST_DEV_SCRIPT := $(DIST_DIR)/bootstrap.dev.bash DIST_SCRIPT := $(DIST_DIR)/bootstrap.bash -DIST_CHECKSUM := $(DIST_SCRIPT).sha256 +DIST_MIN_SCRIPT := $(DIST_DIR)/bootstrap.min.bash +DIST_SCRIPTS := $(DIST_DEV_SCRIPT) $(DIST_SCRIPT) $(DIST_MIN_SCRIPT) +DIST_CHECKSUMS := $(addsuffix .256,$(DIST_SCRIPTS)) SOURCE_FILES := lib/build-metadata.bash lib/runtime/exit-codes.bash lib/runtime/context.bash lib/runtime/config.bash lib/runtime/privilege.bash lib/runtime/logging.bash lib/runtime/recovery.bash lib/runtime/diagnostics.bash lib/backend/diagnostics.bash lib/manifest/parser.bash lib/planner/action-record.bash lib/planner/planner.bash lib/resolver/resolved-action.bash lib/backend/apt.bash lib/backend/apk.bash lib/backend/dnf.bash lib/backend/backend.bash lib/resolver/resolver.bash lib/executor/execution-result.bash lib/executor/apt.bash lib/executor/apk.bash lib/executor/dnf.bash lib/executor/executor.bash src/bootstrap.bash TESTS_DIR := tests/ TEST_SCRIPTS := ${TESTS_DIR}/*.bats @@ -24,6 +28,7 @@ BASHDEPS_VERSION := 0.0.6 BASHDEPS_URL := https://github.com/wesley-dean/bashdeps/releases/download/v$(BASHDEPS_VERSION)/bashdeps.bash BASHDEPS_SHA256 := bb6c807fa12c010950bda06172ac0611d278c57aca1f8352f41502d0d76b4e6c DOXYGEN_BASH_FILTER := $(VENDOR_DIR)/doxygen-bash.awk +BASH_MINIFIER := $(VENDOR_DIR)/bash-minifier.bash REFERENCE_DOC_DIR := doc/reference E2E_TEST_DIR := ${TESTS_DIR}/e2e @@ -37,7 +42,7 @@ BUILD_DATE ?= $(shell git show -s --format=%cI HEAD 2>/dev/null || printf 'unkno .PHONY: all build check checksums clean deps deps-check distclean docs docs-clean FORCE format test test-report test-e2e test-e2e-platform test-e2e-apt test-e2e-apk test-e2e-dnf test-e2e-ubuntu test-e2e-alpine test-e2e-redhat verify-bashdeps ## -# Synchronize development dependencies, then build the consumer artifact. +# Synchronize development dependencies, then build the consumer artifacts. # # The recursive Make invocation keeps ordering explicit under parallel Make. # Plain `make build` remains independent of dependency synchronization and does @@ -47,11 +52,17 @@ all: deps $(MAKE) --no-print-directory build ## -# Build the standalone Bootstrap distribution artifact from maintained source. +# Build all standalone Bootstrap distribution artifacts and checksums. # -build: $(DIST_SCRIPT) +# This target consumes already-prepared Bash-Minifier state. It does not acquire +# or verify dependencies and therefore remains network-free. +# +build: $(DIST_SCRIPTS) $(DIST_CHECKSUMS) -$(DIST_SCRIPT): $(SOURCE_FILES) +## +# Assemble the fully commented development artifact from maintained source. +# +$(DIST_DEV_SCRIPT): $(SOURCE_FILES) mkdir -p "$(DIST_DIR)" { \ printf '%s\n' '#!/usr/bin/env bash'; \ @@ -63,18 +74,62 @@ $(DIST_SCRIPT): $(SOURCE_FILES) printf 'BOOTSTRAP_BUILD_DATE=%q\n' "$(BUILD_DATE)"; \ printf 'BOOTSTRAP_BUILD_COMMIT=%q\n' "$(BUILD_COMMIT)"; \ printf '\n'; \ - cat $(SOURCE_FILES) \ - | sed '/^#/d'; \ + cat $(SOURCE_FILES); \ } >"$@.tmp" chmod 0755 "$@.tmp" mv "$@.tmp" "$@" +## +# Derive the ordinary artifact by removing full-line comments from the complete +# assembled development artifact while preserving its shebang. +# +$(DIST_SCRIPT): $(DIST_DEV_SCRIPT) + sed '1b; /^[[:space:]]*#/d' "$<" >"$@.tmp" + chmod 0755 "$@.tmp" + mv "$@.tmp" "$@" + +## +# Derive the minified artifact from the complete comment-stripped artifact. +# +# Bash-Minifier is manifest-managed dependency state. A missing dependency is an +# actionable build failure rather than a reason for build to access the network. +# +$(DIST_MIN_SCRIPT): $(DIST_SCRIPT) + @test -f "$(BASH_MINIFIER)" || { \ + printf '%s\n' 'Missing build dependency vendor/bash-minifier.bash; run make deps or make all' >&2; \ + exit 1; \ + } + bash "$(BASH_MINIFIER)" -F <"$(DIST_SCRIPT)" >"$@.tmp" + chmod 0755 "$@.tmp" + mv "$@.tmp" "$@" + +## +# Generate a SHA-256 checksum companion for one built executable artifact. +# +$(DIST_DIR)/%.bash.256: $(DIST_DIR)/%.bash + @digest=''; \ + if command -v sha256sum >/dev/null 2>&1; then \ + digest="$$(sha256sum "$<" | awk '{print $$1}')"; \ + elif command -v shasum >/dev/null 2>&1; then \ + digest="$$(shasum -a 256 "$<" | awk '{print $$1}')"; \ + else \ + printf '%s\n' 'No SHA-256 command is available for build checksums' >&2; \ + exit 1; \ + fi; \ + printf '%s %s\n' "$$digest" "$(notdir $<)" >"$@.tmp"; \ + mv "$@.tmp" "$@" + +## +# Compatibility/convenience alias: successful build already creates checksums. +# +checksums: build + ## # Run ShellCheck against hand-maintained Bash source files. # -# The generated distribution artifact is intentionally excluded here. The -# checked source files are the source of truth, and make build assembles them into -# the release artifact. +# Generated distribution artifacts are intentionally excluded here. The checked +# source files are the source of truth, and make build derives all three consumer +# representations from them. # check: shellcheck $(SOURCE_FILES) @@ -86,17 +141,49 @@ format: shfmt -w $(SOURCE_FILES) ## -# Run the behavior-oriented Bats test suite. -# -# The generated distribution artifact is a prerequisite because tests exercise -# the executable users will receive rather than the modular source files. -# -test: $(DIST_SCRIPT) - bats $(TEST_SCRIPTS) +# Run the behavior-oriented Bats test suite against every executable flavor. +# +# Existing tests intentionally address dist/bootstrap.bash as the consumer path. +# The runner temporarily places each built flavor at that path, restores the +# ordinary stripped artifact after each run, and therefore applies the same +# observable-behavior contract to development, stripped, and minified output. +# +test: build + @set -e; \ + backup="$(DIST_DIR)/.bootstrap.bash.test-original"; \ + cp "$(DIST_SCRIPT)" "$$backup"; \ + trap 'mv -f "$$backup" "$(DIST_SCRIPT)"' EXIT; \ + for entry in \ + 'development|$(DIST_DEV_SCRIPT)' \ + 'stripped|'"$$backup" \ + 'minified|$(DIST_MIN_SCRIPT)'; do \ + label="$${entry%%|*}"; \ + artifact="$${entry#*|}"; \ + cp "$$artifact" "$(DIST_SCRIPT)"; \ + chmod 0755 "$(DIST_SCRIPT)"; \ + printf 'Testing %s artifact\n' "$$label"; \ + bats $(TEST_SCRIPTS); \ + done -test-report: $(DIST_SCRIPT) - mkdir -p "$(TEST_RESULTS_DIR)" - bats --formatter junit $(TEST_SCRIPTS) >"$(TEST_RESULTS_DIR)/bats.xml" +## +# Run the Bats suite against every flavor and emit one JUnit file per flavor. +# +test-report: build + @mkdir -p "$(TEST_RESULTS_DIR)" + @set -e; \ + backup="$(DIST_DIR)/.bootstrap.bash.test-original"; \ + cp "$(DIST_SCRIPT)" "$$backup"; \ + trap 'mv -f "$$backup" "$(DIST_SCRIPT)"' EXIT; \ + for entry in \ + 'development|$(DIST_DEV_SCRIPT)' \ + 'stripped|'"$$backup" \ + 'minified|$(DIST_MIN_SCRIPT)'; do \ + label="$${entry%%|*}"; \ + artifact="$${entry#*|}"; \ + cp "$$artifact" "$(DIST_SCRIPT)"; \ + chmod 0755 "$(DIST_SCRIPT)"; \ + bats --formatter junit $(TEST_SCRIPTS) >"$(TEST_RESULTS_DIR)/bats-$${label}.xml"; \ + done ## # Force the bootstrap file target to validate cached bytes whenever requested. @@ -276,16 +363,6 @@ test-e2e-platform: build docker build -t "$${image}" -f "$${context}/Dockerfile" "$${context}"; \ docker run --rm "$${image}" -## -# Generate SHA-256 checksums for release artifacts. -# -# The checksum file is written from inside the distribution directory so users -# can download bootstrap.bash and bootstrap.bash.sha256 into the same working -# directory and verify the artifact with sha256sum -c bootstrap.bash.sha256. -# -checksums: $(DIST_SCRIPT) - cd "$(DIST_DIR)" && sha256sum "$(notdir $(DIST_SCRIPT))" >"$(notdir $(DIST_CHECKSUM))" - clean: rm -rf "$(DIST_DIR)" From b0843c5af32805f2036c8728e7fc0121b47019cb Mon Sep 17 00:00:00 2001 From: Wes Dean Date: Tue, 18 Aug 2026 16:19:24 -0400 Subject: [PATCH 03/15] Add Bash-Minifier build dependency --- dependencies.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/dependencies.txt b/dependencies.txt index 8f6b38e..d235067 100644 --- a/dependencies.txt +++ b/dependencies.txt @@ -7,3 +7,4 @@ # with the pinned released bashdeps bootstrap. id=wesley-dean/bash-doxygen@0.0.6 url=https://raw.githubusercontent.com/wesley-dean/bash-doxygen/v0.0.6/doxygen-bash.awk dest=vendor/doxygen-bash.awk digest=sha256:dc09bccac7cdb69940b2b34f0c2a92d862c5979d578364ec66782ac92338a3ea +id=Zuzzuc/Bash-minifier@9c824e20815a5bca2153ec25ecc02a4edea1430e url=https://raw.githubusercontent.com/Zuzzuc/Bash-minifier/9c824e20815a5bca2153ec25ecc02a4edea1430e/Minify.sh dest=vendor/bash-minifier.bash digest=sha256:93cb422360db4cc410d19b068eb074da020a4a743f0eebc9c442d1e5acd90e9b From 62bcfa8c0f6f1e2f03eebd36c71474e2341f7dfc Mon Sep 17 00:00:00 2001 From: Wes Dean Date: Tue, 18 Aug 2026 16:20:36 -0400 Subject: [PATCH 04/15] Test multi-flavor build boundaries --- tests/build-deps.bats | 98 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 91 insertions(+), 7 deletions(-) diff --git a/tests/build-deps.bats b/tests/build-deps.bats index 36d64b2..90d4c86 100644 --- a/tests/build-deps.bats +++ b/tests/build-deps.bats @@ -17,6 +17,20 @@ sha256_of() { fi } +verify_checksum() { + local checksum=$1 + local directory filename + + directory="$(dirname "$checksum")" + filename="$(basename "$checksum")" + + if command -v sha256sum >/dev/null 2>&1; then + (cd "$directory" && sha256sum -c "$filename") + else + (cd "$directory" && shasum -a 256 -c "$filename") + fi +} + prepare_make_fixture() { local fixture_root=$1 @@ -76,7 +90,20 @@ EOF chmod 0755 "$path" } -@test "build succeeds without acquiring dependency state" { +write_recording_minifier() { + local path=$1 + + cat >"$path" <<'EOF' +#!/usr/bin/env bash +set -euo pipefail + +cat >"${BASH_MINIFIER_INPUT:?}" +cat "${BASH_MINIFIER_INPUT}" +EOF + chmod 0755 "$path" +} + +@test "build does not acquire a missing Bash-Minifier dependency" { local dist_dir vendor_dir fake_bin curl_sentinel dist_dir="${TEST_TMPDIR}/dist" @@ -92,17 +119,62 @@ EOF make -C "$REPO_ROOT" build \ DIST_DIR="$dist_dir" \ VENDOR_DIR="$vendor_dir" \ + BASH_MINIFIER="${vendor_dir}/bash-minifier.bash" \ VERSION=0.0.0-test \ BUILD_COMMIT=test \ BUILD_DATE=2026-08-18T00:00:00Z - [ "$status" -eq 0 ] - [ -x "${dist_dir}/bootstrap.bash" ] + [ "$status" -ne 0 ] [ ! -e "$vendor_dir" ] [ ! -e "$curl_sentinel" ] + [[ "$output" == *'Missing build dependency vendor/bash-minifier.bash; run make deps or make all'* ]] +} + +@test "prepared Bash-Minifier state builds all executable flavors and checksums" { + local dist_dir vendor_dir minifier input_capture artifact checksum + + dist_dir="${TEST_TMPDIR}/prepared-dist" + vendor_dir="${TEST_TMPDIR}/prepared-vendor" + minifier="${vendor_dir}/bash-minifier.bash" + input_capture="${TEST_TMPDIR}/minifier-input.bash" + mkdir -p "$vendor_dir" + write_recording_minifier "$minifier" + + run env \ + BASH_MINIFIER_INPUT="$input_capture" \ + make -C "$REPO_ROOT" build \ + DIST_DIR="$dist_dir" \ + BASH_MINIFIER="$minifier" \ + VERSION=0.0.0-test \ + BUILD_COMMIT=test \ + BUILD_DATE=2026-08-18T00:00:00Z - run "${dist_dir}/bootstrap.bash" --help [ "$status" -eq 0 ] + + for artifact in \ + "${dist_dir}/bootstrap.dev.bash" \ + "${dist_dir}/bootstrap.bash" \ + "${dist_dir}/bootstrap.min.bash"; do + [ -x "$artifact" ] + checksum="${artifact}.256" + [ -f "$checksum" ] + verify_checksum "$checksum" + done + + grep -Fq '# Generated by make build. Do not edit directly.' \ + "${dist_dir}/bootstrap.dev.bash" + ! grep -Fq '# Generated by make build. Do not edit directly.' \ + "${dist_dir}/bootstrap.bash" + cmp "$input_capture" "${dist_dir}/bootstrap.bash" + + rm -rf "$vendor_dir" + for artifact in \ + "${dist_dir}/bootstrap.dev.bash" \ + "${dist_dir}/bootstrap.bash" \ + "${dist_dir}/bootstrap.min.bash"; do + run "$artifact" --help + [ "$status" -eq 0 ] + done } @test "deps reuses a valid bootstrap without network access" { @@ -225,8 +297,8 @@ EOF [[ "$output" == *'run make deps or make all'* ]] } -@test "all synchronizes dependencies before building" { - local fixture_root log_file digest +@test "all synchronizes dependencies before building every flavor" { + local fixture_root log_file digest artifact fixture_root="${TEST_TMPDIR}/all-ordering" log_file="${fixture_root}/bashdeps.log" @@ -241,10 +313,15 @@ set -euo pipefail printf '%s\n' "$*" >>"${BASHDEPS_TEST_LOG:?}" if [[ ${1:-} == sync ]]; then cat >vendor/generated.bash <<'SOURCE' +# generated source comment bootstrap_fixture_from_deps() { : } SOURCE + cat >vendor/bash-minifier.bash <<'MINIFIER' +#!/usr/bin/env bash +cat +MINIFIER fi EOF chmod 0755 "${fixture_root}/vendor/bashdeps.bash" @@ -261,7 +338,14 @@ EOF [ "$status" -eq 0 ] [ "$(head -n 1 "$log_file")" = "sync dependencies.txt" ] - [ -x "${fixture_root}/dist/bootstrap.bash" ] + + for artifact in \ + "${fixture_root}/dist/bootstrap.dev.bash" \ + "${fixture_root}/dist/bootstrap.bash" \ + "${fixture_root}/dist/bootstrap.min.bash"; do + [ -x "$artifact" ] + [ -f "${artifact}.256" ] + done grep -Fq 'bootstrap_fixture_from_deps' "${fixture_root}/dist/bootstrap.bash" } From 2d87c19ca5f44433050b78a88eeb4fb2db6e9290 Mon Sep 17 00:00:00 2001 From: Wes Dean Date: Tue, 18 Aug 2026 16:20:51 -0400 Subject: [PATCH 05/15] Make artifact metadata test representation independent --- tests/trivial.bats | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/trivial.bats b/tests/trivial.bats index e5920f9..06ef85c 100644 --- a/tests/trivial.bats +++ b/tests/trivial.bats @@ -21,9 +21,9 @@ setup() { } @test "generated bootstrap.bash contains release metadata" { - grep -q '^BOOTSTRAP_VERSION=' "$SCRIPT" - grep -q '^BOOTSTRAP_BUILD_DATE=' "$SCRIPT" - grep -q '^BOOTSTRAP_BUILD_COMMIT=' "$SCRIPT" + grep -q 'BOOTSTRAP_VERSION=' "$SCRIPT" + grep -q 'BOOTSTRAP_BUILD_DATE=' "$SCRIPT" + grep -q 'BOOTSTRAP_BUILD_COMMIT=' "$SCRIPT" } @test "generated bootstrap.bash executes successfully" { From dc54e0cdb33977b6b723009b80ef5593a2b3d6bd Mon Sep 17 00:00:00 2001 From: Wes Dean Date: Tue, 18 Aug 2026 16:21:18 -0400 Subject: [PATCH 06/15] Exercise all build artifact flavors in CI --- .github/workflows/test.yml | 46 +++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 252adfb..916a33d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,12 +28,15 @@ jobs: sudo apt-get update sudo apt-get install -y make bats curl doxygen shellcheck shfmt - - name: Verify build does not acquire dependencies + - name: Verify build does not acquire missing dependencies run: | test ! -e vendor - make build + if make build >build.stdout 2>build.stderr; then + printf '%s\n' 'make build unexpectedly succeeded without Bash-Minifier' >&2 + exit 1 + fi + grep -F 'Missing build dependency vendor/bash-minifier.bash' build.stderr test ! -e vendor - dist/bootstrap.bash --help >/dev/null - name: Synchronize and verify project dependencies run: | @@ -46,9 +49,29 @@ jobs: make all make deps-check + - name: Verify six build artifacts and checksums + run: | + for artifact in \ + bootstrap.dev.bash \ + bootstrap.bash \ + bootstrap.min.bash; do + test -x "dist/${artifact}" + test -f "dist/${artifact}.256" + (cd dist && sha256sum -c "${artifact}.256") + done + grep -F '# Generated by make build. Do not edit directly.' dist/bootstrap.dev.bash + if grep -F '# Generated by make build. Do not edit directly.' dist/bootstrap.bash; then + printf '%s\n' 'stripped artifact retained a generated full-line comment' >&2 + exit 1 + fi + if cmp -s dist/bootstrap.bash dist/bootstrap.min.bash; then + printf '%s\n' 'minified artifact is byte-identical to stripped artifact' >&2 + exit 1 + fi + - name: Verify dependency convergence and offline detection run: | - printf '%s\n' 'tampered dependency bytes' >vendor/doxygen-bash.awk + printf '%s\n' 'tampered dependency bytes' >vendor/bash-minifier.bash if make deps-check; then printf '%s\n' 'deps-check unexpectedly accepted tampered dependency bytes' >&2 exit 1 @@ -63,17 +86,12 @@ jobs: test -f doc/reference/index.html test -z "$(git status --short)" - - name: Verify consumer artifact is independent of vendor state - run: | - rm -rf vendor - dist/bootstrap.bash --help >/dev/null - - name: Run static checks run: make check id: shellcheck continue-on-error: true - - name: Run Bats tests + - name: Run Bats tests for every artifact flavor id: bats continue-on-error: true run: make test-report @@ -88,6 +106,14 @@ jobs: fail-on-error: false fail-on-empty: true + - name: Verify all consumer artifacts are independent of vendor state + if: steps.bats.outcome == 'success' + run: | + rm -rf vendor + dist/bootstrap.dev.bash --help >/dev/null + dist/bootstrap.bash --help >/dev/null + dist/bootstrap.min.bash --help >/dev/null + - name: Fail if Bats failed if: steps.bats.outcome == 'failure' run: exit 1 From 9518b42a1624a54ea5b4e900f4a9ef1c8ad28522 Mon Sep 17 00:00:00 2001 From: Wes Dean Date: Tue, 18 Aug 2026 16:21:29 -0400 Subject: [PATCH 07/15] Publish all build artifact flavors --- .github/workflows/semver.yml | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/.github/workflows/semver.yml b/.github/workflows/semver.yml index ee534fb..737a502 100644 --- a/.github/workflows/semver.yml +++ b/.github/workflows/semver.yml @@ -24,7 +24,7 @@ jobs: id-token: write steps: - name: Checkout code - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # pin@v4 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 fetch-tags: true @@ -54,27 +54,35 @@ jobs: make deps make deps-check - - name: Validate release inputs + - name: Validate release inputs and artifact flavors run: make check test - - name: Build release artifact - run: make checksums VERSION="${{ steps.calculate-version.outputs.version-string }}" + - name: Build release artifacts + run: | + make clean + make build VERSION="${{ steps.calculate-version.outputs.version-string }}" - - name: Verify release artifact is independent of build dependencies + - name: Verify release artifacts are independent of build dependencies run: | rm -rf vendor + dist/bootstrap.dev.bash --help >/dev/null dist/bootstrap.bash --help >/dev/null + dist/bootstrap.min.bash --help >/dev/null - name: Attest release artifacts uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # pin@v4.2.2 with: subject-path: | + dist/bootstrap.dev.bash dist/bootstrap.bash - dist/bootstrap.bash.sha256 + dist/bootstrap.min.bash + dist/bootstrap.dev.bash.256 + dist/bootstrap.bash.256 + dist/bootstrap.min.bash.256 - name: Create release id: create-release - uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # pin@v3.0.2 + uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2 with: token: "${{ secrets.GITHUB_TOKEN }}" name: "v${{ steps.calculate-version.outputs.version-string }}" @@ -83,5 +91,9 @@ jobs: draft: false prerelease: false files: | + dist/bootstrap.dev.bash dist/bootstrap.bash - dist/bootstrap.bash.sha256 + dist/bootstrap.min.bash + dist/bootstrap.dev.bash.256 + dist/bootstrap.bash.256 + dist/bootstrap.min.bash.256 From 44a1cc65d48c871ec71b2cec38dff8e7343f1259 Mon Sep 17 00:00:00 2001 From: Wes Dean Date: Tue, 18 Aug 2026 16:21:52 -0400 Subject: [PATCH 08/15] Document multi-flavor build contract for agents --- AGENTS.md | 60 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index f627831..7f088d0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -26,7 +26,8 @@ The ADR collection is the canonical source of architectural intent. Before making significant changes, review the relevant ADRs. Documentation work shall follow ADR-045. Build/development dependency work -shall preserve the boundaries established by ADR-051. +shall preserve the boundaries established by ADR-051. Generated artifact flavor, +minification, checksum, and release work shall preserve ADR-052. ## Clarify Before Acting @@ -64,8 +65,8 @@ A useful guiding principle is: - Preserve stable public interfaces. - Prefer explicit, deterministic, inspectable behavior. - Keep the core engine intentionally small. -- Keep build/development dependency tooling outside the released runtime - artifact. +- Keep build/development dependency tooling outside released runtime + artifacts. ## Technology Stack @@ -79,7 +80,9 @@ Runtime and project implementation: - Plain-text manifests Development/build orchestration also uses Make and the pinned released -`bashdeps.bash` bootstrap described by ADR-051. +`bashdeps.bash` bootstrap described by ADR-051. Bash-Minifier is a +manifest-managed build dependency used only to derive the minified release flavor +under ADR-052. ## Build and Dependency Boundaries @@ -90,7 +93,8 @@ Makefile before executing it. Ordinary externally acquired build/development artifacts are declared in `dependencies.txt` and synchronized by bashdeps under ADR-051. Do not add new one-off download rules to Make for dependencies that fit the released bashdeps -contract. +contract. Current manifest-managed artifacts include the Bash Doxygen filter and +the commit-pinned Bash-Minifier input at `vendor/bash-minifier.bash`. Preserve these target semantics: @@ -98,14 +102,33 @@ Preserve these target semantics: - `make deps-check` verifies existing dependency state without network access or repair. - `make build` does not bootstrap, synchronize, or verify external - dependencies. + dependencies, but it requires already-prepared Bash-Minifier state. +- A fresh checkout uses `make all` or `make deps` followed by `make build`. - `make all` explicitly synchronizes dependencies before invoking `build`. - `make docs` consumes already-prepared documentation dependency state and does not acquire it implicitly. +`make build` produces six distribution files: + +```text +dist/bootstrap.dev.bash +dist/bootstrap.bash +dist/bootstrap.min.bash +dist/bootstrap.dev.bash.256 +dist/bootstrap.bash.256 +dist/bootstrap.min.bash.256 +``` + +The development artifact retains assembled source comments. The ordinary +`bootstrap.bash` artifact removes full-line comments while preserving the +shebang. The minified artifact is derived from that stripped artifact using the +prepared Bash-Minifier dependency. All three executable artifacts represent the +same runtime program and shall remain executable. + `vendor/` and `doc/reference/` are generated state and are excluded from source -control. `dist/bootstrap.bash` must remain functional without bashdeps, -`dependencies.txt`, or the vendor tree. +control. All three `dist/bootstrap*.bash` consumer artifacts must remain +functional without bashdeps, Bash-Minifier, `dependencies.txt`, or the vendor +tree after construction. Treat `dependencies.txt` as data. Do not source or evaluate it as shell code. The committed digest, rather than a filename or URL label, is authoritative for @@ -172,9 +195,16 @@ When introducing new functionality: - update examples or documentation when behavior changes. Build/dependency changes should also exercise the applicable ADR-051 boundaries: -clean build behavior, explicit dependency synchronization, offline verification, -tamper detection, convergence, and runtime independence from generated vendor -state. +network-free build behavior, explicit dependency synchronization, offline +verification, tamper detection, convergence, and runtime independence from +generated vendor state. + +Artifact-generation changes shall apply the observable behavior suite to all +three executable flavors under ADR-052. Tests should avoid assuming that release +metadata or executable statements occupy the same physical lines in the minified +artifact. Build-specific tests should verify all six expected files, executable +permissions, checksum validity, transformation lineage, and runtime independence +from `vendor/`. A change is normally incomplete if the implementation changes but the corresponding tests do not. @@ -193,7 +223,8 @@ When practical: - review the resulting diff; - run formatting, linting, and tests; - run `make deps` and `make deps-check` when dependency state is relevant; -- verify generated consumer artifacts remain functional without `vendor/`; +- verify all generated consumer artifacts remain functional without `vendor/`; +- verify every `.256` file matches its corresponding executable; - verify documentation-only requests changed only documentation. ## Common Failure Modes @@ -206,7 +237,10 @@ Avoid: - inventing design rationale; - changing public behavior unintentionally; - reintroducing direct Makefile acquisition for manifest-managed dependencies; -- making `build`, `deps-check`, or `docs` silently repair dependency state. +- making `build`, `deps-check`, or `docs` silently repair dependency state; +- minifying maintained source files individually rather than the complete + assembled stripped artifact; +- treating the minified artifact as exempt from the ordinary behavior suite. ## Final Principle From 053917dbc273ed61bdcfdc27bd4242a3440201e2 Mon Sep 17 00:00:00 2001 From: Wes Dean Date: Tue, 18 Aug 2026 16:22:27 -0400 Subject: [PATCH 09/15] Document Bootstrap release artifact flavors --- README.md | 61 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 4d42ca5..064ade5 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,9 @@ replacement, but a wrapper. - Per-package installation timeouts when GNU `timeout` is available. - APT installations that omit automatically recommended packages. - Conservative diagnostics that stop rather than guessing when input is unclear. -- A single generated `bootstrap.bash` release artifact. +- Three generated standalone release flavors: development, stripped, and + minified. +- SHA-256 checksum companions for every executable release flavor. - Support for multiple system package managers: - APT: Ubuntu / Debian variants - DNF: RedHat variants @@ -41,9 +43,19 @@ replacement, but a wrapper. ## Install -The published release artifact is a standalone Bash script. +Bootstrap releases contain three executable representations of the same program: -To download the latest release with `curl`: +- `bootstrap.dev.bash` contains the fully assembled source with comments intact. +- `bootstrap.bash` is the ordinary/default artifact with full-line comments + removed. +- `bootstrap.min.bash` is derived from `bootstrap.bash` with the pinned + Bash-Minifier build dependency. + +All three files are standalone executable Bash scripts and are tested against the +same behavior suite. The conventional `bootstrap.bash` filename remains the +default for existing consumers and examples. + +To download the latest ordinary release artifact with `curl`: ```bash curl -fsSL \ @@ -52,6 +64,14 @@ curl -fsSL \ chmod +x bootstrap.bash ``` +Each executable also has a `.256` SHA-256 checksum companion in the release: + +```text +bootstrap.dev.bash.256 +bootstrap.bash.256 +bootstrap.min.bash.256 +``` + ### Optional shell function If you use Bootstrap frequently, you may prefer a shell function that keeps the @@ -207,8 +227,9 @@ external build/development files declared in `dependencies.txt`. The Makefile directly bootstraps only `vendor/bashdeps.bash`. It verifies the pinned release bytes against the SHA-256 digest committed in the Makefile before -execution. The current manifest-managed dependency is the Bash Doxygen filter -used to generate reference documentation. +execution. Current manifest-managed dependencies include the Bash Doxygen filter +used for reference documentation and a commit-pinned copy of Bash-Minifier used +to derive `bootstrap.min.bash`. Synchronize dependency state explicitly with: @@ -222,13 +243,16 @@ Verify already-present dependency state without network access or repair with: make deps-check ``` -Build the standalone consumer artifact without acquiring or verifying external -dependencies with: +Build all three executable flavors and their checksum companions with: ```bash make build ``` +`make build` remains network-free and does not repair dependency state. It does, +however, require an already-prepared `vendor/bash-minifier.bash`; a fresh checkout +therefore needs `make deps` before `make build`. + For a fresh-checkout convenience path that synchronizes dependencies and then builds, use: @@ -236,16 +260,27 @@ builds, use: make all ``` -`vendor/` is generated state and is not committed. The released -`dist/bootstrap.bash` artifact does not require bashdeps, `dependencies.txt`, or -the vendor tree at runtime. See ADR-051 and `doc/testing.md` for the detailed +A successful build creates: + +```text +dist/bootstrap.dev.bash +dist/bootstrap.bash +dist/bootstrap.min.bash +dist/bootstrap.dev.bash.256 +dist/bootstrap.bash.256 +dist/bootstrap.min.bash.256 +``` + +`vendor/` is generated state and is not committed. None of the three released +executables requires bashdeps, Bash-Minifier, `dependencies.txt`, or the vendor +tree at runtime. See ADR-051, ADR-052, and `doc/testing.md` for the detailed boundary and validation workflow. ## Running without installing -The Bootstrap tool is distributed as a single Bash shell script with no -external library dependencies. While the source for the tool is spread across -multiple files, the distributed tool is a single file. Therefore, +The Bootstrap tool is distributed as standalone Bash scripts with no external +library dependencies at runtime. While the source for the tool is spread across +multiple files, each release flavor is a single executable file. Therefore, "installation" in a traditional sense isn't a hard requirement. ### Running with Vet From 8f5d3108cc84f863bf7858930e924698c2e21958 Mon Sep 17 00:00:00 2001 From: Wes Dean Date: Tue, 18 Aug 2026 16:22:50 -0400 Subject: [PATCH 10/15] Document multi-flavor build and test workflow --- doc/testing.md | 121 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 86 insertions(+), 35 deletions(-) diff --git a/doc/testing.md b/doc/testing.md index f110a31..c64c116 100644 --- a/doc/testing.md +++ b/doc/testing.md @@ -9,12 +9,12 @@ builds, tests, and continuous integration behavior consistent. ----------------------------------------- -------------------- Synchronize build/development dependencies `make deps` Verify dependency state offline `make deps-check` - Build the consumer artifact `make build` + Build all release artifact flavors `make build` Synchronize dependencies, then build `make all` Check the source `make check` Format the source `make format` - Run the test suite `make test` - Generate the CI-style test report `make test-report` + Run the test suite against all flavors `make test` + Generate CI-style test reports `make test-report` Generate reference documentation `make docs` Run end-to-end tests `make test-e2e` @@ -33,8 +33,11 @@ This is the dependency-convergence target. It may use the network. Make directly owns only the bootstrap of `vendor/bashdeps.bash`; after verifying that pinned artifact, it runs bashdeps synchronization against the committed manifest. -The current manifest-managed dependency is the Bash Doxygen filter used for -reference documentation. +The manifest-managed dependencies include: + +- `vendor/doxygen-bash.awk`, used for reference documentation; and +- `vendor/bash-minifier.bash`, the commit-pinned Bash-Minifier artifact used to + derive the minified release flavor. To verify already-present dependency state without network access or repair, run: @@ -48,31 +51,58 @@ repaired. `vendor/` is generated state and is excluded from source control. -## Build the consumer artifact +## Build the release artifacts -Build the standalone Bootstrap executable with: +Build the three standalone Bootstrap executable flavors and their checksum +companions with: ``` bash make build ``` -`build` deliberately does not bootstrap, synchronize, or verify external -dependencies. Bootstrap's current consumer artifact is assembled entirely from -maintained repository source, so a clean `make build` succeeds without creating a -`vendor/` tree. +A successful build creates exactly these release files: + +```text +dist/bootstrap.dev.bash +dist/bootstrap.bash +dist/bootstrap.min.bash +dist/bootstrap.dev.bash.256 +dist/bootstrap.bash.256 +dist/bootstrap.min.bash.256 +``` + +The executable flavors have a deliberate lineage: -For the convenient fresh-checkout path that explicitly prepares dependencies -first, run: +1. `bootstrap.dev.bash` assembles the ordered maintained source with comments + intact. +2. `bootstrap.bash` removes full-line comments from that complete assembled + program while preserving the shebang. +3. `bootstrap.min.bash` passes the complete stripped artifact through the pinned + Bash-Minifier dependency. + +All three executable artifacts receive mode `0755`. Each `.256` file records the +SHA-256 digest of the corresponding executable. + +`make build` deliberately does not bootstrap, synchronize, or verify external +dependencies and therefore remains network-free. It now requires already-prepared +`vendor/bash-minifier.bash`. A fresh checkout should use either: ``` bash make all ``` +or: + +``` bash +make deps +make build +``` + `all` completes `make deps` before invoking `make build`, including under parallel Make execution. -The generated `dist/bootstrap.bash` artifact remains independent of bashdeps, -`dependencies.txt`, and `vendor/` at runtime. +After construction, none of the three executable release flavors depends on +bashdeps, Bash-Minifier, `dependencies.txt`, or `vendor/` at runtime. ## Check the source @@ -82,7 +112,9 @@ Run static checks with: make check ``` -This target runs the project's configured shell checks. +This target runs the project's configured shell checks against maintained source. +Generated release flavors are tested behaviorally rather than treated as +maintained source for formatting or ShellCheck purposes. ## Format the source @@ -92,8 +124,7 @@ Format shell source files with: make format ``` -Use this before opening a pull request when shell formatting changes are -needed. +Use this before opening a pull request when shell formatting changes are needed. ## Run the test suite @@ -103,26 +134,44 @@ Run the Bats test suite with: make test ``` -The behavior tests include regression coverage for the Make/bashdeps boundary, -including clean builds, safe bootstrap publication, offline verification, target -ordering, and generated-state cleanup. +The same complete behavior-oriented suite is applied to all three executable +flavors. Existing tests address `dist/bootstrap.bash` as the consumer path; the +Make test runner temporarily places each built flavor at that path and restores +the ordinary stripped artifact after each suite run. This keeps one behavior +contract while allowing the physical representation to differ. -## Generate a test report +Tests should not rely on minification-sensitive formatting such as one assignment +per physical line. Assertions should prefer executable behavior and stable public +content. -Run the test suite and generate the project test report with: +The behavior tests also include regression coverage for the Make/bashdeps +boundary, including network-free build failure when Bash-Minifier is absent, safe +bootstrap publication, offline verification, target ordering, all six expected +build outputs, checksum validity, transformation lineage, executable permissions, +and generated-state cleanup. + +## Generate test reports + +Run the suite against all three flavors and generate JUnit reports with: ``` bash make test-report ``` -The GitHub Action at `.github/workflows/test.yml` exercises dependency boundaries, -runs `make test-report`, and publishes the resulting Bats/JUnit results. This -workflow is a required check. +The target writes separate reports under `test-results/` for the development, +stripped, and minified flavors. The GitHub Action at +`.github/workflows/test.yml` publishes those reports together so a transformation +regression is visible in the same required test workflow. CI also exercises the released bashdeps integration directly: it synchronizes the -real committed manifest, verifies it offline, tampers with the managed Doxygen -filter to prove `deps-check` detects byte drift, and then uses an explicit -`make deps` to converge state again. +real committed manifest, verifies it offline, tampers with the managed +Bash-Minifier bytes to prove `deps-check` detects byte drift, and then uses an +explicit `make deps` to converge state again. + +CI verifies all six release files after a fresh `make all`, checks every `.256` +file, confirms the development artifact retains the generated comment header, +confirms the ordinary artifact does not, and confirms the minified artifact is +not byte-identical to the stripped artifact. ## Generate reference documentation @@ -163,10 +212,10 @@ Run all currently enabled end-to-end test environments with: make test-e2e ``` -The E2E targets require only the built consumer artifact and do not implicitly -synchronize development dependencies. Each enabled platform container receives -the generated Bootstrap script and uses Bootstrap inside the container to install -the tools used by the project test environment. +The E2E targets require built consumer artifacts and do not implicitly +synchronize development dependencies. The current E2E workflow uses the ordinary +`dist/bootstrap.bash` flavor inside platform containers; equivalence among all +three release flavors is enforced by the complete Bats suite described above. At present, the Ubuntu/APT, Alpine/APK, and RedHat-family/DNF environments are enabled: @@ -193,13 +242,15 @@ supported operating-system images using real package managers. The GitHub Action at `.github/workflows/e2e.yml` runs the enabled end-to-end environments as a matrix so each package-manager environment has an independent result. Its initial `make all` invocation also exercises the explicit -fresh-checkout dependency-convergence-and-build path. +fresh-checkout dependency-convergence-and-build path, including Bash-Minifier +preparation and all six build outputs. That release end-to-end workflow will also be a required check. ## Clean generated state -`make clean` removes the generated distribution artifact. +`make clean` removes the complete generated `dist/` tree, including all executable +flavors and checksum companions. `make distclean` additionally removes generated reference documentation, test results, and the entire generated `vendor/` dependency tree. A subsequent From 22b5e9ff5b84e2662fd80ee22c60b60e342031ad Mon Sep 17 00:00:00 2001 From: Wes Dean Date: Tue, 18 Aug 2026 16:23:02 -0400 Subject: [PATCH 11/15] Document verification for all release flavors --- doc/release-verification.md | 89 +++++++++++++++++++++++++++---------- 1 file changed, 65 insertions(+), 24 deletions(-) diff --git a/doc/release-verification.md b/doc/release-verification.md index 9b5531b..7b5abd4 100644 --- a/doc/release-verification.md +++ b/doc/release-verification.md @@ -1,18 +1,44 @@ # Release Verification -Bootstrap releases include a generated executable, a SHA-256 checksum, and a -GitHub build provenance attestation. These mechanisms answer related but -different questions about a downloaded release. +Bootstrap releases include three generated executable flavors, a SHA-256 checksum +for each executable, and GitHub build provenance attestations. These mechanisms +answer related but different questions about a downloaded release. -The checksum confirms that the downloaded file matches the digest published -with the release. The signed provenance attestation binds the artifact digest -to the GitHub Actions workflow that produced it in the -`wesley-dean/bootstrap` repository. +The checksum confirms that the downloaded file matches the digest published with +the release. The signed provenance attestation binds the artifact digest to the +GitHub Actions workflow that produced it in the `wesley-dean/bootstrap` +repository. Neither mechanism establishes that the source code is free from defects. Users should continue to inspect the release artifact before executing it, especially when Bootstrap will run with elevated privileges. +## Release artifact flavors + +Each release publishes these executable representations of the same Bootstrap +program: + +```text +bootstrap.dev.bash +bootstrap.bash +bootstrap.min.bash +``` + +`bootstrap.dev.bash` retains the fully assembled comments, +`bootstrap.bash` is the ordinary/default full-line-comment-stripped artifact, and +`bootstrap.min.bash` is derived from the stripped artifact using the pinned +Bash-Minifier build dependency. + +Each executable has a corresponding `.256` checksum file: + +```text +bootstrap.dev.bash.256 +bootstrap.bash.256 +bootstrap.min.bash.256 +``` + +All six files are included in release attestation scope. + ## Release build dependency preparation The release workflow explicitly prepares repository build/development dependency @@ -23,24 +49,27 @@ make deps make deps-check ``` -before constructing the release artifact. The Makefile independently verifies the -pinned released `vendor/bashdeps.bash` bootstrap, and bashdeps verifies the -ordinary external artifacts declared in `dependencies.txt`. +before constructing the release artifacts. The Makefile independently verifies +the pinned released `vendor/bashdeps.bash` bootstrap, and bashdeps verifies the +ordinary external artifacts declared in `dependencies.txt`, including the +commit-pinned Bash-Minifier input used by `make build`. This dependency relationship is build-time only. `make build` does not acquire or -verify external dependencies, and the published `bootstrap.bash` artifact remains -functional without bashdeps, `dependencies.txt`, or the generated `vendor/` tree. -The release workflow verifies that isolation before attestation and publication. +verify external dependencies. After construction, all three published executable +flavors remain functional without bashdeps, Bash-Minifier, `dependencies.txt`, or +the generated `vendor/` tree. The release workflow verifies that isolation before +attestation and publication. -See ADR-051 for the trust and target boundaries governing this process. +See ADR-051 for the dependency trust and target boundaries and ADR-052 for the +three-flavor release decision. -## Verify the SHA-256 checksum +## Verify a SHA-256 checksum -Download `bootstrap.bash` and `bootstrap.bash.sha256` from the same GitHub -release, place them in the same directory, and run: +Download the executable flavor you want and its matching `.256` file from the same +GitHub release. For the ordinary artifact: ```bash -sha256sum -c bootstrap.bash.sha256 +sha256sum -c bootstrap.bash.256 ``` A successful result reports: @@ -49,23 +78,35 @@ A successful result reports: bootstrap.bash: OK ``` +The same pattern applies to the development and minified flavors: + +```bash +sha256sum -c bootstrap.dev.bash.256 +sha256sum -c bootstrap.min.bash.256 +``` + ## Verify the GitHub build provenance attestation Install and authenticate the GitHub CLI, then verify the executable against the -canonical repository: +canonical repository. For the ordinary artifact: ```bash gh attestation verify bootstrap.bash \ --repo wesley-dean/bootstrap ``` -The release workflow also attests the checksum file. It may be verified -separately: +The development and minified executables may be verified the same way: ```bash -gh attestation verify bootstrap.bash.sha256 \ +gh attestation verify bootstrap.dev.bash \ + --repo wesley-dean/bootstrap + +gh attestation verify bootstrap.min.bash \ --repo wesley-dean/bootstrap ``` -Verification should fail closed. Do not execute an artifact when its checksum -or attestation does not verify successfully. +The release workflow also attests each `.256` checksum file, which may be verified +separately with the same `gh attestation verify` command. + +Verification should fail closed. Do not execute an artifact when its checksum or +attestation does not verify successfully. From 67bc00826fe73e77dd5611a633342c845fb33182 Mon Sep 17 00:00:00 2001 From: Wes Dean Date: Tue, 18 Aug 2026 16:23:15 -0400 Subject: [PATCH 12/15] Index multi-flavor artifact ADR --- doc/adr/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/adr/README.md b/doc/adr/README.md index 5b500e2..74b568a 100644 --- a/doc/adr/README.md +++ b/doc/adr/README.md @@ -73,7 +73,7 @@ Topics include: These ADRs describe how the project should evolve over time. -- ADR-031 through ADR-051 +- ADR-031 through ADR-052 Topics include: @@ -86,7 +86,8 @@ Topics include: - trusted computing base; - contributor philosophy; - bounded package installation and visible progress; -- build/development dependency acquisition and verification. +- build/development dependency acquisition and verification; +- development, stripped, and minified release artifact flavors. ## Architectural Themes From 2a0aee2ea4a22575eeb4f3caae67fccc271946d9 Mon Sep 17 00:00:00 2001 From: Wes Dean Date: Tue, 18 Aug 2026 16:25:22 -0400 Subject: [PATCH 13/15] Install Graphviz for documentation validation --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 916a33d..46e8397 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,7 +26,7 @@ jobs: - name: Install test dependencies run: | sudo apt-get update - sudo apt-get install -y make bats curl doxygen shellcheck shfmt + sudo apt-get install -y make bats curl doxygen graphviz shellcheck shfmt - name: Verify build does not acquire missing dependencies run: | From 54031f0f87cb95e19f7b9070c09d11223bec3fab Mon Sep 17 00:00:00 2001 From: Wes Dean Date: Tue, 18 Aug 2026 16:25:29 -0400 Subject: [PATCH 14/15] Install Graphviz for Pages documentation --- .github/workflows/static.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml index d907a38..9ed4167 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yml @@ -30,7 +30,7 @@ jobs: - name: Install documentation dependencies run: | sudo apt-get update - sudo apt-get install --yes curl doxygen + sudo apt-get install --yes curl doxygen graphviz - name: Synchronize and verify build dependencies run: | From d6464ce4de35f5b2c0d043d9f434af571f7fb697 Mon Sep 17 00:00:00 2001 From: Wes Dean Date: Tue, 18 Aug 2026 16:26:23 -0400 Subject: [PATCH 15/15] Keep build-boundary diagnostics outside checkout --- .github/workflows/test.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 46e8397..d9c60a4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -31,11 +31,12 @@ jobs: - name: Verify build does not acquire missing dependencies run: | test ! -e vendor - if make build >build.stdout 2>build.stderr; then + stderr="${RUNNER_TEMP}/bootstrap-build.stderr" + if make build >/dev/null 2>"${stderr}"; then printf '%s\n' 'make build unexpectedly succeeded without Bash-Minifier' >&2 exit 1 fi - grep -F 'Missing build dependency vendor/bash-minifier.bash' build.stderr + grep -F 'Missing build dependency vendor/bash-minifier.bash' "${stderr}" test ! -e vendor - name: Synchronize and verify project dependencies