Created
February 26, 2026 03:56
-
-
Save ipl31/61f4ba62d0bbd6eb773cafa40adc0509 to your computer and use it in GitHub Desktop.
docker-tag-test.sh
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # Unit tests for the Docker image tag resolution logic used in | |
| # .github/workflows/docker-release.yml | |
| # | |
| # Run: bash scripts/test-docker-tag-resolution.sh | |
| set -euo pipefail | |
| IMAGE="ghcr.io/openclaw/openclaw" | |
| passed=0 | |
| failed=0 | |
| # Arithmetic increment helpers — avoid ((var++)) which returns exit code 1 | |
| # when var transitions from 0, causing failures under set -e. | |
| pass() { passed=$((passed + 1)); } | |
| fail() { failed=$((failed + 1)); } | |
| # ---------- extract the tag-resolution logic under test ---------- | |
| # This function mirrors the "Resolve image tags" step in docker-release.yml. | |
| # ARCH is the architecture suffix (e.g. "amd64", "arm64") or empty for the | |
| # multi-platform manifest tags. | |
| resolve_tags() { | |
| local github_ref="$1" | |
| local arch="${2:-}" | |
| local suffix="" | |
| [[ -n "$arch" ]] && suffix="-${arch}" | |
| local -a tags=() | |
| if [[ "${github_ref}" == "refs/heads/main" ]]; then | |
| tags+=("${IMAGE}:main${suffix}") | |
| fi | |
| if [[ "${github_ref}" == refs/tags/v* ]]; then | |
| local version="${github_ref#refs/tags/v}" | |
| tags+=("${IMAGE}:${version}${suffix}") | |
| # Tag stable (non-prerelease) releases as latest | |
| # Use explicit suffix checks so patch releases like v2026.2.6-1 still get latest | |
| if [[ "$version" != *-beta* && "$version" != *-rc* && "$version" != *-alpha* ]]; then | |
| tags+=("${IMAGE}:latest${suffix}") | |
| fi | |
| fi | |
| # Return tags, one per line | |
| if [[ ${#tags[@]} -gt 0 ]]; then | |
| printf "%s\n" "${tags[@]}" | |
| fi | |
| } | |
| # ---------- tiny test harness ---------- | |
| # assert_tags LABEL GITHUB_REF ARCH EXPECTED_TAG... | |
| # ARCH can be "" for manifest tags. | |
| assert_tags() { | |
| local label="$1" | |
| local github_ref="$2" | |
| local arch="$3" | |
| shift 3 | |
| local -a expected=("$@") | |
| local actual | |
| actual=$(resolve_tags "$github_ref" "$arch") | |
| local expected_sorted actual_sorted | |
| expected_sorted=$(printf "%s\n" "${expected[@]}" | sort) | |
| actual_sorted=$(echo "$actual" | sort) | |
| if [[ "$expected_sorted" == "$actual_sorted" ]]; then | |
| echo " PASS: $label" | |
| pass | |
| else | |
| echo " FAIL: $label" | |
| echo " expected: $(printf "%s\n" "${expected[@]}")" | |
| echo " actual: $actual" | |
| fail | |
| fi | |
| } | |
| # assert_no_latest LABEL GITHUB_REF ARCH | |
| assert_no_latest() { | |
| local label="$1" | |
| local github_ref="$2" | |
| local arch="$3" | |
| local actual | |
| actual=$(resolve_tags "$github_ref" "$arch") | |
| if echo "$actual" | grep -q ":latest"; then | |
| echo " FAIL: $label (unexpected latest tag)" | |
| echo " actual: $actual" | |
| fail | |
| else | |
| echo " PASS: $label" | |
| pass | |
| fi | |
| } | |
| # assert_has_latest LABEL GITHUB_REF ARCH | |
| assert_has_latest() { | |
| local label="$1" | |
| local github_ref="$2" | |
| local arch="$3" | |
| local actual | |
| actual=$(resolve_tags "$github_ref" "$arch") | |
| local suffix="" | |
| [[ -n "$arch" ]] && suffix="-${arch}" | |
| if echo "$actual" | grep -q ":latest${suffix}$"; then | |
| echo " PASS: $label" | |
| pass | |
| else | |
| echo " FAIL: $label (missing latest${suffix} tag)" | |
| echo " actual: $actual" | |
| fail | |
| fi | |
| } | |
| # ---------- tests ---------- | |
| echo "=== Stable releases should get latest ===" | |
| assert_tags "stable release (manifest)" \ | |
| "refs/tags/v2026.2.25" "" \ | |
| "${IMAGE}:2026.2.25" "${IMAGE}:latest" | |
| assert_tags "stable release (amd64)" \ | |
| "refs/tags/v2026.2.25" "amd64" \ | |
| "${IMAGE}:2026.2.25-amd64" "${IMAGE}:latest-amd64" | |
| assert_tags "stable release (arm64)" \ | |
| "refs/tags/v2026.2.25" "arm64" \ | |
| "${IMAGE}:2026.2.25-arm64" "${IMAGE}:latest-arm64" | |
| echo "" | |
| echo "=== Patch/hotfix releases should get latest ===" | |
| assert_has_latest "patch release v2026.2.6-1 (manifest)" \ | |
| "refs/tags/v2026.2.6-1" "" | |
| assert_has_latest "patch release v2026.2.6-1 (amd64)" \ | |
| "refs/tags/v2026.2.6-1" "amd64" | |
| assert_has_latest "patch release v2026.2.6-2 (manifest)" \ | |
| "refs/tags/v2026.2.6-2" "" | |
| assert_has_latest "patch release v2026.2.6-3 (arm64)" \ | |
| "refs/tags/v2026.2.6-3" "arm64" | |
| echo "" | |
| echo "=== Pre-release tags should NOT get latest ===" | |
| assert_no_latest "beta release (manifest)" \ | |
| "refs/tags/v2026.2.25-beta.1" "" | |
| assert_no_latest "beta release (amd64)" \ | |
| "refs/tags/v2026.2.25-beta.1" "amd64" | |
| assert_no_latest "beta release (arm64)" \ | |
| "refs/tags/v2026.2.25-beta.1" "arm64" | |
| assert_no_latest "rc release (manifest)" \ | |
| "refs/tags/v2026.2.25-rc.1" "" | |
| assert_no_latest "rc release (amd64)" \ | |
| "refs/tags/v2026.2.25-rc.1" "amd64" | |
| assert_no_latest "alpha release (manifest)" \ | |
| "refs/tags/v2026.2.25-alpha.1" "" | |
| assert_no_latest "alpha release (amd64)" \ | |
| "refs/tags/v2026.2.25-alpha.1" "amd64" | |
| echo "" | |
| echo "=== Pre-releases still get versioned tags ===" | |
| assert_tags "beta has version tag only (manifest)" \ | |
| "refs/tags/v2026.2.25-beta.1" "" \ | |
| "${IMAGE}:2026.2.25-beta.1" | |
| assert_tags "rc has version tag only (amd64)" \ | |
| "refs/tags/v2026.2.25-rc.1" "amd64" \ | |
| "${IMAGE}:2026.2.25-rc.1-amd64" | |
| assert_tags "alpha has version tag only (arm64)" \ | |
| "refs/tags/v2026.2.25-alpha.1" "arm64" \ | |
| "${IMAGE}:2026.2.25-alpha.1-arm64" | |
| echo "" | |
| echo "=== Main branch builds ===" | |
| assert_tags "main branch (manifest)" \ | |
| "refs/heads/main" "" \ | |
| "${IMAGE}:main" | |
| assert_tags "main branch (amd64)" \ | |
| "refs/heads/main" "amd64" \ | |
| "${IMAGE}:main-amd64" | |
| assert_tags "main branch (arm64)" \ | |
| "refs/heads/main" "arm64" \ | |
| "${IMAGE}:main-arm64" | |
| echo "" | |
| echo "=== Edge cases ===" | |
| assert_has_latest "legacy patch v2026.2.6-1 gets latest" \ | |
| "refs/tags/v2026.2.6-1" "" | |
| assert_no_latest "beta.N style excluded" \ | |
| "refs/tags/v2026.2.25-beta.3" "" | |
| assert_no_latest "legacy beta dot style excluded" \ | |
| "refs/tags/v2026.2.25-beta1" "" | |
| assert_no_latest "main branch never gets latest" \ | |
| "refs/heads/main" "" | |
| echo "" | |
| echo "================================" | |
| echo "Results: ${passed} passed, ${failed} failed" | |
| echo "================================" | |
| [[ $failed -eq 0 ]] && exit 0 || exit 1 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment