|
| 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