Skip to content

fix(scripts): restore executable bit on directly invoked scripts #194

fix(scripts): restore executable bit on directly invoked scripts

fix(scripts): restore executable bit on directly invoked scripts #194

Workflow file for this run

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