Quality hardening: real bug fixes + SOLID/DRY refactors + honest coverage + tooling spine #166
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
| # PR + main verification: typecheck (core + pro), lint, and the full vitest suite | |
| # (which includes the pro/ tests when the pro repo is checked out alongside). | |
| # | |
| # Cross-repo note: pro/ is a separate private repo (paid features). Set the repo | |
| # secret CI_CROSS_REPO_TOKEN (a PAT with read access to off-grid-ai/desktop-pro) | |
| # so the pro tests + pro typecheck run here too. Without it, core is still fully | |
| # verified and the pro steps are skipped gracefully. | |
| name: CI | |
| on: | |
| pull_request: | |
| push: | |
| branches: [main] | |
| jobs: | |
| verify: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 25 # backstop: a hung step fails fast instead of running for hours | |
| steps: | |
| - uses: actions/checkout@v4 | |
| # Check out pro on the branch that MATCHES this PR/push (so a coordinated | |
| # core+pro change is tested together). If pro has no such branch (most PRs), | |
| # this step's checkout fails quietly and the fallback below pulls pro `main`. | |
| # Without this, CI silently tested the core branch against pro `main`, so | |
| # pro's new test files were absent while pro source was still measured → | |
| # coverage cratered (60% vs 97% local). See PR #46. | |
| - name: Check out pro (matching branch) into ./pro | |
| id: pro_branch | |
| uses: actions/checkout@v4 | |
| continue-on-error: true | |
| with: | |
| repository: off-grid-ai/desktop-pro | |
| token: ${{ secrets.CI_CROSS_REPO_TOKEN }} | |
| path: pro | |
| ref: ${{ github.head_ref || github.ref_name }} | |
| - name: Fall back to pro main if no matching branch | |
| if: ${{ steps.pro_branch.outcome != 'success' || hashFiles('pro/tsconfig.json') == '' }} | |
| continue-on-error: true | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: off-grid-ai/desktop-pro | |
| token: ${{ secrets.CI_CROSS_REPO_TOKEN }} | |
| path: pro | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: '24' # node:sqlite (used by integration tests) is available unflagged | |
| - run: npm ci | |
| # Hard gates: types + the full test suite. | |
| - name: Typecheck (core) | |
| timeout-minutes: 6 | |
| run: npm run typecheck | |
| - name: Typecheck (pro) | |
| if: ${{ hashFiles('pro/tsconfig.json') != '' }} | |
| timeout-minutes: 6 | |
| run: cd pro && npx tsc --noEmit -p tsconfig.json | |
| # Runs the full vitest suite AND enforces the coverage ratchet floor | |
| # (vitest.config.ts `thresholds`) — the same gate as the pre-push hook, so a | |
| # coverage regression fails CI, not just local pushes. The pro/** threshold | |
| # group applies only when pro was checked out above (guarded in the config). | |
| - name: Test + coverage thresholds | |
| timeout-minutes: 10 | |
| run: npm run test:coverage | |
| # Architecture-boundary gate (hygiene §A/§H): walks the import graph and fails | |
| # on a forbidden edge — core->pro (open-core), a pure-logic module gaining an | |
| # IO import, renderer->main, circular deps. Fast (graph-only, ~2s), so unlike | |
| # eslint it can BLOCK without wedging CI. Rules verified clean on the tree. | |
| - name: Dependency boundaries | |
| timeout-minutes: 4 | |
| run: npm run depcruise | |
| # NOTE: SonarCloud runs via Automatic Analysis (SonarCloud clones this repo and | |
| # analyzes core on each push/PR — no CI step, token, or coverage upload here). | |
| # Advisory: lint is enforced locally; bounded + non-blocking here so an | |
| # eslint slowdown over the full tree (incl. checked-out pro/) can't wedge CI. | |
| - name: Lint | |
| timeout-minutes: 8 | |
| continue-on-error: true | |
| run: npm run lint |