|
| 1 | +# ------------------------------------------------------------------------------------ |
| 2 | +# Setup Benchstat Composite Action (GoFortress) |
| 3 | +# |
| 4 | +# Purpose: Install and cache the benchstat binary for use in GitHub Actions workflows. |
| 5 | +# Provides efficient caching by OS and version, with automatic binary installation |
| 6 | +# on cache miss and PATH management for seamless integration. |
| 7 | +# |
| 8 | +# Features: |
| 9 | +# - Smart binary caching by OS and version |
| 10 | +# - Automatic go install only on cache miss |
| 11 | +# - PATH management for immediate availability |
| 12 | +# - Performance tracking outputs |
| 13 | +# |
| 14 | +# Usage: |
| 15 | +# - uses: ./.github/actions/setup-benchstat |
| 16 | +# with: |
| 17 | +# benchstat-version: ${{ env.MAGE_X_BENCHSTAT_VERSION }} |
| 18 | +# runner-os: ${{ runner.os }} |
| 19 | +# go-version: ${{ matrix.go-version }} |
| 20 | +# |
| 21 | +# Maintainer: @mrz1836 |
| 22 | +# |
| 23 | +# ------------------------------------------------------------------------------------ |
| 24 | + |
| 25 | +name: "Setup Benchstat" |
| 26 | +description: "Install and cache benchstat binary for benchmark comparison" |
| 27 | + |
| 28 | +inputs: |
| 29 | + benchstat-version: |
| 30 | + description: "Benchstat version to install (e.g., v0.6.0)" |
| 31 | + required: true |
| 32 | + runner-os: |
| 33 | + description: "Runner OS for cache key (e.g., ubuntu-latest, mac-latest)" |
| 34 | + required: true |
| 35 | + go-version: |
| 36 | + description: "Go version being used (e.g., 1.24.x, 1.22). Benchstat requires Go 1.23+" |
| 37 | + required: true |
| 38 | + |
| 39 | +outputs: |
| 40 | + cache-hit: |
| 41 | + description: "Whether benchstat was restored from cache (true/false). Empty if skipped due to Go version." |
| 42 | + value: ${{ steps.benchstat-cache.outputs.cache-hit }} |
| 43 | + installation-method: |
| 44 | + description: "How benchstat was obtained: cached, fresh, or skipped" |
| 45 | + value: ${{ steps.installation-summary.outputs.method }} |
| 46 | + skipped: |
| 47 | + description: "Whether benchstat installation was skipped due to Go version < 1.23" |
| 48 | + value: ${{ steps.version-check.outputs.skip }} |
| 49 | + |
| 50 | +runs: |
| 51 | + using: "composite" |
| 52 | + steps: |
| 53 | + # -------------------------------------------------------------------- |
| 54 | + # Check Go version compatibility (benchstat requires Go 1.23+) |
| 55 | + # -------------------------------------------------------------------- |
| 56 | + - name: 🔍 Check Go version compatibility |
| 57 | + id: version-check |
| 58 | + shell: bash |
| 59 | + run: | |
| 60 | + GO_VERSION="${{ inputs.go-version }}" |
| 61 | + # Extract major and minor version: |
| 62 | + # "1.24.x" -> MAJOR=1, MINOR=24 |
| 63 | + # "1.22" -> MAJOR=1, MINOR=22 |
| 64 | + # "1.23.5" -> MAJOR=1, MINOR=23 |
| 65 | + # "2.0.0" -> MAJOR=2, MINOR=0 |
| 66 | + MAJOR=$(echo "$GO_VERSION" | sed -E 's/^([0-9]+)\..*/\1/') |
| 67 | + MINOR=$(echo "$GO_VERSION" | sed -E 's/^[0-9]+\.([0-9]+).*/\1/') |
| 68 | +
|
| 69 | + if [ -z "$MAJOR" ] || [ -z "$MINOR" ] || ! [[ "$MAJOR" =~ ^[0-9]+$ ]] || ! [[ "$MINOR" =~ ^[0-9]+$ ]]; then |
| 70 | + echo "❌ Could not parse Go version '$GO_VERSION'. Please provide a valid Go version (e.g., '1.23', '1.24.x')." >&2 |
| 71 | + exit 1 |
| 72 | + elif [ "$MAJOR" -gt 1 ]; then |
| 73 | + # Any Go major version > 1 is considered compatible with the 1.23+ requirement |
| 74 | + echo "✅ Go $GO_VERSION >= 1.23: proceeding with benchstat installation" |
| 75 | + echo "skip=false" >> $GITHUB_OUTPUT |
| 76 | + elif [ "$MAJOR" -eq 1 ] && [ "$MINOR" -lt 23 ]; then |
| 77 | + echo "⚠️ Go $GO_VERSION < 1.23: skipping benchstat installation (requires Go 1.23+)" |
| 78 | + echo "skip=true" >> $GITHUB_OUTPUT |
| 79 | + else |
| 80 | + echo "✅ Go $GO_VERSION >= 1.23: proceeding with benchstat installation" |
| 81 | + echo "skip=false" >> $GITHUB_OUTPUT |
| 82 | + fi |
| 83 | +
|
| 84 | + # -------------------------------------------------------------------- |
| 85 | + # Restore benchstat binary cache |
| 86 | + # -------------------------------------------------------------------- |
| 87 | + - name: 💾 Restore benchstat binary cache |
| 88 | + if: steps.version-check.outputs.skip != 'true' |
| 89 | + id: benchstat-cache |
| 90 | + uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4 |
| 91 | + with: |
| 92 | + path: ~/.cache/benchstat-bin |
| 93 | + key: ${{ inputs.runner-os }}-benchstat-${{ inputs.benchstat-version }} |
| 94 | + |
| 95 | + # -------------------------------------------------------------------- |
| 96 | + # Install cached binary to PATH when cache hits |
| 97 | + # -------------------------------------------------------------------- |
| 98 | + - name: 📦 Install cached benchstat to PATH |
| 99 | + if: steps.version-check.outputs.skip != 'true' && steps.benchstat-cache.outputs.cache-hit == 'true' |
| 100 | + shell: bash |
| 101 | + run: | |
| 102 | + echo "📦 Installing cached benchstat binary to PATH..." |
| 103 | +
|
| 104 | + # Copy cached binary to GOPATH and add to PATH |
| 105 | + mkdir -p "$(go env GOPATH)/bin" |
| 106 | + cp ~/.cache/benchstat-bin/benchstat "$(go env GOPATH)/bin/benchstat" |
| 107 | + chmod +x "$(go env GOPATH)/bin/benchstat" |
| 108 | + echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH" |
| 109 | +
|
| 110 | + echo "✅ Cached benchstat binary installed to PATH" |
| 111 | +
|
| 112 | + # -------------------------------------------------------------------- |
| 113 | + # Install benchstat via go install when cache misses |
| 114 | + # -------------------------------------------------------------------- |
| 115 | + - name: ⬇️ Install benchstat (cache miss) |
| 116 | + if: steps.version-check.outputs.skip != 'true' && steps.benchstat-cache.outputs.cache-hit != 'true' |
| 117 | + shell: bash |
| 118 | + run: | |
| 119 | + echo "⬇️ Cache miss – installing benchstat via go install..." |
| 120 | + echo "📋 Installing benchstat version: ${{ inputs.benchstat-version }}" |
| 121 | +
|
| 122 | + # Install benchstat |
| 123 | + go install "golang.org/x/perf/cmd/benchstat@${{ inputs.benchstat-version }}" |
| 124 | +
|
| 125 | + # Cache the binary for future runs |
| 126 | + mkdir -p ~/.cache/benchstat-bin |
| 127 | + cp "$(go env GOPATH)/bin/benchstat" ~/.cache/benchstat-bin/benchstat |
| 128 | +
|
| 129 | + # Ensure GOPATH/bin is in PATH |
| 130 | + echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH" |
| 131 | +
|
| 132 | + echo "✅ Benchstat installed and cached" |
| 133 | +
|
| 134 | + # -------------------------------------------------------------------- |
| 135 | + # Verify benchstat installation and set outputs |
| 136 | + # -------------------------------------------------------------------- |
| 137 | + - name: 🔍 Verify benchstat installation |
| 138 | + id: installation-summary |
| 139 | + shell: bash |
| 140 | + run: | |
| 141 | + # Check if installation was skipped due to Go version |
| 142 | + if [[ "${{ steps.version-check.outputs.skip }}" == "true" ]]; then |
| 143 | + echo "⏭️ Benchstat installation skipped (Go version < 1.23)" |
| 144 | + echo "method=skipped" >> $GITHUB_OUTPUT |
| 145 | + echo "📋 Installation method: Skipped" |
| 146 | + exit 0 |
| 147 | + fi |
| 148 | +
|
| 149 | + echo "🔍 Verifying benchstat installation..." |
| 150 | +
|
| 151 | + # Test that benchstat is available and working |
| 152 | + if ! command -v benchstat >/dev/null 2>&1; then |
| 153 | + echo "❌ ERROR: benchstat is not available in PATH" >&2 |
| 154 | + exit 1 |
| 155 | + fi |
| 156 | +
|
| 157 | + # Show version |
| 158 | + echo "✅ benchstat is available" |
| 159 | + benchstat -h 2>&1 | head -3 || true |
| 160 | +
|
| 161 | + # Determine installation method |
| 162 | + if [[ "${{ steps.benchstat-cache.outputs.cache-hit }}" == "true" ]]; then |
| 163 | + echo "method=cached" >> $GITHUB_OUTPUT |
| 164 | + echo "📋 Installation method: Cached" |
| 165 | + else |
| 166 | + echo "method=fresh" >> $GITHUB_OUTPUT |
| 167 | + echo "📋 Installation method: Fresh install" |
| 168 | + fi |
0 commit comments