feat: SSH-Host Fallback + Open-Section Editor Logos #969
Workflow file for this run
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: CI | |
| on: | |
| pull_request: | |
| push: | |
| branches: [main] | |
| # Cancel superseded runs on the same ref (e.g. rapid PR pushes), but never | |
| # cancel main — every merged commit gets a full run. | |
| concurrency: | |
| group: ci-${{ github.ref }} | |
| cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} | |
| permissions: | |
| contents: read | |
| jobs: | |
| backend: | |
| name: Backend (go test) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 | |
| with: | |
| go-version-file: app/backend/go.mod | |
| - uses: extractions/setup-just@e33e0265a09d6d736e2ee1e0eb685ef1de4669ff # v3 | |
| # internal/sessions integration tests shell out to fab + tmux. fab is not | |
| # available on the runner, so those cases t.Skip — the rest still run. | |
| - name: Install tmux | |
| run: sudo apt-get update && sudo apt-get install -y tmux | |
| - name: Run backend tests | |
| run: just test-backend | |
| frontend: | |
| name: Frontend (vitest + tsc) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: 20 | |
| - uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4 | |
| with: | |
| version: 9 | |
| - uses: extractions/setup-just@e33e0265a09d6d736e2ee1e0eb685ef1de4669ff # v3 | |
| - name: Install frontend dependencies | |
| run: cd app/frontend && pnpm install --frozen-lockfile | |
| - name: Type-check | |
| run: cd app/frontend && npx tsc --noEmit | |
| - name: Run frontend unit tests | |
| run: just test-frontend | |
| e2e: | |
| name: E2E (shard ${{ matrix.shard }}/3) | |
| runs-on: ubuntu-latest | |
| # Blocking gate (since 260602-a1wo). The e2e suite is SSE-driven and shares | |
| # one dev + tmux server per shard; under a 2-vCPU runner's contention a | |
| # rotating subset of tests can lose the timing race (the flaky set is | |
| # non-deterministic). Readiness gates (see tests/e2e/_ready.ts) reduce this, | |
| # and proper de-flaking (deterministic polls / optimistic-aware waits) is a | |
| # tracked follow-up. E2E is now a required dependency of the `ci-gate` job, | |
| # so a failing/flaky shard blocks merge — admin bypass on the main ruleset | |
| # is the intended escape hatch for known-flake overrides. | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| shard: [1, 2, 3] | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 | |
| with: | |
| go-version-file: app/backend/go.mod | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 | |
| with: | |
| node-version: 20 | |
| - uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4 | |
| with: | |
| version: 9 | |
| - uses: extractions/setup-just@e33e0265a09d6d736e2ee1e0eb685ef1de4669ff # v3 | |
| # The e2e harness runs against a live dev server: tmux backs the sessions, | |
| # air gives the backend live-reload, Vite serves the frontend. Each shard | |
| # is its own runner with its own /tmp/tmux-* socket dir, so the shared | |
| # rk-test-e2e server is naturally isolated between shards — that is what | |
| # makes sharding (rather than in-process workers) the safe parallelism. | |
| - name: Install tmux | |
| run: sudo apt-get update && sudo apt-get install -y tmux | |
| - name: Install air (backend live-reload used by `just dev`) | |
| run: | | |
| # Pin to the last air release that targets go 1.23 (the repo's | |
| # toolchain). @latest pulls a build requiring go >= 1.25, which | |
| # silently triggers a toolchain download and drifts from go.mod. | |
| go install github.com/air-verse/air@v1.61.7 | |
| # Ensure the go install bin dir is on PATH for `just dev` (dev.sh | |
| # hard-fails if `air` is missing). setup-go usually adds this, but | |
| # make it explicit so the e2e job never silently can't find air. | |
| echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH" | |
| - name: Set up project (deps, playwright chromium, .env.local) | |
| run: just setup | |
| - name: Run e2e tests (shard ${{ matrix.shard }}/3) | |
| run: just test-e2e --shard=${{ matrix.shard }}/3 | |
| - name: Upload Playwright report on failure | |
| if: failure() | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 | |
| with: | |
| name: playwright-report-shard-${{ matrix.shard }} | |
| path: | | |
| app/frontend/playwright-report/ | |
| app/frontend/test-results/ | |
| retention-days: 7 | |
| # Single stable-named aggregate gate for branch-protection / rulesets to | |
| # require. The individual jobs above have names that are fine to require | |
| # directly EXCEPT the e2e matrix, whose names (`E2E (shard N/3)`) are coupled | |
| # to the shard count — requiring those by name would silently break the gate | |
| # if the matrix ever changes. Requiring this one job instead decouples the | |
| # ruleset from the job topology: changing the shard count needs no ruleset | |
| # edit, because the matrix's aggregate result still surfaces as `needs.e2e`. | |
| # | |
| # Hard gates = backend + frontend + e2e (all three required). The e2e job had | |
| # its `continue-on-error: true` removed so a failing shard actually fails the | |
| # job (and thus this gate) — given e2e's known CI flakiness, admin bypass on | |
| # the main ruleset is the intended escape hatch for overriding a known-flake. | |
| # | |
| # `if: always()` is REQUIRED: without it this job is *skipped* when a | |
| # dependency fails, and GitHub treats a skipped required check as passing — | |
| # which would defeat the gate. With `always()` the job runs regardless, and | |
| # the explicit result checks below fail it unless every dependency succeeded. | |
| # For the e2e matrix, `needs.e2e.result` is the AGGREGATE: it is 'success' | |
| # only when every shard succeeded, 'failure' if any shard failed — so one | |
| # check covers all shards. | |
| ci-gate: | |
| name: CI gate | |
| runs-on: ubuntu-latest | |
| needs: [backend, frontend, e2e] | |
| if: ${{ always() }} | |
| steps: | |
| - name: Verify required jobs succeeded | |
| run: | | |
| echo "backend = ${{ needs.backend.result }}" | |
| echo "frontend = ${{ needs.frontend.result }}" | |
| echo "e2e = ${{ needs.e2e.result }}" | |
| if [ "${{ needs.backend.result }}" != "success" ] || \ | |
| [ "${{ needs.frontend.result }}" != "success" ] || \ | |
| [ "${{ needs.e2e.result }}" != "success" ]; then | |
| echo "::error::A required CI job did not succeed — blocking merge." | |
| exit 1 | |
| fi | |
| echo "All required CI jobs succeeded." |