diff --git a/.github/actions/prerelease-setup/README.md b/.github/actions/prerelease-setup/README.md index 81778282..e008cf85 100644 --- a/.github/actions/prerelease-setup/README.md +++ b/.github/actions/prerelease-setup/README.md @@ -22,7 +22,12 @@ The action performs, in order: upgrade target (`standalone-vcluster-upgrade-version`, `platform-rc-version`) resolves to the most recently published pre-release whose final release has not shipped yet, excluding `-next` - internal builds. An empty base + internal builds and anything below the previous stable minor line. A + pre-release that old belongs to a line that was never finished, and once + every current line has gone GA those are all that is left: a manual run + once resolved `0.18.2-alpha.0` that way and died on a missing asset. When + nothing qualifies the step fails and asks for the version explicitly. An + empty base (`standalone-vcluster-version`, `platform-base-version`) resolves to the highest stable release strictly below its target, so a maintenance release is tested against the release before it rather than against @@ -51,14 +56,14 @@ provisioning (`aws-test-infra`) and the Ginkgo test execution -| INPUT | TYPE | REQUIRED | DEFAULT | DESCRIPTION | -|-------------------------------------|--------|----------|---------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| github-token | string | true | | GitHub token with contents:read on loft-sh/loft-enterprise.
Required because the platform release resolvers
call the GitHub API for a
private repo; unauthenticated calls return 404.
Pass the calling job's github.token from
a job whose permissions grant contents:read. | -| platform-base-version | string | false | | Platform version for the initial install
(e.g. 4.9.0). Empty resolves to the highest
stable release of loft-sh/loft-enterprise below the
RC target. | -| platform-rc-version | string | false | | Platform RC version for upgrade (e.g. 4.10.0-alpha.6).
Empty resolves to the most recently
published platform pre-release whose final release
has not shipped, excluding -next builds. | -| role-session-name | string | true | | AWS STS role-session-name. Each consumer job
passes a distinct value (e.g. prerelease-vcluster-, prerelease-aicloud-). | -| standalone-vcluster-upgrade-version | string | false | | vCluster version to upgrade standalone to
(e.g. 0.35.0-alpha.7). Empty resolves to the most
recently published vCluster pre-release whose final
release has not shipped, excluding -next
builds. Must differ from the resolved
base version. | -| standalone-vcluster-version | string | false | | vCluster version to install for standalone
(e.g. 0.34.0). Empty resolves to the highest
stable release of loft-sh/vcluster below the
upgrade target. | +| INPUT | TYPE | REQUIRED | DEFAULT | DESCRIPTION | +|-------------------------------------|--------|----------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| github-token | string | true | | GitHub token with contents:read on loft-sh/loft-enterprise.
Required because the platform release resolvers
call the GitHub API for a
private repo; unauthenticated calls return 404.
Pass the calling job's github.token from
a job whose permissions grant contents:read. | +| platform-base-version | string | false | | Platform version for the initial install
(e.g. 4.9.0). Empty resolves to the highest
stable release of loft-sh/loft-enterprise below the
RC target. | +| platform-rc-version | string | false | | Platform RC version for upgrade (e.g. 4.10.0-alpha.6).
Empty resolves to the most recently
published platform pre-release whose final release
has not shipped, excluding -next builds
and anything below the previous stable
minor line; fails when nothing qualifies. | +| role-session-name | string | true | | AWS STS role-session-name. Each consumer job
passes a distinct value (e.g. prerelease-vcluster-, prerelease-aicloud-). | +| standalone-vcluster-upgrade-version | string | false | | vCluster version to upgrade standalone to
(e.g. 0.35.0-alpha.7). Empty resolves to the most
recently published vCluster pre-release whose final
release has not shipped, excluding -next
builds and anything below the previous
stable minor line; fails when nothing
qualifies. Must differ from the resolved
base version. | +| standalone-vcluster-version | string | false | | vCluster version to install for standalone
(e.g. 0.34.0). Empty resolves to the highest
stable release of loft-sh/vcluster below the
upgrade target. | diff --git a/.github/actions/prerelease-setup/action.yml b/.github/actions/prerelease-setup/action.yml index f585ca17..fa3088d0 100644 --- a/.github/actions/prerelease-setup/action.yml +++ b/.github/actions/prerelease-setup/action.yml @@ -13,7 +13,7 @@ inputs: required: false default: '' standalone-vcluster-upgrade-version: - description: 'vCluster version to upgrade standalone to (e.g. 0.35.0-alpha.7). Empty resolves to the most recently published vCluster pre-release whose final release has not shipped, excluding -next builds. Must differ from the resolved base version.' + description: 'vCluster version to upgrade standalone to (e.g. 0.35.0-alpha.7). Empty resolves to the most recently published vCluster pre-release whose final release has not shipped, excluding -next builds and anything below the previous stable minor line; fails when nothing qualifies. Must differ from the resolved base version.' required: false default: '' platform-base-version: @@ -21,7 +21,7 @@ inputs: required: false default: '' platform-rc-version: - description: 'Platform RC version for upgrade (e.g. 4.10.0-alpha.6). Empty resolves to the most recently published platform pre-release whose final release has not shipped, excluding -next builds.' + description: 'Platform RC version for upgrade (e.g. 4.10.0-alpha.6). Empty resolves to the most recently published platform pre-release whose final release has not shipped, excluding -next builds and anything below the previous stable minor line; fails when nothing qualifies.' required: false default: '' diff --git a/.github/actions/prerelease-setup/src/resolve-versions.sh b/.github/actions/prerelease-setup/src/resolve-versions.sh index dedfbe7c..b2afa4ec 100755 --- a/.github/actions/prerelease-setup/src/resolve-versions.sh +++ b/.github/actions/prerelease-setup/src/resolve-versions.sh @@ -1,8 +1,8 @@ #!/usr/bin/env bash # Resolves and validates the four version inputs for the pre-release checks. # An empty upgrade target becomes the most recently published pre-release whose -# final release has not shipped yet; an empty base becomes the highest release -# below that target. +# final release has not shipped yet, on or above the previous stable minor line; +# an empty base becomes the highest release below that target. # # Needs GNU coreutils: `sort -V` must rank '~' below everything, which BSD and # macOS sort do not. The runners are Ubuntu; `make test` on a Mac will disagree. @@ -85,16 +85,24 @@ stable_tags() { | sed 's/^v//' | { grep -E "$STABLE_RE" || true; } } +line_of() { local v="${1%%-*}"; printf '%s' "${v%.*}"; } + +previous_stable_line() { + stable_tags "$1" | while read -r v; do line_of "$v"; echo; done | sort -uV | tail -n2 | head -n1 +} + # The most recently published pre-release whose final release is not out yet. # /releases is not returned in any dependable order, so publish time is read # from the payload and sorted here rather than trusting the first row. newest_unreleased_pre_release() { - local repo="$1" released + local repo="$1" released floor released="$(stable_tags "$repo")" + floor="$(previous_stable_line "$repo")" local ranked first ranked="$(pre_release_tags "$repo" | while IFS=$'\t' read -r published version; do [ -n "$version" ] || continue if grep -qxF "${version%%-*}" <<<"$released"; then continue; fi + if [ -n "$floor" ] && is_below "$(line_of "$version")" "$floor"; then continue; fi printf '%s\t%s\t%s\n' "$published" "$(sort_key "$version")" "$version" done | sort -t "$(printf '\t')" -k1,1r -k2,2Vr)" [ -n "$ranked" ] || return 0 @@ -118,7 +126,7 @@ UP="${STANDALONE_VCLUSTER_UPGRADE_VERSION_INPUT:-}" UP="${UP#v}" if [ -z "$UP" ]; then UP="$(newest_unreleased_pre_release vcluster)" - [ -n "$UP" ] || { echo "no vCluster pre-release found in loft-sh/vcluster"; exit 1; } + [ -n "$UP" ] || { echo "no unreleased vCluster pre-release on or above the $(previous_stable_line vcluster) line in loft-sh/vcluster; pass standalone-vcluster-upgrade-version explicitly"; exit 1; } echo "Resolved newest unreleased vCluster pre-release: $UP" fi [[ "$UP" =~ $VERSION_RE ]] || { echo "invalid standalone-vcluster-upgrade-version: $UP"; exit 1; } @@ -141,7 +149,7 @@ RC="${PLATFORM_RC_VERSION_INPUT:-}" RC="${RC#v}" if [ -z "$RC" ]; then RC="$(newest_unreleased_pre_release loft-enterprise)" - [ -n "$RC" ] || { echo "no platform pre-release found in loft-sh/loft-enterprise"; exit 1; } + [ -n "$RC" ] || { echo "no unreleased platform pre-release on or above the $(previous_stable_line loft-enterprise) line in loft-sh/loft-enterprise; pass platform-rc-version explicitly"; exit 1; } echo "Resolved newest unreleased platform pre-release: $RC" fi [[ "$RC" =~ $VERSION_RE ]] || { echo "invalid platform-rc-version: $RC"; exit 1; } diff --git a/.github/actions/prerelease-setup/test/resolve-versions.bats b/.github/actions/prerelease-setup/test/resolve-versions.bats index dbf1d821..a7f82f98 100644 --- a/.github/actions/prerelease-setup/test/resolve-versions.bats +++ b/.github/actions/prerelease-setup/test/resolve-versions.bats @@ -359,3 +359,74 @@ out() { grep "^$1=" "$GITHUB_OUTPUT" | cut -d= -f2-; } [ "$status" -ne 0 ] [[ "$output" == *"no stable vCluster release below 0.35.3-rc.1"* ]] } + +# --- abandoned pre-releases -------------------------------------------------- + +@test "a pre-release from a line that was never finished is not a target" { + # run 35222301790: every 0.37 pre-release had shipped, so the resolver fell + # through to a 2023 alpha whose line was abandoned before install-standalone.sh existed + set_releases vcluster 1 "[ + $(release v0.37.1-rc.2 true 2026-09-10T10:00:00Z), + $(release v0.37.1 false 2026-09-15T10:00:00Z), + $(release v0.37.0 false 2026-09-01T10:00:00Z), + $(release v0.36.2 false 2026-08-20T10:00:00Z), + $(release v0.18.2-alpha.0 true 2023-12-28T13:30:54Z), + $(release v0.18.1 false 2023-12-01T10:00:00Z) + ]" + run "$SCRIPT" + [ "$status" -ne 0 ] + [[ "$output" != *"0.18.2-alpha.0"* ]] + [[ "$output" == *"pass standalone-vcluster-upgrade-version explicitly"* ]] +} + +@test "a maintenance pre-release on the previous line is still a target" { + set_releases vcluster 1 "[ + $(release v0.36.4-rc.1 true 2026-10-10T10:00:00Z), + $(release v0.37.3 false 2026-10-05T10:00:00Z), + $(release v0.36.3 false 2026-09-20T10:00:00Z), + $(release v0.18.2-alpha.0 true 2023-12-28T13:30:54Z) + ]" + run "$SCRIPT" + [ "$status" -eq 0 ] + [ "$(out standalone-vcluster-upgrade-version)" = "0.36.4-rc.1" ] + [ "$(out standalone-vcluster-version)" = "0.36.3" ] +} + +@test "a patch pre-release on the current line is a target" { + set_releases vcluster 1 "[ + $(release v0.37.4-rc.1 true 2026-10-16T13:11:47Z), + $(release v0.37.3 false 2026-10-05T10:00:00Z), + $(release v0.36.3 false 2026-09-20T10:00:00Z) + ]" + run "$SCRIPT" + [ "$status" -eq 0 ] + [ "$(out standalone-vcluster-upgrade-version)" = "0.37.4-rc.1" ] + [ "$(out standalone-vcluster-version)" = "0.37.3" ] +} + +@test "a pre-release of the next major is a target" { + export STANDALONE_VCLUSTER_UPGRADE_VERSION_INPUT="0.36.1-rc.3" + set_releases loft-enterprise 1 "[ + $(release v5.0.0-alpha.1 true 2026-10-08T20:41:44Z), + $(release v4.12.3 false 2026-10-01T10:00:00Z), + $(release v4.11.3 false 2026-09-01T10:00:00Z), + $(release v2.3.1-alpha.0 true 2022-01-01T10:00:00Z) + ]" + run "$SCRIPT" + [ "$status" -eq 0 ] + [ "$(out platform-rc-version)" = "5.0.0-alpha.1" ] + [ "$(out platform-base-version)" = "4.12.3" ] +} + +@test "the platform target is rejected the same way" { + export STANDALONE_VCLUSTER_UPGRADE_VERSION_INPUT="0.36.1-rc.3" + set_releases loft-enterprise 1 "[ + $(release v4.12.3 false 2026-10-01T10:00:00Z), + $(release v4.11.3 false 2026-09-01T10:00:00Z), + $(release v2.3.1-alpha.0 true 2022-01-01T10:00:00Z) + ]" + run "$SCRIPT" + [ "$status" -ne 0 ] + [[ "$output" != *"2.3.1-alpha.0"* ]] + [[ "$output" == *"pass platform-rc-version explicitly"* ]] +}