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
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
19 changes: 10 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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).
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.0.0-rc.1
2.0.0
14 changes: 6 additions & 8 deletions docs/versioning-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion lib/bash/base-bash-libs.release
Original file line number Diff line number Diff line change
@@ -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
6 changes: 3 additions & 3 deletions lib/bash/std/tests/lib_std.bats
Original file line number Diff line number Diff line change
Expand Up @@ -588,23 +588,23 @@ 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" <<EOF
#!/usr/bin/env bash
source "$STDLIB_PATH"
base_require_version "2.0.0-rc.1"
base_require_version "2.0.0-alpha.1"
if base_require_version "2.0.0"; then
if base_require_version "2.0.1"; then
exit 3
fi
EOF

bats_run bash "$script"

[ "$status" -eq 0 ]
[[ "$output" == *"base-bash-libs 2.0.0 or newer is required"* ]]
[[ "$output" == *"base-bash-libs 2.0.1 or newer is required"* ]]
}

@test "base_require_version returns status 2 for invalid version strings" {
Expand Down
14 changes: 4 additions & 10 deletions scripts/release
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down Expand Up @@ -82,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}"
Expand Down Expand Up @@ -207,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
Expand All @@ -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=("$@")
Expand Down
51 changes: 28 additions & 23 deletions tests/bash-42-release-smoke.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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."
Expand All @@ -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]"
Expand Down Expand Up @@ -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=<release>' "$capture_path" >/dev/null || return 1
grep -Fx 'arg=<check>' "$capture_path" >/dev/null || return 1
grep -Fx "arg=<$repo_root/base_manifest.yaml>" "$capture_path" >/dev/null || return 1
grep -Fx 'arg=<release>' "$capture_path" > /dev/null || return 1
grep -Fx 'arg=<check>' "$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

Expand Down
11 changes: 7 additions & 4 deletions tests/release.bats
Original file line number Diff line number Diff line change
Expand Up @@ -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=<publish>' "$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" {
Expand Down
Loading