Spaces:
Runtime error
Runtime error
| # T-008: GitHub Actions CI β Windows Runner, Check/Lint/Test, Python Enforcement | |
| **Type:** Task | |
| **Phase:** 0 β Foundation | |
| **Autonomy:** `agent:review-required` β CI config has security implications (permissions, secrets scope). Human reviews before merge. | |
| **Stack:** `stack:infra` | |
| **Version:** v0.1 | |
| **Iteration:** iter-1 | |
| **Effort:** S (half-day) | |
| --- | |
| > β οΈ **Agent Scope:** Replace the T-001 CI stub at `.github/workflows/ci.yml` with a full workflow. Add missing `package.json` scripts if absent. Do **not** add a full `cargo tauri build` step β that is T-015's scope. This workflow is check/lint/test only: fast feedback on every PR. | |
| --- | |
| ## Context | |
| Every PR to `main` must be gated by: compile check, clippy lint, unit tests, TypeScript typecheck, format check, and the Python-in-src enforcement rule. Without CI these checks are optional and will be skipped. With CI they are required status checks β the PR cannot merge if any fails. | |
| **Why Windows runner:** The project is Windows-first. WASAPI, DirectML, and the VST3/CLAP bundler all have Windows-specific behaviour. Running CI on `ubuntu-latest` would miss platform-specific compile errors that only appear with the MSVC toolchain. | |
| **Scope boundary:** This workflow runs checks only. The full `cargo tauri build` (which requires WebView2 and takes ~10 minutes) is gated behind T-015 which adds a separate `build` job. Keeping check/lint/test fast means PR feedback in under 5 minutes. | |
| --- | |
| ## Prerequisites | |
| - [ ] T-001 merged β `.github/workflows/ci.yml` stub exists, `package.json` exists | |
| - [ ] T-007 merged β `Taskfile.yml` with `task ci:python-check` exists | |
| --- | |
| ## Acceptance Criteria | |
| ### Workflow | |
| - [ ] Workflow triggers on push to `main` and on all pull requests targeting `main` | |
| - [ ] All steps run on `windows-latest` | |
| - [ ] Rust nightly toolchain is installed with `clippy` and `rustfmt` components | |
| - [ ] Rust build cache (`~/.cargo/registry`, `~/.cargo/git`, `target/`) is cached keyed on `Cargo.lock` | |
| - [ ] Node.js 20 LTS and pnpm 9 are installed and frontend deps cached | |
| - [ ] `go-task` is installed so `task ci:python-check` can run | |
| ### Jobs β `check` (fast feedback, ~3β5 min) | |
| - [ ] `cargo check --workspace` passes | |
| - [ ] `cargo clippy --workspace -- -D warnings` passes | |
| - [ ] `cargo fmt --all --check` passes (fails if unformatted Rust code is committed) | |
| - [ ] `pnpm typecheck` passes | |
| - [ ] `pnpm format:check` passes | |
| - [ ] `task ci:python-check` passes (zero Python subprocess calls in `src/` or `kansas/`) | |
| ### Jobs β `test` (depends on `check`) | |
| - [ ] `cargo test --workspace` passes | |
| - [ ] `pnpm test` passes (or skips gracefully if no test suite exists yet) | |
| ### `package.json` scripts | |
| - [ ] `typecheck` script exists: `tsc --noEmit` | |
| - [ ] `format:check` script exists: `prettier --check "src/**/*.{ts,css,html}"` | |
| - [ ] `format` script exists: `prettier --write "src/**/*.{ts,css,html}"` | |
| - [ ] `lint` script exists: `eslint src/ --ext .ts` | |
| - [ ] `test` script exists: `vitest run` (or `echo 'No tests yet' && exit 0` stub if vitest not configured) | |
| ### Security | |
| - [ ] Workflow has `permissions: read-all` at the top level (deny-by-default principle) | |
| - [ ] No `GITHUB_TOKEN` used beyond the default checkout scope | |
| - [ ] No secrets accessed in this workflow (secrets are for Day 10 release workflow) | |
| --- | |
| ## `package.json` Scripts to Add | |
| If these are missing from the `package.json` created by T-001, add them: | |
| ```json | |
| { | |
| "scripts": { | |
| "dev": "vite", | |
| "build": "tsc && vite build", | |
| "preview": "vite preview", | |
| "typecheck": "tsc --noEmit", | |
| "lint": "eslint src/ --ext .ts --max-warnings 0", | |
| "format": "prettier --write \"src/**/*.{ts,css,html}\"", | |
| "format:check": "prettier --check \"src/**/*.{ts,css,html}\"", | |
| "test": "echo '[test] No frontend tests yet β see T-130' && exit 0" | |
| } | |
| } | |
| ``` | |
| Add `prettier` and `eslint` as dev deps if not already present: | |
| ```bash | |
| pnpm add -D prettier eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin | |
| ``` | |
| Add `.prettierrc` at repo root: | |
| ```json | |
| { | |
| "semi": true, | |
| "singleQuote": true, | |
| "trailingComma": "es5", | |
| "printWidth": 100, | |
| "tabWidth": 2 | |
| } | |
| ``` | |
| Add `.eslintrc.json` at repo root: | |
| ```json | |
| { | |
| "root": true, | |
| "parser": "@typescript-eslint/parser", | |
| "plugins": ["@typescript-eslint"], | |
| "extends": [ | |
| "eslint:recommended", | |
| "plugin:@typescript-eslint/recommended" | |
| ], | |
| "rules": { | |
| "@typescript-eslint/no-explicit-any": "error", | |
| "@typescript-eslint/no-unused-vars": "error" | |
| }, | |
| "ignorePatterns": ["dist/", "node_modules/"] | |
| } | |
| ``` | |
| --- | |
| ## `.github/workflows/ci.yml` | |
| ```yaml | |
| name: CI | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| # Deny all permissions by default; grant only what each job needs | |
| permissions: read-all | |
| env: | |
| CARGO_TERM_COLOR: always | |
| RUST_BACKTRACE: 1 | |
| jobs: | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # Job 1: Check, lint, typecheck, format, Python enforcement | |
| # Target time: < 5 minutes | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| check: | |
| name: Check & Lint | |
| runs-on: windows-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| # ββ Rust ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| - name: Install Rust nightly | |
| uses: dtolnay/rust-toolchain@nightly | |
| with: | |
| components: clippy, rustfmt | |
| - name: Cache Rust build artifacts | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| # Key on Cargo.lock so wildcard deps get fresh cache on cargo update | |
| key: ${{ runner.os }}-cargo-${{ hashFiles('Cargo.lock') }} | |
| cache-on-failure: true | |
| # ββ Node / pnpm βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| - name: Install Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 9 | |
| run_install: false | |
| - name: Get pnpm store path | |
| id: pnpm-cache | |
| shell: bash | |
| run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_OUTPUT | |
| - name: Cache pnpm store | |
| uses: actions/cache@v4 | |
| with: | |
| path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} | |
| key: ${{ runner.os }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }} | |
| restore-keys: ${{ runner.os }}-pnpm- | |
| - name: Install frontend dependencies | |
| run: pnpm install --frozen-lockfile | |
| # ββ go-task βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| - name: Install go-task | |
| uses: arduino/setup-task@v2 | |
| with: | |
| version: 3.x | |
| repo-token: ${{ secrets.GITHUB_TOKEN }} | |
| # ββ Rust checks βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| - name: cargo check | |
| run: cargo check --workspace | |
| - name: cargo clippy | |
| run: cargo clippy --workspace -- -D warnings | |
| - name: cargo fmt check | |
| run: cargo fmt --all --check | |
| # ββ TypeScript checks βββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| - name: TypeScript typecheck | |
| run: pnpm typecheck | |
| - name: Prettier format check | |
| run: pnpm format:check | |
| - name: ESLint | |
| run: pnpm lint | |
| # ββ Policy enforcement ββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| - name: Python-in-src check | |
| run: task ci:python-check | |
| shell: bash | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # Job 2: Unit tests (runs after check passes) | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| test: | |
| name: Test | |
| runs-on: windows-latest | |
| needs: check | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Install Rust nightly | |
| uses: dtolnay/rust-toolchain@nightly | |
| - name: Cache Rust build artifacts | |
| uses: Swatinem/rust-cache@v2 | |
| with: | |
| key: ${{ runner.os }}-cargo-${{ hashFiles('Cargo.lock') }} | |
| cache-on-failure: true | |
| - name: Install Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 9 | |
| run_install: false | |
| - name: Install frontend dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: cargo test | |
| run: cargo test --workspace | |
| - name: Frontend test | |
| run: pnpm test | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # Job 3: Plugin validation stub (T-143 wires in pluginval) | |
| # ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| pluginval: | |
| name: Plugin Validation (stub) | |
| runs-on: windows-latest | |
| needs: check | |
| # T-143 replaces this stub with real pluginval execution | |
| # and removes the `if: false` guard | |
| if: false | |
| steps: | |
| - name: pluginval stub | |
| run: echo "pluginval CI β see T-143" | |
| ``` | |
| --- | |
| ## Implementation Notes | |
| ### Why `dtolnay/rust-toolchain@nightly` not `actions-rs/toolchain` | |
| `actions-rs/toolchain` is unmaintained. `dtolnay/rust-toolchain` is the community-maintained replacement β it handles nightly, components, and targets correctly on all platforms. | |
| ### Why `Swatinem/rust-cache@v2` | |
| It caches `~/.cargo/registry`, `~/.cargo/git`, and `target/` in a single step. On Windows runners the `target/` directory can be 2β4 GB β without caching the Rust build step takes 8β12 minutes on every run. With a warm cache it drops to 1β3 minutes. | |
| ### `pnpm install --frozen-lockfile` | |
| This fails the CI job if `pnpm-lock.yaml` is out of sync with `package.json`. It prevents "works on my machine" dependency drift. Developers must commit an updated lockfile when adding deps. | |
| ### Python check via `task ci:python-check` | |
| Calls the Taskfile target defined in T-007. The target uses `grep -rn` and exits non-zero if any match is found. The `shell: bash` directive is required on Windows runners to use bash syntax in the `grep` command. | |
| ### The `pluginval` job with `if: false` | |
| This is a placeholder job that never runs (`if: false` prevents execution). T-143 replaces it with real pluginval steps and removes the guard. Having it here means: | |
| - T-143 agents have a named job to fill in β no new job creation needed | |
| - The job name appears in the GitHub Actions UI under "skipped" β visible reminder | |
| ### Branch protection (manual step β not in YAML) | |
| After merging T-008, go to **GitHub β Settings β Branches β Add rule** for `main`: | |
| - β Require status checks to pass before merging | |
| - Required checks: `Check & Lint`, `Test` | |
| - β Require branches to be up to date before merging | |
| This is a repo settings action, not something the agent can do via YAML. | |
| --- | |
| ## Testing | |
| ### Local simulation | |
| ```bash | |
| # These are the same commands the CI runs β all should pass locally first | |
| cargo check --workspace | |
| cargo clippy --workspace -- -D warnings | |
| cargo fmt --all --check | |
| pnpm typecheck | |
| pnpm format:check | |
| pnpm lint | |
| task ci:python-check | |
| cargo test --workspace | |
| pnpm test | |
| ``` | |
| ### After pushing | |
| - Open a draft PR against `main` | |
| - Verify both `Check & Lint` and `Test` jobs appear and pass | |
| - Introduce a deliberate clippy warning (e.g. unused variable) β verify CI fails on that job | |
| - Revert the deliberate warning β verify CI passes again | |
| --- | |
| ## GitHub CLI | |
| ```bash | |
| gh issue create \ | |
| --title "T-008: GitHub Actions CI β Windows runner, check/lint/test, Python enforcement" \ | |
| --label "type:task,stack:infra,agent:review-required,priority:critical,status:ready,day:1" \ | |
| --body-file T-008.md | |
| ``` | |
| --- | |
| **Parent:** GENESIS | |
| **Blocks:** T-015 (adds `build` job to this workflow), T-074 (adds timing test step), T-136 (expands Python check to cover more patterns), T-143 (fills in `pluginval` job), T-147 (adds perf regression job) | |
| **Blocked By:** T-001, T-007 | |
| **Version:** v0.1 Β· **Iteration:** iter-1 Β· **Effort:** S (half-day) | |