Skip to content

refactor(app): migrate App.vue to Pinia (47 commits — App 2211→72 code lines, + store tests/debt) #1282

refactor(app): migrate App.vue to Pinia (47 commits — App 2211→72 code lines, + store tests/debt)

refactor(app): migrate App.vue to Pinia (47 commits — App 2211→72 code lines, + store tests/debt) #1282

Workflow file for this run

# Browser E2E tests via Playwright. Separate from ci.yml because the
# job needs Playwright's browser deps (~300 MB chromium runtime) and
# is slower than the lint/unit tests — keeping it isolated means a
# fast CI iteration if you don't touch frontend/.
#
# Workflow ALWAYS triggers (no `paths:` filter at the workflow level)
# so the `playwright` context required by branch protection is always
# reported. A `Detect e2e-relevant changes` step short-circuits the
# heavy steps when nothing in frontend/, pkg/, *.go, or the OpenAPI
# spec changed — docs-only and release-please PRs go green in ~10s
# instead of leaving `playwright` stuck on "Expected — Waiting for
# status to be reported".
#
# Mirrors the local `task test-e2e` recipe:
# 1. build the frontend (Vite → frontend/dist)
# 2. build the serveronly binary (//go:embed pulls in frontend/dist)
# 3. install Playwright chromium with system deps
# 4. run `npx playwright test` (Playwright's webServer block boots
# the binary on :7099 with HOME=/tmp/recall-e2e for isolation)
#
# All ${{ }} expressions inside `run:` blocks go through env vars (the
# PR_BODY-safe pattern); SHAs from `github.event.pull_request.base.sha`
# / `github.event.before` / `github.sha` are validated to be 40-hex by
# GitHub, but env-var passing is the conservative default.
name: E2E
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
jobs:
playwright:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
# Full history so the path-detect step can diff against the
# PR base / push-event predecessor.
fetch-depth: 0
- name: Detect e2e-relevant changes
id: changes
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
BEFORE_SHA: ${{ github.event.before }}
HEAD_SHA: ${{ github.sha }}
run: |
set -eu
# PR events: diff against base.sha. Push events: diff
# against `before` (the previous tip of main). On a brand-
# new branch or initial push, both can be empty / all-zeros
# — fall back to HEAD~1 (or HEAD if even that's missing).
base=""
if [ -n "${BASE_SHA:-}" ]; then
base="$BASE_SHA"
elif [ -n "${BEFORE_SHA:-}" ] && [ "$BEFORE_SHA" != "0000000000000000000000000000000000000000" ]; then
base="$BEFORE_SHA"
else
base="$(git rev-parse "${HEAD_SHA}^" 2>/dev/null || echo "$HEAD_SHA")"
fi
changed="$(git diff --name-only "${base}..${HEAD_SHA}" || true)"
relevant=false
if printf '%s\n' "$changed" | grep -qE '^(frontend/|pkg/|api/openapi\.yaml$|\.github/workflows/e2e\.yml$)|\.go$'; then
relevant=true
fi
echo "relevant=$relevant" >> "$GITHUB_OUTPUT"
if [ "$relevant" = "false" ]; then
echo "::notice::No e2e-relevant changes (diff: ${base}..${HEAD_SHA}) — skipping Playwright, posting success."
fi
# go (build serveronly) + node (frontend build + Playwright) from
# mise.toml's pins; also loads [env] (GOTOOLCHAIN=local, …).
- uses: jdx/mise-action@dba19683ed58901619b14f395a24841710cb4925 # v4.1.0
if: steps.changes.outputs.relevant == 'true'
with:
install_args: go node
- name: Install Tesseract (the parser shells out to it)
if: steps.changes.outputs.relevant == 'true'
# golden-parse.spec.ts drives the real OCR → parser → store pipeline (the
# single biggest lever for Go integration coverage). The server
# auto-detects tesseract via exec.LookPath, so it just needs to be on
# PATH. No version-match gate here: the spec asserts loosely (a record was
# produced), tolerant of OCR drift; golden-fidelity is enforced in ci.yml.
run: |
sudo apt-get update -y
sudo apt-get install -y --no-install-recommends tesseract-ocr
- name: Install frontend deps
if: steps.changes.outputs.relevant == 'true'
working-directory: frontend
run: npm ci --no-audit --no-fund
- name: Build frontend (instrumented for e2e coverage)
if: steps.changes.outputs.relevant == 'true'
working-directory: frontend
# E2E_COVERAGE=1 emits inline source maps so monocart can remap the
# browser's V8 coverage back to .ts/.vue source. Behaviour-neutral —
# the suite still validates correctness exactly as before.
env:
E2E_COVERAGE: '1'
run: npm run build
- name: Build serveronly binary (coverage-instrumented)
if: steps.changes.outputs.relevant == 'true'
# `go build -cover` instruments the server so Playwright's HTTP traffic
# produces Go integration coverage; counters flush to GOCOVERDIR on the
# graceful SIGTERM shutdown Playwright's webServer sends on teardown.
run: |
mkdir -p /tmp/recall-e2e "${GITHUB_WORKSPACE}/coverage/e2e/go-covdata"
go build -cover -coverpkg=./... -tags serveronly -o /tmp/recall-e2e/recall-server .
- name: Install Playwright chromium + webkit (with system deps)
if: steps.changes.outputs.relevant == 'true'
working-directory: frontend
# webkit = the macOS Wails WKWebView engine; the `webkit` project
# (scoped to *-webkit.spec.ts) guards engine-specific regressions the
# chromium suite can't see. See playwright.config.ts.
run: npx playwright install --with-deps chromium webkit
- name: Run Playwright E2E suite
if: steps.changes.outputs.relevant == 'true'
working-directory: frontend
# E2E_COVERAGE + GOCOVERDIR turn on best-effort coverage collection
# (V8 frontend coverage via monocart + Go binary coverage). Collection
# is wrapped so it can never fail a test — the suite gates correctness,
# not coverage. GOCOVERDIR must be absolute (the server's cwd differs).
env:
E2E_COVERAGE: '1'
GOCOVERDIR: ${{ github.workspace }}/coverage/e2e/go-covdata
run: npx playwright test
# ── Integration coverage (informational; never gates merge) ──────────
# Go: covdata → coverprofile → func/cobertura. Frontend lcov/cobertura
# are produced by the Playwright globalTeardown (monocart). Both steps
# continue-on-error so a coverage hiccup never reds the e2e job.
- name: Build Go integration coverage report
if: ${{ always() && steps.changes.outputs.relevant == 'true' }}
continue-on-error: true
run: |
set -eu
mkdir -p coverage/e2e/go
go tool covdata textfmt -i="${GITHUB_WORKSPACE}/coverage/e2e/go-covdata" -o coverage/e2e/go/coverage.out
go tool cover -func=coverage/e2e/go/coverage.out > coverage/e2e/go/coverage.txt
go install github.com/boumenot/gocover-cobertura@latest
# GOFLAGS=-tags=serveronly so gocover-cobertura's internal `go list`
# sees the serveronly build-tag files (app_server.go, main_server.go)
# that the instrumented binary — and thus the profile — include.
GOFLAGS=-tags=serveronly gocover-cobertura < coverage/e2e/go/coverage.out > coverage/e2e/go/cobertura.xml
- name: Write integration-coverage job summary
if: ${{ always() && steps.changes.outputs.relevant == 'true' }}
continue-on-error: true
run: python3 scripts/ci/e2e-coverage-summary.py >> "$GITHUB_STEP_SUMMARY"
- name: Upload Go integration coverage
if: ${{ always() && steps.changes.outputs.relevant == 'true' }}
continue-on-error: true
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: go-e2e-coverage
path: coverage/e2e/go/
retention-days: 30
- name: Upload frontend integration coverage
if: ${{ always() && steps.changes.outputs.relevant == 'true' }}
continue-on-error: true
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: frontend-e2e-coverage
path: coverage/e2e/frontend/
retention-days: 30
- name: Upload Playwright report on failure
if: ${{ failure() && steps.changes.outputs.relevant == 'true' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: playwright-report
path: frontend/playwright-report/
retention-days: 7