Skip to content

Commit 5b7b435

Browse files
chore: cherry pick fixes for v0.12.0 and final release (#1366)
Signed-off-by: robert-cronin <robert.owen.cronin@gmail.com>
1 parent 3efb1aa commit 5b7b435

File tree

3 files changed

+213
-20
lines changed

3 files changed

+213
-20
lines changed

.github/workflows/release.yml

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -27,30 +27,27 @@ jobs:
2727
- name: Set Version Variables
2828
id: vars
2929
run: |
30-
# Remove the 'refs/tags/v' prefix.
31-
VERSION_TAG=$(echo "${{ github.ref }}" | sed 's,refs/tags/v,,')
32-
echo "version_tag=${VERSION_TAG}" >> "$GITHUB_OUTPUT"
33-
34-
- name: Validate Version Order
35-
run: |
36-
PUSHED_TAG="v${{ steps.vars.outputs.version_tag }}"
30+
# Get the version without the 'refs/tags/v' prefix, e.g., "1.2.3"
31+
VERSION_TAG_ONLY=$(echo "${{ github.ref }}" | sed 's,refs/tags/v,,')
3732
38-
# Get the immediate previous tag.
39-
PREVIOUS_TAG=$(git describe --tags --abbrev=0 "${PUSHED_TAG}^" 2>/dev/null || echo "v0.0.0")
33+
# Reconstruct the full pushed tag, e.g., "v1.2.3"
34+
PUSHED_TAG="v${VERSION_TAG_ONLY}"
4035
41-
echo "Validating pushed tag: ${PUSHED_TAG}"
42-
echo "Comparing against previous tag: ${PREVIOUS_TAG}"
43-
44-
# Create a list of the two tags and sort them. The pushed tag must be the last one.
45-
LATEST_SORTED=$(printf "%s\n%s" "$PUSHED_TAG" "$PREVIOUS_TAG" | sort -V | tail -n1)
36+
# Get the immediate previous tag relative to the pushed tag.
37+
PREVIOUS_TAG=$(git describe --tags --abbrev=0 "${PUSHED_TAG}^" 2>/dev/null || echo "v0.0.0")
4638
47-
if [ "$LATEST_SORTED" != "$PUSHED_TAG" ] || [ "$PUSHED_TAG" == "$PREVIOUS_TAG" ]; then
48-
echo "::error::Validation Failed: Pushed tag '${PUSHED_TAG}' is not strictly greater than the previous tag '${PREVIOUS_TAG}'."
49-
echo "::error::This could be a typo. Did you mean to release a different version?"
50-
exit 1
51-
fi
39+
# Output for other steps
40+
echo "version_tag=${VERSION_TAG_ONLY}" >> "$GITHUB_OUTPUT"
41+
42+
# Outputs for the validation step
43+
echo "pushed_tag=${PUSHED_TAG}" >> "$GITHUB_OUTPUT"
44+
echo "previous_tag=${PREVIOUS_TAG}" >> "$GITHUB_OUTPUT"
5245
53-
echo "✅ Version validation passed."
46+
- name: Validate Version Order
47+
run: bash .github/workflows/scripts/release/validate_version.sh
48+
env:
49+
PUSHED_TAG: ${{ steps.vars.outputs.pushed_tag }}
50+
PREVIOUS_TAG: ${{ steps.vars.outputs.previous_tag }}
5451

5552
- name: Create release branch
5653
run: |
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
#!/usr/bin/env bash
2+
#
3+
# test_validation.sh
4+
#
5+
# Unit test harness for validate_version.sh
6+
#
7+
# This script is "stateless" and does not require a git repository.
8+
# It tests the logic of validate_version.sh by passing
9+
# environment variables (PUSHED_TAG, PREVIOUS_TAG) and checking
10+
# the exit code.
11+
#
12+
13+
set -e # Exit on first error
14+
15+
# --- Robust Path Detection ---
16+
# Get the absolute directory where this script is located.
17+
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
18+
VALIDATE_SCRIPT="${SCRIPT_DIR}/validate_version.sh"
19+
20+
if [ ! -f "$VALIDATE_SCRIPT" ]; then
21+
echo "Error: Validation script not found at '$VALIDATE_SCRIPT'" >&2
22+
echo "Please ensure validate_version.sh is in the same directory as this test script." >&2
23+
exit 1
24+
fi
25+
26+
# --- Test Harness ---
27+
TEST_COUNT=0
28+
FAIL_COUNT=0
29+
30+
# Helper to run a test
31+
# $1: Expected exit code (0 for pass, 1 for fail)
32+
# $2: The "pushed" tag to test
33+
# $3: The "previous" tag to simulate
34+
# $4: Test description
35+
run_test() {
36+
local expected_code="$1"
37+
local pushed_tag="$2"
38+
local previous_tag="$3"
39+
local description="$4"
40+
local actual_code=0
41+
local output=""
42+
43+
TEST_COUNT=$((TEST_COUNT + 1))
44+
echo -n "Test: $description... "
45+
46+
# Run the validation script, capturing stdout/stderr and exit code
47+
# We pass the tags as environment variables, just like the workflow
48+
output=$(
49+
PUSHED_TAG="$pushed_tag" \
50+
PREVIOUS_TAG="$previous_tag" \
51+
"$VALIDATE_SCRIPT" 2>&1
52+
) || actual_code=$?
53+
54+
if [ "$actual_code" -eq "$expected_code" ]; then
55+
echo "PASS ✅"
56+
else
57+
FAIL_COUNT=$((FAIL_COUNT + 1))
58+
echo "FAIL ❌"
59+
echo " Expected exit code: $expected_code"
60+
echo " Got exit code: $actual_code"
61+
echo " PUSHED_TAG=$pushed_tag, PREVIOUS_TAG=$previous_tag"
62+
echo " Script output:"
63+
echo "$output" | sed 's/^/ /' # Indent output for readability
64+
fi
65+
}
66+
67+
# --- Main Execution ---
68+
69+
echo
70+
echo "--- Running Validation Logic Tests ---"
71+
72+
# --- PASSING Scenarios (Expected Exit 0) ---
73+
echo
74+
echo "Running PASS scenarios (expected exit 0)..."
75+
run_test 0 "v0.1.0" "v0.0.0" "First release"
76+
run_test 0 "v0.12.0" "v0.12.0-rc.3" "Special case: Final release (v0.12.0 > v0.12.0-rc.3)"
77+
run_test 0 "v0.12.0-rc.4" "v0.12.0-rc.3" "Subsequent RC (rc.4 > rc.3)"
78+
run_test 0 "v0.12.1" "v0.12.0" "Patch bump (v0.12.1 > v0.12.0)"
79+
run_test 0 "v0.13.0" "v0.12.9" "Minor bump (v0.13.0 > v0.12.9)"
80+
run_test 0 "v1.0.0" "v0.99.0" "Major bump (v1.0.0 > v0.99.0)"
81+
run_test 0 "v1.10.0" "v1.9.0" "Version sort check (1.10.0 > 1.9.0)"
82+
run_test 0 "v2.0.0" "v1.10.5" "Version sort check (2.0.0 > 1.10.5)"
83+
84+
# --- FAILING Scenarios (Expected Exit 1) ---
85+
echo
86+
echo "Running FAIL scenarios (expected exit 1)..."
87+
run_test 1 "v0.12.0-rc.3" "v0.12.0-rc.3" "Same tag (rc.3 == rc.3)"
88+
run_test 1 "v1.2.3" "v1.2.3" "Same tag (final == final)"
89+
run_test 1 "v0.12.0-rc.2" "v0.12.0-rc.3" "Lower RC (rc.2 < rc.3)"
90+
run_test 1 "v0.12.0-beta" "v0.12.0-rc.1" "Lower pre-release type (beta < rc)"
91+
run_test 1 "v0.11.0" "v0.12.0" "Lower minor version (v0.11.0 < v0.12.0)"
92+
run_test 1 "v1.2.0" "v1.3.0" "Lower minor version (v1.2.0 < v1.3.0)"
93+
run_test 1 "v0.12.0-rc.1" "v0.12.0" "Pre-release after final (rc.1 < final)"
94+
run_test 1 "v1.9.0" "v1.10.0" "Version sort check (1.9.0 < 1.10.0)"
95+
96+
# --- Final Report ---
97+
echo
98+
echo "--- Test Summary ---"
99+
if [ "$FAIL_COUNT" -eq 0 ]; then
100+
echo "All $TEST_COUNT tests passed! 🎉"
101+
exit 0
102+
else
103+
echo "$FAIL_COUNT out of $TEST_COUNT tests failed. ❌"
104+
exit 1
105+
fi
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
#!/usr/bin/env bash
2+
#
3+
# .github/workflows/scripts/release/validate_version.sh
4+
#
5+
# Checks if a new pushed tag is semantically greater than the
6+
# immediate previous tag using GNU sort -V, with a manual
7+
# override for SemVer pre-release vs. final release comparisons.
8+
#
9+
# This script is intended to be run from a GitHub Actions workflow.
10+
# It relies on the following environment variables being set:
11+
#
12+
# PUSHED_TAG: The new tag being validated (e.g., "v1.2.3")
13+
# PREVIOUS_TAG: The immediate previous tag (e.g., "v1.2.2" or "v0.0.0")
14+
#
15+
# Exits with 0 on success (validation passed).
16+
# Exits with 1 on failure (validation failed).
17+
#
18+
19+
# Exit on error, on unset variable, or pipe failure
20+
set -euo pipefail
21+
22+
# 1. Check that environment variables are set
23+
if [ -z "${PUSHED_TAG:-}" ]; then
24+
echo "::error::PUSHED_TAG environment variable is not set." >&2
25+
exit 1
26+
fi
27+
28+
if [ -z "${PREVIOUS_TAG:-}" ]; then
29+
echo "::error::PREVIOUS_TAG environment variable is not set." >&2
30+
exit 1
31+
fi
32+
33+
# 2. Log the inputs
34+
echo "Validating pushed tag: ${PUSHED_TAG}"
35+
echo "Comparing against previous tag: ${PREVIOUS_TAG}"
36+
37+
# 3. Check for identical tags (a definite failure)
38+
if [ "$PUSHED_TAG" == "$PREVIOUS_TAG" ]; then
39+
echo "::error::Validation Failed: Pushed tag '${PUSHED_TAG}' is identical to the previous tag '${PREVIOUS_TAG}'."
40+
exit 1
41+
fi
42+
43+
# 4. Extract base versions (before any '-' pre-release suffix)
44+
# Example: "v0.12.0-rc.1" -> "v0.12.0"
45+
# Example: "v0.12.0" -> "v0.12.0"
46+
PUSHED_BASE="${PUSHED_TAG%%-*}"
47+
PREVIOUS_BASE="${PREVIOUS_TAG%%-*}"
48+
49+
# 5. Handle the special SemVer case: pre-release vs. final
50+
# This is the case where 'sort -V' fails.
51+
if [ "$PUSHED_BASE" == "$PREVIOUS_BASE" ]; then
52+
53+
# CASE A: Pushed tag is a final release, previous was a pre-release.
54+
# This is GOOD. (e.g., v0.12.0 > v0.12.0-rc.3)
55+
# PUSHED_TAG == PUSHED_BASE (it has no '-')
56+
# PREVIOUS_TAG != PREVIOUS_BASE (it has a '-')
57+
if [ "$PUSHED_TAG" == "$PUSHED_BASE" ] && [ "$PREVIOUS_TAG" != "$PREVIOUS_BASE" ]; then
58+
echo "✅ Version validation passed (final release after pre-release)."
59+
exit 0
60+
fi
61+
62+
# CASE B: Pushed tag is a pre-release, previous was final.
63+
# This is BAD. (e.g., v0.12.0-rc.1 < v0.12.0)
64+
# PUSHED_TAG != PUSHED_BASE (it has a '-')
65+
# PREVIOUS_TAG == PREVIOUS_BASE (it has no '-')
66+
if [ "$PUSHED_TAG" != "$PUSHED_BASE" ] && [ "$PREVIOUS_TAG" == "$PREVIOUS_BASE" ]; then
67+
echo "::error::Validation Failed: Pushed tag '${PUSHED_TAG}' is a pre-release for a version that is already final ('${PREVIOUS_TAG}')."
68+
exit 1
69+
fi
70+
71+
# If both are pre-releases (rc.4 vs rc.3) or both are final (which
72+
# is caught by the identical check), we fall through to the
73+
# standard 'sort -V' logic, which handles rc.4 > rc.3 correctly.
74+
fi
75+
76+
# 6. For all other cases (different base versions), use standard version sort.
77+
# This correctly handles:
78+
# - v0.12.1 > v0.12.0
79+
# - v0.13.0 > v0.12.9
80+
# - v0.12.0-rc.4 > v0.12.0-rc.3
81+
LATEST_SORTED=$(printf "%s\n%s" "$PUSHED_TAG" "$PREVIOUS_TAG" | sort -V | tail -n1)
82+
83+
# 7. Check failure condition
84+
if [ "$LATEST_SORTED" != "$PUSHED_TAG" ]; then
85+
echo "::error::Validation Failed: Pushed tag '${PUSHED_TAG}' is not strictly greater than the previous tag '${PREVIOUS_TAG}'."
86+
echo "::error::This could be a typo. Did you mean to release a different version?"
87+
exit 1
88+
fi
89+
90+
echo "✅ Version validation passed."
91+
exit 0

0 commit comments

Comments
 (0)