Skip to content

Merge pull request #102 from Zeus-Deus/fix-workspace-project-icons #332

Merge pull request #102 from Zeus-Deus/fix-workspace-project-icons

Merge pull request #102 from Zeus-Deus/fix-workspace-project-icons #332

Workflow file for this run

name: CI
on:
push:
# Only main here — feature branches are covered by the `pull_request`
# trigger below. Previously this also listed "feature/**", which meant a
# PR from a feature branch ran the whole matrix TWICE for the same commit
# (once for the branch push, once for the PR). Keeping just `main` gives us
# exactly one run per PR plus one post-merge run on main, with no loss of
# coverage: every change is still gated on its PR and re-verified on main.
branches:
- main
pull_request:
branches:
- main
# Cancel in-flight runs for the same branch when a new commit is pushed.
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
# ---------------------------------------------------------------------------
# Lightweight JS + sidecar checks. These need neither the Rust toolchain, the
# Linux GTK/WebKit system libraries, nor the staged sidecar binaries, so they
# run as a separate job IN PARALLEL with `rust` below. Splitting them off
# takes the frontend typecheck/tests and the sidecar checks off the critical
# path — previously they ran serially before `cargo test` and added several
# minutes to every run. Coverage is unchanged: both jobs run the full
# ubuntu + windows matrix.
# ---------------------------------------------------------------------------
web-checks:
name: web-checks (${{ matrix.os }})
strategy:
# Never fail-fast — we always want to see both Linux and Windows results,
# even if one platform regresses.
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup Node
uses: actions/setup-node@v5
with:
# Node 22 for the project's npm scripts. The action itself is on
# @v5 (Node 24 runtime) so it won't trip GitHub's Node-20-actions
# deprecation that takes effect 2026-06-02.
# See: https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/
node-version: 22
cache: npm
- name: Install npm dependencies
run: npm ci
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Sidecar ToS boundary check
# Static check that forbids the sidecar from reading Claude
# credential files, hitting Anthropic URLs directly, spawning
# the `claude` binary outside the auth-probe allowlist, or
# peeking at ANTHROPIC_* / CLAUDE_CODE_OAUTH_TOKEN env vars.
# This is a hard CI gate so a violation can never slip in.
shell: bash
run: cd sidecar/claude-agent && bun run check-tos
- name: Sidecar unit tests
shell: bash
run: cd sidecar/claude-agent && bun install --frozen-lockfile && bun test
- name: TypeScript typecheck
run: npm run check
- name: Frontend tests
run: npm run test
# ---------------------------------------------------------------------------
# Heavy Rust job: this is the critical path. tauri-build validates
# `externalBin`/`bundle.resources` at compile time, so every sidecar binary
# must be staged and the frontend `dist/` must exist before `cargo check` /
# `cargo test`. That work lives here (and only here).
# ---------------------------------------------------------------------------
rust:
name: rust (${{ matrix.os }})
strategy:
# Never fail-fast — we always want to see both Linux and Windows
# results, even if one platform regresses. Masking a Windows-only
# break with a Linux pass (or vice versa) would defeat the whole
# point of having this matrix.
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
# Healthy runs finish in ~6 min on Linux and ~9 min on Windows. The
# GitHub-default ceiling is 6 hours per job, so a single hung test on
# Windows can pin a runner for that long (see PR #28's post-merge CI).
# 30 min is a comfortable 3× headroom over the slow side; anything
# past that is a hang, not slowness.
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Free disk space (Linux)
if: matrix.os == 'ubuntu-latest'
# GitHub-hosted Ubuntu runners ship with ~14 GB free on /. Our
# job restores a Rust target cache, runs `cargo check` and
# `cargo test` (each round of which adds GB to target/), installs
# node_modules, builds the frontend, and `bun install`s the
# sidecar — that comfortably exceeds 14 GB and has produced
# post-merge CI failures with "No space left on device" during
# `cargo test` (see PR #44 and PR #46 merge runs).
#
# Removing the preinstalled toolchains we never touch (Android
# SDK ~12 GB, .NET ~1.6 GB, Haskell GHC ~5 GB, CodeQL ~5 GB,
# cached Docker images ~3 GB) frees ~25 GB in <30 s and avoids
# pinning a third-party action. Windows runners have ~150 GB
# free and don't need this.
shell: bash
run: |
sudo rm -rf /usr/share/dotnet
sudo rm -rf /usr/local/lib/android
sudo rm -rf /opt/ghc
sudo rm -rf /opt/hostedtoolcache/CodeQL
sudo docker image prune --all --force || true
df -h /
- name: Install Linux system dependencies
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
libwebkit2gtk-4.1-dev \
build-essential \
libssl-dev \
libgtk-3-dev \
libayatana-appindicator3-dev \
librsvg2-dev \
patchelf \
file \
ripgrep
# This matches the package list installed in release.yml so the CI
# toolchain is a strict subset of what the release pipeline uses.
# libfuse2 is excluded — it is only needed by `tauri build`'s
# AppImage packager, not by `cargo check` or `cargo test`.
# ripgrep is required by `commands::files::tests::grep_count_pattern_*`,
# which shells out to `rg` to count CODEMUX_DEBUG markers.
- name: Install Windows system dependencies
if: matrix.os == 'windows-latest'
# Chocolatey is pre-installed on windows-latest runners. ripgrep
# is needed for the same `grep_count_pattern_*` tests as Linux.
run: choco install ripgrep -y --no-progress
shell: pwsh
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
- name: Rust cache
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri -> target
# Scope the cache per OS so Linux and Windows artifacts never
# collide in the same cache key.
key: ${{ matrix.os }}
- name: Setup Node
uses: actions/setup-node@v5
with:
# Node 22 for the project's npm scripts. The action itself is on
# @v5 (Node 24 runtime) so it won't trip GitHub's Node-20-actions
# deprecation that takes effect 2026-06-02.
# See: https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/
node-version: 22
cache: npm
- name: Install npm dependencies
run: npm ci
- name: Setup Bun
# Needed to build the claude-agent sidecar below. Installed here
# so the stage step can produce the per-target binary before
# `cargo check` fails tauri-build's externalBin validation.
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Stage claude-agent sidecar binary
shell: bash
run: |
# Like the agent-browser step below, tauri-build validates
# `externalBin` at compile time, so the sidecar binary has to
# be present before `cargo check` / `cargo test`. Full build
# via Bun; fall back to a zero-byte placeholder if Bun fails
# (e.g. transient registry hiccup) — CI is checking types and
# running tests, not producing distributables.
TARGET="${CARGO_BUILD_TARGET:-$(rustc -vV | grep host | cut -d' ' -f2)}"
mkdir -p src-tauri/binaries
case "$TARGET" in
*windows*) DEST="src-tauri/binaries/codemux-claude-sidecar-$TARGET.exe" ;;
*) DEST="src-tauri/binaries/codemux-claude-sidecar-$TARGET" ;;
esac
if bash scripts/build-claude-sidecar.sh; then
if [ ! -s "$DEST" ]; then
echo "[ci] build-claude-sidecar.sh reported success but $DEST is missing — placeholder"
touch "$DEST"
chmod +x "$DEST" 2>/dev/null || true
fi
else
echo "[ci] sidecar build failed — creating placeholder at $DEST"
touch "$DEST"
chmod +x "$DEST" 2>/dev/null || true
fi
- name: Stage agent-browser sidecar binary
shell: bash
run: |
# tauri-build validates `externalBin` at compile time, so even
# `cargo check` fails if src-tauri/binaries/agent-browser-<target>
# does not exist. Our existing copy-agent-browser.sh maps the host
# target triple to the upstream npm package's pre-built binary.
#
# Git Bash is preinstalled on windows-latest, so `shell: bash`
# works on both platforms without needing a second .ps1 script.
bash scripts/copy-agent-browser.sh || true
# Fallback: if the copy script couldn't find the upstream binary
# (e.g. agent-browser's postinstall was skipped on a particular
# platform — see upstream issue #549), drop a zero-byte placeholder
# at the expected path so tauri-build's externalBin check passes.
# CI's job is to verify type-correctness and run unit tests, not
# to produce a distributable installer — an empty placeholder is
# sufficient for `cargo check` / `cargo test`.
TARGET="${CARGO_BUILD_TARGET:-$(rustc -vV | grep host | cut -d' ' -f2)}"
mkdir -p src-tauri/binaries
case "$TARGET" in
*windows*) DEST="src-tauri/binaries/agent-browser-$TARGET.exe" ;;
*) DEST="src-tauri/binaries/agent-browser-$TARGET" ;;
esac
if [ ! -f "$DEST" ]; then
echo "[ci] Real agent-browser binary not found at $DEST — creating placeholder"
touch "$DEST"
chmod +x "$DEST" 2>/dev/null || true
fi
- name: Stage codemux-remote binary (placeholder for cargo check)
shell: bash
run: |
# tauri.conf.json's `bundle.resources = ["binaries/codemux-remote-*"]`
# makes tauri-build fail at compile time if no matching file
# exists. In release.yml the Ubuntu runner actually builds this
# binary (it ships in the .deb/.rpm/AppImage). In ci.yml we
# only need cargo check / cargo test to succeed — a zero-byte
# placeholder satisfies the glob without spending the time to
# cross-compile. Same pattern as the agent-browser stage above.
TARGET="${CARGO_BUILD_TARGET:-$(rustc -vV | grep host | cut -d' ' -f2)}"
mkdir -p src-tauri/binaries
case "$TARGET" in
*windows*) DEST="src-tauri/binaries/codemux-remote-$TARGET.exe" ;;
*) DEST="src-tauri/binaries/codemux-remote-$TARGET" ;;
esac
if [ ! -f "$DEST" ]; then
echo "[ci] Creating zero-byte codemux-remote placeholder at $DEST"
touch "$DEST"
chmod +x "$DEST" 2>/dev/null || true
fi
- name: Build frontend
# `frontendDist: "../dist"` in tauri.conf.json means tauri-build
# wants `dist/` to exist at compile time — build the frontend first.
run: npm run build
- name: Cargo check
run: cargo check --manifest-path src-tauri/Cargo.toml
- name: Configure git identity and line endings for tests
# Several tests in src-tauri/src/git.rs spin up a fixture repo and
# shell out to `git commit`. GitHub runners have git installed but
# no default user.email/user.name, so `git commit` refuses to run
# unless we provide a throwaway identity here.
#
# The `core.autocrlf false` + `core.eol lf` pair disables Git for
# Windows's default `autocrlf=true` behavior — without this, git on
# Windows rewrites `\n` to `\r\n` on checkout, and tests that write
# a file with `\n` then read it back after a git operation fail
# with `left: "...\r\n"` vs `right: "...\n"` assertion mismatches.
# Linux git already defaults to autocrlf=false, so these lines are
# no-ops there.
#
# Works on both Linux and Windows runners — git --global reads from
# $HOME on Linux and %USERPROFILE% on Windows.
shell: bash
run: |
git config --global user.email "ci@codemux.dev"
git config --global user.name "Codemux CI"
git config --global core.autocrlf false
git config --global core.eol lf
- name: Cargo test
run: cargo test --manifest-path src-tauri/Cargo.toml