name: CI on: pull_request: {} push: branches: [main] # NO `tags:` here, deliberately. Nothing in THIS file gates a release — the checks # that BLOCK a publish (this same suite plus the real-daemon E2E matrix) live in # release-gate.yml, which Release and Docker images each list in `needs:` because # workflows are not ordered against one another. # # It used to run on tags too, "for the record". The record was three copies: the gate # runs this suite once per calling workflow, so a tag push executed typecheck + tests # THREE times and produced one extra full run whose result gated nothing and whose # failure nobody could act on differently. A tag is not branch-protected, so no # required check disappears with it. workflow_dispatch: {} # One in-flight run per ref. PR pushes cancel their predecessor — nobody reads # the result of a superseded commit — while main and tags run to completion so # their history stays complete. concurrency: group: ci-${{ github.ref }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} # Deliberately NO `paths:` filter. These jobs are required status checks, and a # path-filtered required check never reports on a PR it excluded — the PR is then # unmergeable forever with no visible reason. jobs: typecheck: name: Typecheck runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v7 - name: Setup Bun uses: oven-sh/setup-bun@v2 with: bun-version: latest - name: Cache bun install uses: actions/cache@v6 with: path: ~/.bun/install/cache key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock', '**/bun.lockb') }} restore-keys: | ${{ runner.os }}-bun- - name: Install dependencies run: bun install --frozen-lockfile - name: Typecheck apps/api run: bun run --cwd apps/api lint - name: Typecheck apps/dashboard working-directory: apps/dashboard run: | set -o pipefail # fumadocs has pre-existing type errors that are filtered out; # any other error causes the step to fail via the exit code below. npx tsc --noEmit 2>&1 | tee tsc.log | grep -v fumadocs || true if grep -v fumadocs tsc.log | grep -E '^(.*): error TS' > /dev/null; then echo "Typecheck failed (non-fumadocs errors above)" exit 1 fi test: name: Test runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v7 - name: Setup Bun uses: oven-sh/setup-bun@v2 with: bun-version: latest - name: Cache bun install uses: actions/cache@v6 with: path: ~/.bun/install/cache key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock', '**/bun.lockb') }} restore-keys: | ${{ runner.os }}-bun- - name: Install dependencies run: bun install --frozen-lockfile # Runs `turbo run test` → vitest across every package that defines a test # script (@repo/core, @repo/adapters, @repo/db [PGlite — no external DB], # apps/api, apps/dashboard). Packages resolve to src, so no build needed. # apps/api excludes test/e2e/** here — those need a daemon and run in the # e2e-docker job in release-gate.yml, where they gate the publish. - name: Run tests run: bun run test # The webmail server's own suite, which nothing else runs. # # GH-220: apps/email/server/test/{sanitize,from-header,list-snippet}.test.ts had no # runner at all. They are `bun:test` files, and the root `test` script is # `turbo run test --filter=!@repo/email` → vitest, so turbo never reached them; the # server is not a root workspace member either (workspaces is apps/* + packages/*, # which matches apps/email but not its subdirectories), so its deps are not installed # by the root install. Net effect: 35 assertions were green on someone's laptop and # unreachable from every pipeline — including sanitize.test.ts, which pins the fix for # the CSS url()/@import read-receipt leak (GHSA-3hcp-c4c7-6m8p) and asserts the read # pane stays inert. That is exactly the kind of test that must not rot. # # Its own job rather than a turbo target: these need `bun test` (not vitest) and a # separate install rooted in apps/email/server. Independent and parallel, so it cannot # slow the jobs above. webmail-server-test: name: Test webmail server runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v7 - name: Setup Bun uses: oven-sh/setup-bun@v2 with: bun-version: latest - name: Cache bun install uses: actions/cache@v6 with: path: ~/.bun/install/cache key: ${{ runner.os }}-bun-webmail-${{ hashFiles('apps/email/server/bun.lock') }} restore-keys: | ${{ runner.os }}-bun-webmail- ${{ runner.os }}-bun- # Not --frozen-lockfile: the committed lockfile is regenerated by # scripts/build-release.ts for the dist, so it can legitimately lag the manifest # here. Resolving fresh is fine for a test-only install. - name: Install webmail server dependencies working-directory: apps/email/server run: bun install - name: Run webmail server tests working-directory: apps/email/server run: bun test