CI #2710
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: | |
| merge_group: | |
| pull_request: | |
| push: | |
| branches: [main] | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| CARGO_TERM_COLOR: always | |
| CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse | |
| jobs: | |
| test: | |
| name: Test | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| partition: [1/3, 2/3, 3/3] | |
| steps: | |
| - name: Free up space | |
| run: sudo rm -rf /opt/hostedtoolcache /usr/share/dotnet /usr/local/lib/android /opt/ghc /usr/local/.ghcup || true | |
| - uses: actions/checkout@v7.0.1 | |
| - name: Configure toolchain | |
| run: | | |
| rustup toolchain install --profile minimal --no-self-update stable | |
| rustup default stable | |
| - uses: taiki-e/install-action@v2.86.1 | |
| with: | |
| tool: cargo-nextest,just | |
| - uses: Swatinem/rust-cache@v2 | |
| - uses: actions/setup-node@v7.0.0 | |
| with: | |
| node-version: lts/* | |
| cache: npm | |
| cache-dependency-path: private-web/package-lock.json | |
| # The private-server tests serve the embedded React SPA via rust-embed, | |
| # so they need private-web/dist/ to exist. `just test` sets | |
| # SKIP_FRONTEND_BUILD=1 (build.rs won't build it) and a fresh checkout has | |
| # no dist/, so build it explicitly here. Dev machines already have a dist/ | |
| # from working on the frontend. | |
| - name: Build frontend | |
| run: npm ci && npm run build | |
| working-directory: private-web | |
| # `just test` runs nextest against a throwaway tmpfs Postgres that | |
| # scripts/ramdisk-pg.sh spins up with the runner's own initdb/pg_ctl | |
| # (preinstalled on ubuntu-latest), so no system Postgres service or | |
| # role/db setup is needed here — same path developers run locally. | |
| - run: just test --partition hash:${{ matrix.partition }} | |
| clippy: | |
| name: Clippy | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Free up space | |
| run: sudo rm -rf /opt/hostedtoolcache /usr/share/dotnet /usr/local/lib/android /opt/ghc /usr/local/.ghcup || true | |
| - uses: actions/checkout@v7.0.1 | |
| - name: Configure toolchain | |
| run: | | |
| rustup toolchain install --profile minimal --no-self-update stable | |
| rustup default stable | |
| rustup component add clippy | |
| - uses: taiki-e/install-action@v2.86.1 | |
| with: | |
| tool: just | |
| - uses: Swatinem/rust-cache@v2 | |
| - run: just lint | |
| fmt: | |
| name: Rustfmt | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Free up space | |
| run: sudo rm -rf /opt/hostedtoolcache /usr/share/dotnet /usr/local/lib/android /opt/ghc /usr/local/.ghcup || true | |
| - uses: actions/checkout@v7.0.1 | |
| - name: Configure toolchain | |
| run: | | |
| rustup toolchain install --profile minimal --no-self-update stable | |
| rustup default stable | |
| rustup component add rustfmt | |
| - uses: taiki-e/install-action@v2.86.1 | |
| with: | |
| tool: just | |
| - run: just fmt-check | |
| e2e: | |
| name: Playwright | |
| runs-on: ubuntu-latest | |
| # The fixture (private-web/e2e/fixture.ts) spawns the private-server | |
| # binary plus a Vite dev server itself, so this job builds the rust | |
| # binaries the fixture expects, installs the frontend deps + chromium, | |
| # and runs `npm run test:e2e`. | |
| # | |
| # Sharded across 4 parallel jobs via Playwright's --shard; each shard | |
| # runs a quarter of the tests. Artifacts are suffixed with the shard | |
| # index so uploads don't collide. | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| shard: [1, 2, 3, 4] | |
| steps: | |
| - name: Free up space | |
| run: sudo rm -rf /opt/hostedtoolcache /usr/share/dotnet /usr/local/lib/android /opt/ghc /usr/local/.ghcup || true | |
| - uses: actions/checkout@v7.0.1 | |
| - name: Configure toolchain | |
| run: | | |
| rustup toolchain install --profile minimal --no-self-update stable | |
| rustup default stable | |
| - uses: Swatinem/rust-cache@v2 | |
| - uses: actions/setup-node@v7.0.0 | |
| with: | |
| node-version: lts/* | |
| cache: npm | |
| cache-dependency-path: private-web/package-lock.json | |
| - name: Build private-server + migrate | |
| # SKIP_FRONTEND_BUILD avoids private-server/build.rs running an npm | |
| # install + vite build to embed dist/ — the e2e fixture uses Vite | |
| # at runtime so the embedded bundle isn't needed here. | |
| env: | |
| SKIP_FRONTEND_BUILD: "1" | |
| run: cargo build --bin private-server --bin migrate --locked | |
| - name: npm ci | |
| run: npm ci | |
| working-directory: private-web | |
| # Frontend unit tests (vitest). They need no browser or database, so | |
| # they run here before the expensive Playwright setup and fail fast. | |
| - name: Frontend unit tests | |
| run: npm run test | |
| working-directory: private-web | |
| - name: Cache Playwright browsers | |
| uses: actions/cache@v6.1.0 | |
| with: | |
| path: ~/.cache/ms-playwright | |
| key: ${{ runner.os }}-playwright-${{ hashFiles('private-web/package-lock.json') }} | |
| restore-keys: | | |
| ${{ runner.os }}-playwright- | |
| - name: Install Playwright browsers | |
| run: npx playwright install --with-deps chromium | |
| working-directory: private-web | |
| - name: Playwright tests | |
| # The wrapper spins up a throwaway tmpfs Postgres and exports | |
| # CANOPY_E2E_ADMIN_DATABASE_URL pointing at it; the fixture creates its | |
| # per-worker databases on that cluster. Same DB harness as `just test`. | |
| run: ../scripts/ramdisk-pg.sh npm run test:e2e -- --shard=${{ matrix.shard }}/4 | |
| working-directory: private-web | |
| - name: Upload Playwright report | |
| if: failure() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: playwright-report-${{ matrix.shard }} | |
| path: | | |
| private-web/playwright-report | |
| private-web/test-results | |
| retention-days: 7 | |
| if-no-files-found: ignore | |
| - name: Upload Playwright screenshots | |
| # Every test (pass or fail) takes an end-of-test screenshot (see | |
| # playwright.config.ts `use.screenshot`), so this always uploads, | |
| # not just on failure, to make visual review of a PR's UI trivial. | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: playwright-screenshots-${{ matrix.shard }} | |
| path: private-web/test-results/**/*.png | |
| retention-days: 5 | |
| if-no-files-found: ignore | |
| generated: | |
| name: Generated files | |
| runs-on: ubuntu-latest | |
| # The committed OpenAPI documents and the generated API client must be what | |
| # the current code produces. Regenerating needs no database and no network: | |
| # the documents come from compile-time annotations and the client comes from | |
| # the committed public document. | |
| steps: | |
| - name: Free up space | |
| run: sudo rm -rf /opt/hostedtoolcache /usr/share/dotnet /usr/local/lib/android /opt/ghc /usr/local/.ghcup || true | |
| - uses: actions/checkout@v7.0.1 | |
| - name: Configure toolchain | |
| run: | | |
| rustup toolchain install --profile minimal --no-self-update stable | |
| rustup default stable | |
| - uses: taiki-e/install-action@v2.86.1 | |
| with: | |
| tool: just | |
| - uses: Swatinem/rust-cache@v2 | |
| - run: just check-generated | |
| - run: just check-api-deps | |
| semver: | |
| name: API compatibility | |
| runs-on: ubuntu-latest | |
| # The public API stays compatible unless a break is coordinated, and | |
| # `.workhorse/specs/platform/api-compatibility.md` makes that mechanical: the | |
| # published client generated after a change must not be a semver-breaking | |
| # change against the one generated before. | |
| # | |
| # cargo-semver-checks reads the bump in the crate's manifest (stamped from the | |
| # document's info.version) against what is on crates.io, so an uncoordinated | |
| # break fails here, while a break whose major has been raised in the document | |
| # passes — that raise being where the coordination is recorded. | |
| steps: | |
| - name: Free up space | |
| run: sudo rm -rf /opt/hostedtoolcache /usr/share/dotnet /usr/local/lib/android /opt/ghc /usr/local/.ghcup || true | |
| - uses: actions/checkout@v7.0.1 | |
| - name: Configure toolchain | |
| run: | | |
| rustup toolchain install --profile minimal --no-self-update stable | |
| rustup default stable | |
| - uses: taiki-e/install-action@v2.86.1 | |
| with: | |
| tool: cargo-semver-checks,just | |
| - uses: Swatinem/rust-cache@v2 | |
| - run: just semver-checks | |
| api-crate: | |
| name: API client | |
| runs-on: ubuntu-latest | |
| # The published client is a cargo project of its own (see release-plz.toml), so | |
| # the workspace jobs above do not build, lint, format-check or test it. Without | |
| # this, the one crate with outside consumers is the one nothing checks. | |
| steps: | |
| - uses: actions/checkout@v7.0.1 | |
| - name: Configure toolchain | |
| run: | | |
| rustup toolchain install --profile minimal --no-self-update stable --component clippy,rustfmt | |
| rustup default stable | |
| - uses: taiki-e/install-action@v2.86.1 | |
| with: | |
| tool: just | |
| - uses: Swatinem/rust-cache@v2 | |
| - run: just check-api-crate | |
| # Dummy job to have a stable name for PR requirements | |
| tests-pass: | |
| if: always() # always run even if dependencies fail | |
| name: Tests pass | |
| needs: | |
| - test | |
| - clippy | |
| - fmt | |
| - e2e | |
| - generated | |
| - semver | |
| - api-crate | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: re-actors/alls-green@release/v1 | |
| with: | |
| jobs: ${{ toJSON(needs) }} |