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..c470fc0 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,12 @@ | 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 (planned)](https://github.com/basefoundry/base-bash-libs/issues/215) | -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 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. @@ -225,11 +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. -`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. +`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). 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/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" < /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 @@ -220,13 +221,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/bash-42-release-smoke.sh b/tests/bash-42-release-smoke.sh index 8078c04..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." @@ -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,36 +78,31 @@ 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" - cat >"$git_stub" <<'EOF' -#!/usr/bin/env bash -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" - 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 - 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 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" {