chore(release): v0.1.49 #319
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: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| # Least privilege for GITHUB_TOKEN; mirror ci-web.yml. | |
| permissions: | |
| contents: read | |
| # Stacked pushes to the same ref cancel the superseded run instead | |
| # of burning the full 3-OS matrix to completion; mirror ci-web.yml. | |
| concurrency: | |
| group: ci-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| CARGO_TERM_COLOR: always | |
| # Opt every JavaScript-based action (`actions/checkout@v6`, | |
| # `actions/cache@v5`, `actions/setup-node@v5`, `pnpm/action-setup@v5`) | |
| # into the Node 24 runtime ahead of GitHub's June 2026 forced switch. | |
| # Skips the deprecation warning on every job; also surfaces any | |
| # Node-24 incompatibility well before the September 2026 hard | |
| # removal of Node 20 from runners. | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" | |
| jobs: | |
| lint: | |
| name: Format / Clippy (Linux) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Install Rust stable | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: clippy, rustfmt | |
| # Swatinem/rust-cache keys on job id + toolchain + lockfile and | |
| # prunes stale artifacts, unlike a raw actions/cache of target/ | |
| # keyed only on Cargo.lock (which froze at the first | |
| # post-lockfile-change run and re-uploaded multi-GB archives). | |
| - name: Rust build cache | |
| uses: Swatinem/rust-cache@v2 | |
| - name: Check formatting | |
| run: cargo fmt --check -p claudepot-core -p claudepot-cli -p xtask | |
| - name: Clippy (core + cli + xtask, all targets) | |
| # `--all-targets` lints test code too. Without it, test-only | |
| # lints accumulate silently between Rust releases and surface | |
| # as a wall of errors at the next local clippy run (see the | |
| # 2026-05-13 cleanup commit for the 7-lint backlog this gate | |
| # would have caught at the source). | |
| run: cargo clippy --all-targets -p claudepot-core -p claudepot-cli -p xtask -- -D warnings | |
| # CC-parity fixtures (parity-harness/README.md): verifies | |
| # claudepot-core's settings-merge semantics against golden | |
| # fixtures generated from CC's own implementation. The xtask | |
| # crate shares the cargo cache above, so the marginal cost is | |
| # one small crate compile plus a millisecond fixture run. | |
| - name: CC-parity fixtures (cargo xtask verify-cc-parity) | |
| run: cargo xtask verify-cc-parity | |
| # ─── Architectural invariants ───────────────────────────── | |
| # Three grep-based tripwires. Each protects an invariant | |
| # called out in dev-docs/codex-plans/20260515-1130-shared-memory.md | |
| # that the type system can't enforce on its own. | |
| - name: Invariant — codex_session parser is a single leaf | |
| # The Codex rollout parser is a neutral leaf in the | |
| # workspace. The decoder pattern is `"session_meta" =>` | |
| # in a match arm — flag that anywhere outside the | |
| # codex_session/ module. We deliberately ignore plain | |
| # string occurrences (test fixtures, comments) because | |
| # those aren't parsers. | |
| run: | | |
| set -euo pipefail | |
| violators=$(grep -rnE '"session_meta"[[:space:]]*=>' crates/ --include='*.rs' || true) | |
| unexpected=$(echo "$violators" | grep -v 'crates/claudepot-core/src/codex_session/' || true) | |
| if [ -n "$unexpected" ]; then | |
| echo "::error::Codex rollout decoder found outside codex_session/:" | |
| echo "$unexpected" | |
| echo | |
| echo "Codex rollout parsing (matching on 'session_meta' record type)" | |
| echo "must live in claudepot-core/src/codex_session/. Duplicating it" | |
| echo "elsewhere leads to two parsers drifting in parallel — exactly" | |
| echo "what the plan's leaf-module discipline prevents." | |
| exit 1 | |
| fi | |
| - name: Invariant — raw text SELECTs are confined to redaction-aware paths | |
| # R9 of the plan: every read of transcript-content columns | |
| # must go through redaction::apply before crossing an | |
| # emission boundary. This grep catches new SELECTs that | |
| # skip the redaction step. | |
| run: | | |
| set -euo pipefail | |
| patterns='SELECT.*user_text|SELECT.*assistant_text|SELECT.*tool_result_text' | |
| violators=$(grep -rlE "$patterns" crates/ --include='*.rs' || true) | |
| unexpected=$(echo "$violators" \ | |
| | grep -v 'crates/claudepot-core/src/shared_memory/search.rs' \ | |
| | grep -v 'crates/claudepot-core/src/shared_memory/read.rs' \ | |
| | grep -v 'crates/claudepot-core/src/shared_memory/indexer.rs' \ | |
| | grep -v 'crates/claudepot-core/src/shared_memory/schema.rs' \ | |
| | grep -v 'crates/claudepot-core/src/shared_memory/claude_exchanges.rs' \ | |
| || true) | |
| if [ -n "$unexpected" ]; then | |
| echo "::error::Raw text SELECTs found outside redaction-aware paths:" | |
| echo "$unexpected" | |
| echo | |
| echo "Every emission of exchanges.user_text / assistant_text /" | |
| echo "tool_calls.tool_result_text must go through redaction::apply" | |
| echo "before crossing a UI / export / log / MCP boundary." | |
| echo "Extend the allowlist in .github/workflows/ci.yml if adding" | |
| echo "a legitimate reader, and document why redaction is handled" | |
| echo "at the call site." | |
| exit 1 | |
| fi | |
| - name: Invariant — no bare matches!() statements | |
| # H2 of the grill report: a bare matches!(err, Variant); | |
| # returns bool and discards it. Tests pass against any | |
| # error variant. The grill round caught six such sites; | |
| # this gate prevents regression. | |
| # | |
| # The signal is a `matches!()` call used as a *statement* | |
| # (followed by `;`), not as an expression value. Match | |
| # arms, return values, and let bindings have no trailing | |
| # semicolon and are correctly excluded. | |
| run: | | |
| set -euo pipefail | |
| violators=$(grep -rnE '^[[:space:]]+matches!\(.*\);' crates/ --include='*.rs' || true) | |
| if [ -n "$violators" ]; then | |
| echo "::error::Bare matches!() statements found:" | |
| echo "$violators" | |
| echo | |
| echo "A bare matches!() returns bool and discards it — the test" | |
| echo "passes against any variant. Wrap in assert!(matches!(...))." | |
| exit 1 | |
| fi | |
| test: | |
| name: Tests (${{ matrix.os }}) | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-latest, windows-latest] | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Install Rust stable | |
| uses: dtolnay/rust-toolchain@stable | |
| # See the lint job for why rust-cache replaces actions/cache. | |
| - name: Rust build cache | |
| uses: Swatinem/rust-cache@v2 | |
| - name: Tests (core + cli) | |
| run: cargo test -p claudepot-core -p claudepot-cli | |
| # The tauri crate carries real unit tests (orchestrators, DTOs, | |
| # preferences) plus an install smoke test that no other gate | |
| # runs. It builds without extra system packages on macOS and | |
| # Windows; on Linux it needs webkit2gtk + friends (see | |
| # release.yml's build-linux-x86_64 apt list), which aren't | |
| # worth installing on every CI run — release.yml's Linux build | |
| # job is the Linux compile gate for this crate. | |
| # | |
| # tauri-build validates the `bundle.externalBin` entry at | |
| # build-script time, so the CLI binary must be staged under | |
| # src-tauri/binaries/ first (a fresh checkout doesn't have it). | |
| # The frontend `dist/` is NOT needed: dev-profile codegen | |
| # doesn't embed assets. | |
| - name: Tests (tauri crate) | |
| if: matrix.os != 'ubuntu-latest' | |
| shell: bash | |
| run: | | |
| set -eu | |
| cargo build -p claudepot-cli | |
| TRIPLE=$(rustc -vV | sed -n 's/^host: //p') | |
| EXT="" | |
| case "$TRIPLE" in *windows*) EXT=".exe" ;; esac | |
| mkdir -p src-tauri/binaries | |
| cp "target/debug/claudepot$EXT" "src-tauri/binaries/claudepot-cli-$TRIPLE$EXT" | |
| cargo test -p claudepot-tauri | |
| # grill X26: `--ignored` integration tests are normally locked out | |
| # of the default suite — they touch the real OS scheduler, hit | |
| # live LLMs, or otherwise depend on infra that the matrix runners | |
| # can't always reach. Without a lane in CI they ran nowhere, so | |
| # the register→fire→record→prune path and the templates+LLM | |
| # round-trip drifted unobserved. | |
| # | |
| # This job runs them in advisory mode: `continue-on-error: true` | |
| # so a flake doesn't redden the merge gate, but failures are | |
| # still visible on the Actions summary page. Promote to a hard | |
| # gate once the infra-flakes are smoothed out. | |
| e2e-ignored: | |
| name: E2E (--ignored, advisory) | |
| runs-on: ubuntu-latest | |
| continue-on-error: true | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Install Rust stable | |
| uses: dtolnay/rust-toolchain@stable | |
| # See the lint job for why rust-cache replaces actions/cache. | |
| - name: Rust build cache | |
| uses: Swatinem/rust-cache@v2 | |
| # Scheduler-touching E2E. On Linux the active scheduler is | |
| # systemd-user; tests that require it self-skip when systemd | |
| # isn't reachable inside the runner sandbox, so the lane stays | |
| # green even when systemd is absent. | |
| - name: Agent E2E (--ignored) | |
| run: cargo test -p claudepot-core --test agent_e2e -- --ignored | |
| # Templates + LLM round-trip. Self-skips when no API key is | |
| # available (no secret on PRs from forks — the lane stays | |
| # advisory either way). | |
| - name: Templates real-LLM (--ignored) | |
| env: | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| run: cargo test -p claudepot-core --test templates_real_llm -- --ignored | |
| frontend-test: | |
| name: Frontend tests (Vitest) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v5 | |
| with: | |
| version: 9 | |
| - name: Setup Node | |
| uses: actions/setup-node@v5 | |
| with: | |
| node-version: 24 | |
| cache: pnpm | |
| - name: Install JS deps | |
| run: pnpm install --frozen-lockfile | |
| - name: Typecheck | |
| run: pnpm tsc --noEmit | |
| # The production vite build catches failure classes tsc and | |
| # vitest don't (asset resolution, CSS shard syntax errors, | |
| # rollup-only module issues). Without this step the first | |
| # `pnpm build` is tauri.conf.json's beforeBuildCommand at | |
| # release-tag time — the most expensive place to find out. | |
| # Unlike web/'s skipped build (see ci-web.yml), this is a | |
| # plain client-side vite bundle that builds offline. | |
| - name: Production build (vite) | |
| run: pnpm build | |
| - name: Run tests | |
| run: pnpm test |