@@ -273,7 +273,56 @@ jobs:
273273 steps :
274274 - uses : actions/checkout@v4
275275
276+ # GDK-1702: this job billed ~5 min of setup+install per push while
277+ # the census measured most pushes touching no Go at all. It is a gate
278+ # (the cross-platform list is empty since 90e27bdf), so the skip must
279+ # never hide a Go change — hence the fail-open rules below. GitHub Actions has no
280+ # per-job paths filter (`on:` filters are workflow-level and would
281+ # skip every job), so the first step diffs the change and every step
282+ # below is gated on its output.
283+ #
284+ # It fails OPEN, and that is the contract: a forced push whose before
285+ # is unreachable, a first push (all-zero before), workflow_dispatch,
286+ # an empty event base, or any diff error runs the job. A gate skipped
287+ # by mistake is the failure mode that matters; a gate run for nothing
288+ # is five minutes.
289+ #
290+ # The path list is the Go surface plus the job's own inputs: the
291+ # script it runs, and this workflow (a change here can rewrite the
292+ # gate itself). `git diff A B` compares trees without needing shared
293+ # history, so two depth-1 objects are enough.
294+ - name : Did this change touch Go?
295+ id : gofilter
296+ env :
297+ EVENT_NAME : ${{ github.event_name }}
298+ BEFORE : ${{ github.event.before }}
299+ BASE_SHA : ${{ github.event.pull_request.base.sha }}
300+ SHA : ${{ github.sha }}
301+ run : |
302+ run_job() { echo "go=true" >> "$GITHUB_OUTPUT"; echo "staticcheck: $1"; exit 0; }
303+ skip_job() { echo "go=false" >> "$GITHUB_OUTPUT"; echo "staticcheck: $1"; exit 0; }
304+ case "$EVENT_NAME" in
305+ pull_request) base="$BASE_SHA" ;;
306+ push) base="$BEFORE" ;;
307+ *) run_job "event '$EVENT_NAME' has no before — running (fail open)" ;;
308+ esac
309+ if [ -z "$base" ] || printf '%s' "$base" | grep -Eq '^0+$'; then
310+ run_job "no usable base sha (first push or forced push) — running (fail open)"
311+ fi
312+ if ! git fetch --no-tags --depth=1 origin "$base" 2>/dev/null; then
313+ run_job "base $base not fetchable — running (fail open)"
314+ fi
315+ if ! changed="$(git diff --name-only "$base" "$SHA")"; then
316+ run_job "git diff failed — running (fail open)"
317+ fi
318+ n=$(printf '%s\n' "$changed" | grep -c . || true)
319+ if printf '%s\n' "$changed" | grep -Eq '\.go$|^go\.mod$|^go\.sum$|^desktop/|^tools/staticcheck\.sh$|^\.github/workflows/ci\.yml$'; then
320+ run_job "Go-touching change in ${n} changed path(s)"
321+ fi
322+ skip_job "no Go-touching change in ${n} changed path(s)"
323+
276324 - name : Set up Go
325+ if : steps.gofilter.outputs.go == 'true'
277326 uses : actions/setup-go@v5
278327 with :
279328 go-version-file : go.mod
@@ -283,11 +332,13 @@ jobs:
283332 # artefact is the script's classifier, not staticcheck. It runs over
284333 # fixtures, needs no toolchain, and takes a second.
285334 - name : staticcheck.sh self-test
335+ if : steps.gofilter.outputs.go == 'true'
286336 run : bash tools/staticcheck.sh --self-test
287337
288338 # Pinned on purpose: staticcheck gains checks between releases, and
289339 # @latest would turn a new check into a CI change nobody made.
290340 - name : Install staticcheck
341+ if : steps.gofilter.outputs.go == 'true'
291342 run : |
292343 go install honnef.co/go/tools/cmd/staticcheck@v0.7.0
293344 echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH"
@@ -301,6 +352,7 @@ jobs:
301352 # second copy of the mirror-flaky apt step in this file for a job that
302353 # cannot fail. The root module gets all three either way.
303354 - name : staticcheck over the GOOS matrix
355+ if : steps.gofilter.outputs.go == 'true'
304356 run : bash tools/staticcheck.sh
305357
306358 e2e :
@@ -312,6 +364,15 @@ jobs:
312364 # seeded home, so e2e/playwright.config.ts keeps workers: 1 and
313365 # fullyParallel: false — raising workers inside one runner is the
314366 # unsafe variant (single shared served instance) and stays out.
367+ #
368+ # GDK-1702: which files land on which shard is owned by
369+ # tools/e2e-partition.sh — dealt by the measured table
370+ # (e2e/shard-weights.tsv), because --shard's count-based split sent
371+ # 220/297/261 s of test time to the three runners and the longest
372+ # shard is the job's wall clock. --check runs in every shard before
373+ # the run: a spec that escaped the deal (new file outside the table,
374+ # stale weight row, glob drifting from what playwright collects)
375+ # fails the job that would otherwise have skipped it silently.
315376 strategy :
316377 fail-fast : false
317378 matrix :
@@ -478,8 +539,14 @@ jobs:
478539 echo "::error::playwright chromium download failed three times — browser cache, not apt, not the tests"
479540 exit 1
480541
542+ # Positional e2e/-prefixed paths on purpose: --shard=N/3 splits by
543+ # count (balanced only if every file costs the same), while the
544+ # partitioner deals files by measured seconds.
545+ - name : E2E partition check
546+ run : bash tools/e2e-partition.sh --check 3
547+
481548 - name : Run browser E2E
482- run : npx playwright test --config e2e/playwright.config.ts --shard= ${{ matrix.shard }}/3
549+ run : npx playwright test --config e2e/playwright.config.ts $(bash tools/e2e-partition.sh ${{ matrix.shard }} 3)
483550
484551 # Nested module (desktop/go.mod). package main imports wails v3, which does
485552 # not compile on Linux with CGO_ENABLED=0 (undefined pointer in the linux
0 commit comments