From 6b22995ae4b0f22f2a80be3154a34a0a1dccd7c6 Mon Sep 17 00:00:00 2001 From: Ramesh Padmanabhaiah <22363102+codeforester@users.noreply.github.com> Date: Sat, 15 Aug 2026 07:07:17 +0530 Subject: [PATCH 1/8] release: unlock v2.0.0 GA after RC gates --- CHANGELOG.md | 4 +++- README.md | 16 +++++++--------- VERSION | 2 +- docs/versioning-policy.md | 14 ++++++-------- lib/bash/base-bash-libs.release | 2 +- scripts/release | 11 +++-------- tests/release.bats | 11 +++++++---- 7 files changed, 28 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cb5b85..0ae6282 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,9 @@ and versions are tracked in the repo-root `VERSION` file. ## [Unreleased] -## [2.0.0-rc.1] - 2026-08-15 +## [2.0.0] - 2026-08-15 + +The v2.0.0-rc.1 candidate was published and verified before this GA release. ### Added diff --git a/README.md b/README.md index 3d3160d..8097b4c 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,10 @@ | Version | License | Install | Release notes | | --- | --- | --- | --- | -| `2.0.0-rc.1` | [Apache-2.0](LICENSE) | `brew install basefoundry/base/base-bash-libs` | [v2.0.0-rc.1](https://github.com/basefoundry/base-bash-libs/releases/tag/v2.0.0-rc.1) | +| `2.0.0` | [Apache-2.0](LICENSE) | `brew install basefoundry/base/base-bash-libs` | [v2.0.0](https://github.com/basefoundry/base-bash-libs/releases/tag/v2.0.0) | -The v2.0.0-rc.1 row describes the release candidate under verification. Until -its canonical GA asset is published and the first-party cutover is complete, -v1.4.0 remains the current stable package. +The v2.0.0 row describes the current stable release. The v2.0.0-rc.1 +candidate was verified against the same release contract before GA publication. Reusable Bash standard library for reliable shell scripts. @@ -225,11 +224,10 @@ The repo-root `VERSION` file is the source of truth for the package version. The top strip in this README and the runtime `BASE_BASH_LIBS_VERSION` constant are validated against that file. -`v1.4.0` remains stable during the clean-break v2 development train. The sole -next stable target is `v2.0.0`; there will be no stable v1.5.0 or version reset -to 0.x. See the [versioning and release-line policy](docs/versioning-policy.md) -for prerelease identifiers, publication gates, the withdrawn July 2026 v2 -event, immutable consumption, and the post-GA support contract. +`v2.0.0` is the first stable release of the clean-break v2 line. There was no +stable v1.5.0 or version reset to 0.x. See the [versioning and release-line +policy](docs/versioning-policy.md) for prerelease identifiers, the withdrawn +July 2026 v2 event, immutable consumption, and the post-GA support contract. Pinned checkout, archive, Homebrew, vendored, and standalone consumption is documented in [`docs/pinned-consumption.md`](docs/pinned-consumption.md). diff --git a/VERSION b/VERSION index 97041a7..227cea2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.0.0-rc.1 +2.0.0 diff --git a/docs/versioning-policy.md b/docs/versioning-policy.md index d0ee7b2..64417c3 100644 --- a/docs/versioning-policy.md +++ b/docs/versioning-policy.md @@ -36,15 +36,13 @@ mandatory entry point for every release inspection and publication attempt. It enforces the permitted v2 identifiers before delegating read-only operations and dry runs to Base's guarded release command. -Prerelease publication is now available because #233 and the follow-up release -artifact contract have landed. Maintainers must still build and verify the +Prerelease publication became available because #233 and the follow-up +release-artifact contract landed. The reviewed `v2.0.0-rc.1` artifact and the +pre-GA work in #240 then validated and rehearsed the exact release candidate +across Base, Base Demo, Homebrew, vendored, and bundled paths. The guard now +permits `v2.0.0` GA publication; maintainers must still build and verify the canonical archive, checksum manifest, SBOM, and provenance from the reviewed -commit before publishing a prerelease. Real `v2.0.0` publication remains -locked until the engineering, policy, documentation, integration, and -reference-application gates in #214 are complete and the pre-GA work in #240 -has validated and rehearsed the exact release candidate across Base, Base Demo, -Homebrew, vendored, and bundled paths. The remaining #240 steps then publish -compatible Base and Homebrew updates after the Base Bash GA asset exists. +GA commit before publishing and completing the remaining first-party handoff. The GA lock is code-reviewed policy, not an environment-variable or sentinel-file override. The PR that satisfies each gate must update the guard diff --git a/lib/bash/base-bash-libs.release b/lib/bash/base-bash-libs.release index f44fbbb..f179dba 100644 --- a/lib/bash/base-bash-libs.release +++ b/lib/bash/base-bash-libs.release @@ -1,5 +1,5 @@ schema_version=1 -version=2.0.0-rc.1 +version=2.0.0 commit=unknown dirty_state=unknown provenance=release-artifact diff --git a/scripts/release b/scripts/release index 4fb6da8..bf58202 100755 --- a/scripts/release +++ b/scripts/release @@ -18,7 +18,9 @@ available after the verified release-artifact contract has landed; GA remains locked until the pre-GA release-candidate gates are complete. The refs command is a read-only preflight that fails closed when the candidate -tag already exists locally or on origin. +tag already exists locally or on origin. GA publication is available only +after the reviewed RC artifact, first-party consumer, and rollback gates are +complete. EOF } @@ -220,13 +222,6 @@ main() { if [[ "$command" == "publish" && "$dry_run" -eq 0 ]]; then release_check_tag_refs "$repo_root" "v$version" || return $? - if [[ "$version_kind" == "prerelease" ]]; then - : - else - release_error "Publishing v2.0.0 GA is locked until the #214 pre-GA gates and #240 RC rehearsal are complete." - release_error "Use 'publish --dry-run' to inspect the guarded plan without changing GitHub state." - return 1 - fi fi delegated_arguments=("$@") diff --git a/tests/release.bats b/tests/release.bats index 3eaba97..99f6e2f 100644 --- a/tests/release.bats +++ b/tests/release.bats @@ -130,12 +130,15 @@ assert_driver_not_called() { [ -e "$RELEASE_PUBLISH_MARKER" ] } -@test "release guard locks real v2 GA publication" { +@test "release guard delegates real v2 GA publication after reviewed RC gates" { bats_run "$RELEASE_SCRIPT" publish --version 2.0.0 --yes - [ "$status" -eq 1 ] - [[ "$output" == *"#240 RC rehearsal"* ]] - assert_driver_not_called + [ "$status" -eq 0 ] + grep -Fx 'arg=' "$RELEASE_CAPTURE" + grep -Fx 'arg=<--version>' "$RELEASE_CAPTURE" + grep -Fx 'arg=<2.0.0>' "$RELEASE_CAPTURE" + grep -Fx 'arg=<--yes>' "$RELEASE_CAPTURE" + [ -e "$RELEASE_PUBLISH_MARKER" ] } @test "release refs preflight accepts an unused candidate tag" { From 327aca1e0eb92d00fb9932d777171b8609a66192 Mon Sep 17 00:00:00 2001 From: Ramesh Padmanabhaiah <22363102+codeforester@users.noreply.github.com> Date: Sat, 15 Aug 2026 07:08:44 +0530 Subject: [PATCH 2/8] fix: keep GA release version validation shellcheck-clean --- scripts/release | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/release b/scripts/release index bf58202..006e5a9 100755 --- a/scripts/release +++ b/scripts/release @@ -84,7 +84,6 @@ main() { local command="${1-}" local manifest_value="" local version="" - local version_kind="" local dry_run=0 local index local release_driver="${BASE_BASH_RELEASE_BASECTL:-basectl}" @@ -209,7 +208,7 @@ main() { return 2 fi - version_kind="$(release_version_kind "$version")" || { + release_version_kind "$version" > /dev/null || { release_error "Version '$version' is outside the Base Bash v2.0.0 release line." release_error "Use 2.0.0-alpha.N, 2.0.0-beta.N, 2.0.0-rc.N, or 2.0.0." return 1 From c855d3ba9ad79fd785e57f0c2727bdeb3e265159 Mon Sep 17 00:00:00 2001 From: Ramesh Padmanabhaiah <22363102+codeforester@users.noreply.github.com> Date: Sat, 15 Aug 2026 07:10:41 +0530 Subject: [PATCH 3/8] docs: keep GA candidate marked planned until publication --- README.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 8097b4c..c470fc0 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,12 @@ | Version | License | Install | Release notes | | --- | --- | --- | --- | -| `2.0.0` | [Apache-2.0](LICENSE) | `brew install basefoundry/base/base-bash-libs` | [v2.0.0](https://github.com/basefoundry/base-bash-libs/releases/tag/v2.0.0) | +| `2.0.0` | [Apache-2.0](LICENSE) | `brew install basefoundry/base/base-bash-libs` | [v2.0.0 (planned)](https://github.com/basefoundry/base-bash-libs/issues/215) | -The v2.0.0 row describes the current stable release. The v2.0.0-rc.1 +The v2.0.0 row describes the planned next stable release. The v2.0.0-rc.1 candidate was verified against the same release contract before GA publication. +Until its canonical GA asset is published and the first-party cutover is +complete, v1.4.0 remains the current stable package. Reusable Bash standard library for reliable shell scripts. @@ -224,10 +226,11 @@ The repo-root `VERSION` file is the source of truth for the package version. The top strip in this README and the runtime `BASE_BASH_LIBS_VERSION` constant are validated against that file. -`v2.0.0` is the first stable release of the clean-break v2 line. There was no -stable v1.5.0 or version reset to 0.x. See the [versioning and release-line -policy](docs/versioning-policy.md) for prerelease identifiers, the withdrawn -July 2026 v2 event, immutable consumption, and the post-GA support contract. +`v1.4.0` remains stable during the clean-break v2 release preparation. The +sole next stable target is `v2.0.0`; there will be no stable v1.5.0 or version +reset to 0.x. See the [versioning and release-line policy](docs/versioning-policy.md) +for prerelease identifiers, the withdrawn July 2026 v2 event, immutable +consumption, and the post-GA support contract. Pinned checkout, archive, Homebrew, vendored, and standalone consumption is documented in [`docs/pinned-consumption.md`](docs/pinned-consumption.md). From bf42d715d4446d75a1bdc7e421a3aca7c860b72b Mon Sep 17 00:00:00 2001 From: Ramesh Padmanabhaiah <22363102+codeforester@users.noreply.github.com> Date: Sat, 15 Aug 2026 07:21:11 +0530 Subject: [PATCH 4/8] test: keep version ordering assertion valid for GA --- lib/bash/std/tests/lib_std.bats | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/bash/std/tests/lib_std.bats b/lib/bash/std/tests/lib_std.bats index af35fad..2e25206 100644 --- a/lib/bash/std/tests/lib_std.bats +++ b/lib/bash/std/tests/lib_std.bats @@ -588,7 +588,7 @@ EOF [[ "$output" == *"loaded version is $BASE_BASH_LIBS_VERSION"* ]] } -@test "base_require_version orders prereleases before the stable release" { +@test "base_require_version orders prereleases before a newer stable release" { local script="$TEST_TMPDIR/version-prerelease.sh" create_script "$script" < Date: Sat, 15 Aug 2026 07:28:39 +0530 Subject: [PATCH 5/8] test: allow GA release guard smoke delegation --- tests/bash-42-release-smoke.sh | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tests/bash-42-release-smoke.sh b/tests/bash-42-release-smoke.sh index 8078c04..91c507e 100755 --- a/tests/bash-42-release-smoke.sh +++ b/tests/bash-42-release-smoke.sh @@ -96,8 +96,17 @@ EOF release_smoke_expect_blocked "$capture_path" "$output_path" \ "$release_script" check --version 1.5.0 || return 1 - release_smoke_expect_blocked "$capture_path" "$output_path" \ - "$release_script" publish --version 2.0.0 --yes || return 1 + + rm -f -- "$capture_path" + "$release_script" publish --version 2.0.0 --yes >"$output_path" 2>&1 + if (($? != 0)); then + release_smoke_fail "GA release command was not delegated." + return 1 + fi + if [[ ! -e "$capture_path" ]]; then + release_smoke_fail "GA release command did not reach the delegated driver." + return 1 + fi release_smoke_expect_blocked "$capture_path" "$output_path" \ "$release_script" publish --version 2.0.0 --manifest --dry-run --yes || return 1 From 66ba29e7a72de03e3b70d16dec8a19dcd7aab131 Mon Sep 17 00:00:00 2001 From: Ramesh Padmanabhaiah <22363102+codeforester@users.noreply.github.com> Date: Sat, 15 Aug 2026 07:34:36 +0530 Subject: [PATCH 6/8] test: make release smoke shim portable --- tests/bash-42-release-smoke.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/bash-42-release-smoke.sh b/tests/bash-42-release-smoke.sh index 91c507e..a5aae4f 100755 --- a/tests/bash-42-release-smoke.sh +++ b/tests/bash-42-release-smoke.sh @@ -69,8 +69,8 @@ main() { capture_path="$release_smoke_dir/delegated.out" output_path="$release_smoke_dir/command.out" git_stub="$release_smoke_dir/git" - cat >"$git_stub" <<'EOF' -#!/usr/bin/env bash + printf '#!%s\n' "$BASH" >"$git_stub" + cat >>"$git_stub" <<'EOF' if [[ "$*" == *"show-ref --verify --quiet refs/tags/"* ]]; then exit 1 fi From 9884813707b31feb7d954dcb4ae974ee2a341219 Mon Sep 17 00:00:00 2001 From: Ramesh Padmanabhaiah <22363102+codeforester@users.noreply.github.com> Date: Sat, 15 Aug 2026 07:36:24 +0530 Subject: [PATCH 7/8] test: use exported git seam in release smoke --- tests/bash-42-release-smoke.sh | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/tests/bash-42-release-smoke.sh b/tests/bash-42-release-smoke.sh index a5aae4f..358ed6f 100755 --- a/tests/bash-42-release-smoke.sh +++ b/tests/bash-42-release-smoke.sh @@ -32,10 +32,20 @@ release_smoke_cleanup() { fi } +git() { + if [[ "$*" == *"show-ref --verify --quiet refs/tags/"* ]]; then + return 1 + fi + if [[ "$*" == *"ls-remote --tags origin refs/tags/"* ]]; then + return 0 + fi + printf 'Unexpected Git invocation in the Bash 4.2 release smoke: %s\n' "$*" >&2 + return 127 +} + main() { local expected_major="${1-}" expected_minor="${2-}" expected_patch="${3-}" local script_dir repo_root release_script release_driver capture_path output_path - local git_stub if (($# != 0 && $# != 3)); then release_smoke_fail "usage: $0 [expected-major expected-minor expected-patch]" @@ -68,21 +78,7 @@ main() { release_driver="$repo_root/tests/fixtures/basectl-release-stub" capture_path="$release_smoke_dir/delegated.out" output_path="$release_smoke_dir/command.out" - git_stub="$release_smoke_dir/git" - printf '#!%s\n' "$BASH" >"$git_stub" - cat >>"$git_stub" <<'EOF' -if [[ "$*" == *"show-ref --verify --quiet refs/tags/"* ]]; then - exit 1 -fi -if [[ "$*" == *"ls-remote --tags origin refs/tags/"* ]]; then - exit 0 -fi -printf 'Unexpected Git invocation in the Bash 4.2 release smoke: %s\n' "$*" >&2 -exit 127 -EOF - chmod +x "$git_stub" || return 1 - PATH="$release_smoke_dir:$PATH" - export PATH + export -f git export BASE_BASH_RELEASE_BASECTL="$release_driver" export BASE_BASH_RELEASE_TEST_CAPTURE="$capture_path" From f6ac4215713a29b43b74c60141e3e58069574902 Mon Sep 17 00:00:00 2001 From: Ramesh Padmanabhaiah <22363102+codeforester@users.noreply.github.com> Date: Sat, 15 Aug 2026 07:38:07 +0530 Subject: [PATCH 8/8] style: format release smoke script --- tests/bash-42-release-smoke.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/bash-42-release-smoke.sh b/tests/bash-42-release-smoke.sh index 358ed6f..a0c2e33 100755 --- a/tests/bash-42-release-smoke.sh +++ b/tests/bash-42-release-smoke.sh @@ -13,7 +13,7 @@ release_smoke_expect_blocked() { shift 2 rm -f -- "$capture_path" - "$@" >"$output_path" 2>&1 + "$@" > "$output_path" 2>&1 status=$? if ((status == 0)); then release_smoke_fail "blocked release command returned success." @@ -82,19 +82,19 @@ main() { export BASE_BASH_RELEASE_BASECTL="$release_driver" export BASE_BASH_RELEASE_TEST_CAPTURE="$capture_path" - if ! "$release_script" check --version 2.0.0-alpha.1 >"$output_path" 2>&1; then + if ! "$release_script" check --version 2.0.0-alpha.1 > "$output_path" 2>&1; then release_smoke_fail "a supported prerelease check was not delegated." return 1 fi - grep -Fx 'arg=' "$capture_path" >/dev/null || return 1 - grep -Fx 'arg=' "$capture_path" >/dev/null || return 1 - grep -Fx "arg=<$repo_root/base_manifest.yaml>" "$capture_path" >/dev/null || return 1 + grep -Fx 'arg=' "$capture_path" > /dev/null || return 1 + grep -Fx 'arg=' "$capture_path" > /dev/null || return 1 + grep -Fx "arg=<$repo_root/base_manifest.yaml>" "$capture_path" > /dev/null || return 1 release_smoke_expect_blocked "$capture_path" "$output_path" \ "$release_script" check --version 1.5.0 || return 1 rm -f -- "$capture_path" - "$release_script" publish --version 2.0.0 --yes >"$output_path" 2>&1 + "$release_script" publish --version 2.0.0 --yes > "$output_path" 2>&1 if (($? != 0)); then release_smoke_fail "GA release command was not delegated." return 1