Repository navigation
fix(scripts): restore executable bit on directly invoked scripts #194
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
| name: Benchmarks | |
| on: | |
| workflow_dispatch: | |
| schedule: | |
| - cron: '0 6 * * 1' # weekly Monday 6 AM UTC | |
| push: | |
| branches: [main] | |
| paths: | |
| - '**.go' | |
| - 'go.mod' | |
| - 'go.sum' | |
| permissions: | |
| contents: write # commit updated baselines on main dispatches | |
| jobs: | |
| bench: | |
| # One benchmark run per branch per platform — avoids artifact collisions. | |
| concurrency: | |
| group: bench-${{ github.ref }}-${{ matrix.os }} | |
| cancel-in-progress: true | |
| strategy: | |
| fail-fast: false # let all platforms finish even if one fails | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| time_threshold: 15 # stable runners, minimal binary-layout effects | |
| - os: macos-latest | |
| time_threshold: 40 # Apple Silicon VMs amplify Go binary-layout non-determinism | |
| runs-on: ${{ matrix.os }} | |
| env: | |
| CGO_ENABLED: "0" | |
| # Packages that have benchmark files. | |
| BENCH_PKGS: >- | |
| ./internal/git/ | |
| ./internal/ai/ | |
| ./internal/config/ | |
| ./internal/crashlog/ | |
| ./internal/panels/gitdiff/ | |
| ./internal/panels/filetree/ | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 0 # needed for git push on main | |
| - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 | |
| with: | |
| go-version: "1.26.5" | |
| - name: Install benchstat | |
| run: go install golang.org/x/perf/cmd/benchstat@v0.0.0-20260615155930-9e4b9ddef5b6 | |
| # Compute a stable platform key (e.g. linux-amd64, darwin-arm64). | |
| # Used for artifact names and baseline paths. | |
| - name: Set platform key | |
| run: | | |
| PLATFORM="$(go env GOOS)-$(go env GOARCH)" | |
| echo "PLATFORM=$PLATFORM" >> "$GITHUB_ENV" | |
| - name: Run benchmarks | |
| run: | | |
| go test -bench=. -benchmem -count=6 -run='^$' -timeout=15m \ | |
| ${{ env.BENCH_PKGS }} | tee bench-current.txt | |
| - name: Upload benchmark results | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: bench-${{ env.PLATFORM }}-${{ github.sha }} | |
| path: bench-current.txt | |
| retention-days: 90 | |
| # ── PR: run main benchmarks on the SAME RUNNER to eliminate cross-machine | |
| # variance. Apple Silicon GitHub Actions VMs show 30-50% variance across | |
| # different runner instances, making stored-baseline comparisons unreliable. | |
| # By benchmarking both main and PR code on the same machine we keep a | |
| # tight threshold and detect real regressions without false positives. | |
| - name: Run main benchmarks (same-runner baseline) | |
| if: github.event_name == 'pull_request' | |
| run: | | |
| BASE_SHA="${{ github.event.pull_request.base.sha }}" | |
| PR_REF="$(git rev-parse HEAD)" | |
| echo "Checking out base ($BASE_SHA) for same-runner comparison…" | |
| git checkout "$BASE_SHA" --quiet | |
| # Only benchmark packages that exist on the base branch — the PR may | |
| # introduce new packages that have no main-branch counterpart. | |
| VALID_PKGS="" | |
| for pkg in $BENCH_PKGS; do | |
| dir="${pkg#./}" | |
| dir="${dir%/}" | |
| if [ -d "$dir" ]; then | |
| VALID_PKGS="$VALID_PKGS $pkg" | |
| fi | |
| done | |
| if [ -z "$VALID_PKGS" ]; then | |
| echo "No benchmark packages found on base — skipping comparison." | |
| echo "no_baseline=true" >> "$GITHUB_ENV" | |
| else | |
| echo "Benchmarking on base: $VALID_PKGS" | |
| go test -bench=. -benchmem -count=6 -run='^$' -timeout=15m \ | |
| $VALID_PKGS | tee bench-baseline.txt | |
| fi | |
| # Return to PR code. | |
| git checkout "$PR_REF" --quiet | |
| - name: Compare vs same-runner baseline | |
| if: github.event_name == 'pull_request' && env.no_baseline != 'true' | |
| run: | | |
| benchstat bench-baseline.txt bench-current.txt > benchstat-diff.txt | |
| cat benchstat-diff.txt | |
| bash scripts/bench-regress.sh bench-baseline.txt bench-current.txt ${{ matrix.time_threshold }} 10 || { | |
| echo "regression=true" >> "$GITHUB_ENV" | |
| } | |
| - name: Build PR comment body | |
| if: github.event_name == 'pull_request' && env.no_baseline != 'true' | |
| run: | | |
| { | |
| echo "## Benchmarks: \`${{ env.PLATFORM }}\`" | |
| echo "" | |
| echo "<details>" | |
| echo "<summary>📊 Results vs <code>main</code> (same-runner) — click to expand</summary>" | |
| echo "" | |
| echo '```' | |
| cat benchstat-diff.txt | |
| echo '```' | |
| echo "" | |
| echo "</details>" | |
| echo "" | |
| if [ "${{ env.regression }}" = "true" ]; then | |
| echo "> [!CAUTION]" | |
| echo "> Benchmarks regressed by more than ${{ matrix.time_threshold }}% on \`${{ env.PLATFORM }}\`." | |
| else | |
| echo "> [!NOTE]" | |
| echo "> No significant regressions on \`${{ env.PLATFORM }}\` (threshold: ${{ matrix.time_threshold }}%, same-runner comparison)." | |
| fi | |
| } > bench-comment.md | |
| # Each platform gets its own sticky comment (keyed by platform). | |
| - name: Post PR comment | |
| if: github.event_name == 'pull_request' && env.no_baseline != 'true' | |
| uses: marocchino/sticky-pull-request-comment@5770ad5eb8f42dd2c4f34da00c94c5381e49af88 # v3.0.5 | |
| with: | |
| header: benchmarks-${{ env.PLATFORM }} | |
| path: bench-comment.md | |
| - name: Fail on significant regression | |
| if: github.event_name == 'pull_request' && env.regression == 'true' | |
| run: | | |
| echo "::error::[${{ env.PLATFORM }}] Benchmarks regressed >${{ matrix.time_threshold }}%. See the PR comment." | |
| exit 1 | |
| # -- main push: upload per-platform baseline for the update-baselines job -- | |
| - name: Upload baseline for commit | |
| if: github.ref == 'refs/heads/main' | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: baseline-${{ env.PLATFORM }} | |
| path: bench-current.txt | |
| retention-days: 7 | |
| # Collect baselines from all matrix legs and commit in a single push, | |
| # avoiding the git-push race that occurs when matrix jobs push concurrently. | |
| update-baselines: | |
| if: github.ref == 'refs/heads/main' | |
| needs: [bench] | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| fetch-depth: 0 | |
| - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| pattern: baseline-* | |
| merge-multiple: false | |
| - name: Commit updated baselines | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| for dir in baseline-*; do | |
| PLATFORM="${dir#baseline-}" | |
| mkdir -p "perf/baselines/$PLATFORM" | |
| cp "$dir/bench-current.txt" "perf/baselines/$PLATFORM/main.txt" | |
| done | |
| git add perf/baselines/ | |
| git diff --cached --quiet || \ | |
| git commit -m "perf: update baselines [skip ci]" | |
| # Retry push up to 3 times with rebase to handle concurrent main updates. | |
| pushed=false | |
| for attempt in 1 2 3; do | |
| if git pull --rebase origin main && git push; then | |
| pushed=true | |
| break | |
| fi | |
| echo "Push attempt $attempt failed, retrying..." | |
| sleep 5 | |
| done | |
| if [ "$pushed" = false ]; then | |
| echo "ERROR: failed to push baselines after 3 attempts" | |
| exit 1 | |
| fi |