Go workflow (mxschmitt/playwright-go)
The Go workflow from mxschmitt/playwright-go, explained and optimized by Latchkey.
CI health: B - good
Point runs-on at Latchkey and get job timeouts, SHA-pinned actions, self-healing for flaky steps, and up to 58% lower cost, applied automatically.
What it does
This is the Go workflow from the mxschmitt/playwright-go repository, a real project running GitHub Actions. It is shown here with attribution under its MIT license.
Below, Latchkey shows a faster, safer version produced by its optimization engine.
The workflow
name: Go
on:
push:
branches: [ main, roll/* ]
pull_request:
branches: [ main, roll/* ]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Set up Go 1.x
uses: actions/setup-go@v6
with:
go-version: oldstable
id: go
- name: golangci-lint
uses: golangci/golangci-lint-action@v9
with:
version: latest
test:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
browser: [chromium, firefox, webkit]
go: ['stable', 'oldstable']
runs-on: ${{ matrix.os }}
name: ${{ matrix.browser }} on ${{ matrix.os }}, go ${{ matrix.go }}
permissions:
# Needed for flakiness.io upload via GitHub OIDC (no access token required).
id-token: write
contents: read
steps:
- uses: actions/checkout@v7
with:
submodules: true
- name: Set up Go
uses: actions/setup-go@v6
with:
go-version: ${{ matrix.go }}
id: go
- name: Cache drivers
uses: actions/cache@v6
with:
# In order:
# * Driver for linux
# * Driver for macOS
# * Driver for windows
path: |
~/.cache/ms-playwright-go
~/.cache/ms-playwright
~/Library/Caches/ms-playwright-go
~/Library/Caches/ms-playwright
~\AppData\Local\ms-playwright-go
~\AppData\Local\ms-playwright
key: ${{ runner.os }}-go${{ matrix.go }}-${{ matrix.browser }}-${{ hashFiles('**/run.go') }}
- run: |
go install ./...
go install github.com/mxschmitt/flakiness-go/cmd/flakiness-go@latest
go install gotest.tools/gotestsum@latest
playwright install --with-deps ${{ matrix.browser }}
# We run the suite through gotestsum so that flaky tests (browser-side
# timing races that upstream microsoft/playwright absorbs with `retries: 3`)
# are re-run instead of failing the whole job. gotestsum re-runs ONLY the
# failed tests, exits 0 if each passes within the attempt budget, and
# writes every attempt to --jsonfile. We then pipe that json into
# `flakiness-go --stdin`, which records each attempt as a distinct
# RunAttempt - so a fail-then-pass is uploaded to flakiness.io as *flaky*
# (visible), not hidden. A test that fails every attempt still fails the
# job (gotestsum exits non-zero), and --rerun-fails-max-failures aborts the
# rerun if a broad breakage produces too many failures to be "flaky".
#
# Exit handling: `flakiness-go --stdin` always exits 0 (it is a reporter),
# so the pass/fail gate is gotestsum's exit code, captured below. We always
# run the reporter - even on a hard failure - so the failure is uploaded.
#
# Coverage caveat: when a rerun happens, `go test` overwrites covprofile
# with only the re-run test's coverage. Reruns are rare (only on a flake)
# and coverage upload is best-effort (continue-on-error, stable only), so we
# accept the occasional degraded number rather than running the suite twice.
- name: Test
shell: bash
env:
BROWSER: ${{ matrix.browser }}
FLAKINESS_PROJECT: playwright-community/playwright-go
FLAKINESS_NAME: ${{ matrix.browser }}
if: matrix.os == 'ubuntu-latest'
run: |
ec=0
xvfb-run gotestsum \
--format standard-verbose \
--rerun-fails=2 \
--rerun-fails-max-failures=15 \
--packages=./... \
--jsonfile=gotest-report.json \
-- -timeout=15m -coverprofile=covprofile -coverpkg="github.com/mxschmitt/playwright-go" --race || ec=$?
flakiness-go --stdin < gotest-report.json || true
exit $ec
- name: Test
shell: bash
env:
BROWSER: ${{ matrix.browser }}
FLAKINESS_PROJECT: playwright-community/playwright-go
FLAKINESS_NAME: ${{ matrix.browser }}
if: matrix.os != 'ubuntu-latest'
run: |
ec=0
gotestsum \
--format standard-verbose \
--rerun-fails=2 \
--rerun-fails-max-failures=15 \
--packages=./... \
--jsonfile=gotest-report.json \
-- -timeout=15m -coverprofile=covprofile -coverpkg="github.com/mxschmitt/playwright-go" --race || ec=$?
flakiness-go --stdin < gotest-report.json || true
exit $ec
- name: Send coverage
uses: shogo82148/actions-goveralls@v1
if: matrix.go == 'stable'
with:
path-to-profile: covprofile
flag-name: ${{ matrix.os }}-${{ matrix.browser }}
parallel: true
continue-on-error: true
finish:
if: ${{ always() }}
needs: test
runs-on: ubuntu-latest
steps:
- uses: shogo82148/actions-goveralls@v1
with:
parallel-finished: true
test-examples:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Set up Go 1.x
uses: actions/setup-go@v6
with:
go-version: oldstable
id: go
- run: |
go install ./...
playwright install --with-deps
- name: Run examples
run: |
for dir in examples/*; do
echo "::group::go run $dir/main.go"
xvfb-run -a go run $dir/main.go
echo "::endgroup::"
done
The same workflow, on Latchkey
Removes redundant runs and caps runaway jobs. Added and changed lines are highlighted.
name: Go on: push: branches: [ main, roll/* ] pull_request: branches: [ main, roll/* ] concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: lint: timeout-minutes: 30 name: Lint runs-on: latchkey-small steps: - uses: actions/checkout@v7 - name: Set up Go 1.x uses: actions/setup-go@v6 with: go-version: oldstable id: go - name: golangci-lint uses: golangci/golangci-lint-action@v9 with: version: latest test: timeout-minutes: 30 strategy: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] browser: [chromium, firefox, webkit] go: ['stable', 'oldstable'] runs-on: ${{ matrix.os }} name: ${{ matrix.browser }} on ${{ matrix.os }}, go ${{ matrix.go }} permissions: # Needed for flakiness.io upload via GitHub OIDC (no access token required). id-token: write contents: read steps: - uses: actions/checkout@v7 with: submodules: true - name: Set up Go uses: actions/setup-go@v6 with: go-version: ${{ matrix.go }} id: go - name: Cache drivers uses: actions/cache@v6 with: # In order: # * Driver for linux # * Driver for macOS # * Driver for windows path: | ~/.cache/ms-playwright-go ~/.cache/ms-playwright ~/Library/Caches/ms-playwright-go ~/Library/Caches/ms-playwright ~\AppData\Local\ms-playwright-go ~\AppData\Local\ms-playwright key: ${{ runner.os }}-go${{ matrix.go }}-${{ matrix.browser }}-${{ hashFiles('**/run.go') }} - run: | go install ./... go install github.com/mxschmitt/flakiness-go/cmd/flakiness-go@latest go install gotest.tools/gotestsum@latest playwright install --with-deps ${{ matrix.browser }} # We run the suite through gotestsum so that flaky tests (browser-side # timing races that upstream microsoft/playwright absorbs with `retries: 3`) # are re-run instead of failing the whole job. gotestsum re-runs ONLY the # failed tests, exits 0 if each passes within the attempt budget, and # writes every attempt to --jsonfile. We then pipe that json into # `flakiness-go --stdin`, which records each attempt as a distinct # RunAttempt - so a fail-then-pass is uploaded to flakiness.io as *flaky* # (visible), not hidden. A test that fails every attempt still fails the # job (gotestsum exits non-zero), and --rerun-fails-max-failures aborts the # rerun if a broad breakage produces too many failures to be "flaky". # # Exit handling: `flakiness-go --stdin` always exits 0 (it is a reporter), # so the pass/fail gate is gotestsum's exit code, captured below. We always # run the reporter - even on a hard failure - so the failure is uploaded. # # Coverage caveat: when a rerun happens, `go test` overwrites covprofile # with only the re-run test's coverage. Reruns are rare (only on a flake) # and coverage upload is best-effort (continue-on-error, stable only), so we # accept the occasional degraded number rather than running the suite twice. - name: Test shell: bash env: BROWSER: ${{ matrix.browser }} FLAKINESS_PROJECT: playwright-community/playwright-go FLAKINESS_NAME: ${{ matrix.browser }} if: matrix.os == 'ubuntu-latest' run: | ec=0 xvfb-run gotestsum \ --format standard-verbose \ --rerun-fails=2 \ --rerun-fails-max-failures=15 \ --packages=./... \ --jsonfile=gotest-report.json \ -- -timeout=15m -coverprofile=covprofile -coverpkg="github.com/mxschmitt/playwright-go" --race || ec=$? flakiness-go --stdin < gotest-report.json || true exit $ec - name: Test shell: bash env: BROWSER: ${{ matrix.browser }} FLAKINESS_PROJECT: playwright-community/playwright-go FLAKINESS_NAME: ${{ matrix.browser }} if: matrix.os != 'ubuntu-latest' run: | ec=0 gotestsum \ --format standard-verbose \ --rerun-fails=2 \ --rerun-fails-max-failures=15 \ --packages=./... \ --jsonfile=gotest-report.json \ -- -timeout=15m -coverprofile=covprofile -coverpkg="github.com/mxschmitt/playwright-go" --race || ec=$? flakiness-go --stdin < gotest-report.json || true exit $ec - name: Send coverage uses: shogo82148/actions-goveralls@v1 if: matrix.go == 'stable' with: path-to-profile: covprofile flag-name: ${{ matrix.os }}-${{ matrix.browser }} parallel: true continue-on-error: true finish: timeout-minutes: 30 if: ${{ always() }} needs: test runs-on: latchkey-small steps: - uses: shogo82148/actions-goveralls@v1 with: parallel-finished: true test-examples: timeout-minutes: 30 runs-on: latchkey-small steps: - uses: actions/checkout@v7 - name: Set up Go 1.x uses: actions/setup-go@v6 with: go-version: oldstable id: go - run: | go install ./... playwright install --with-deps - name: Run examples run: | for dir in examples/*; do echo "::group::go run $dir/main.go" xvfb-run -a go run $dir/main.go echo "::endgroup::" done
What changed
- Run on Latchkey managed runners with one line (
runs-on), which apply the fixes below automatically and self-heal transient failures. This example useslatchkey-small; pick the runner size that fits the job. - Add a job timeout so a hung step cannot burn hours of runner time.
2 third-party actions are referenced by a movable tag. Pin them to the commit SHA (Latchkey resolves and applies this automatically) so a repointed tag cannot change what runs.
What Latchkey heals here
This workflow has steps that commonly fail on transient issues (network, registries, flaky browsers). On Latchkey managed runners they are detected, retried, and self-healed instead of failing your build:
- End-to-end and browser tests
This workflow runs 4 jobs (21 with the matrix expanded) per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.