Skip to content

CI/CD

Capsem uses GitHub Actions for continuous integration and release automation. There are four workflows.

WorkflowTriggerWhat it does
ci.yamlPull requestsFull test suite: Rust unit/integration, frontend, Python, coverage
release.yamlTag push (v*)Build assets, build apps (macOS + Linux), create GitHub release
docs.yamlPush to main (docs changes)Build and deploy docs.capsem.org
site.yamlPush to main (site changes)Build and deploy capsem.org

Runs on every pull request. Two parallel jobs:

Tests the KVM backend, which only compiles on Linux:

  1. Enable /dev/kvm via udev rules
  2. Unit tests with coverage for: capsem-core, capsem-logger, capsem-proto, capsem-service, capsem, capsem-mcp
  3. Verify KVM tests actually ran (not silently skipped)
  4. Upload coverage to Codecov with linux-unit flag

Full test suite on macOS (Apple VZ backend):

  1. Dependency auditcargo audit + pnpm audit
  2. Rust unit tests with coverage — all 10 crates: capsem-core, capsem-agent, capsem-logger, capsem-proto, capsem-gateway, capsem-service, capsem, capsem-mcp, capsem-tray, capsem-process
  3. Rust integration tests — cross-crate tests from tests/ directory
  4. Frontend — type check (astro check + svelte-check), vitest with coverage, production build
  5. Python schema tests — capsem-builder tests with 90% coverage floor
  6. Python integration tests — bootstrap, codesign, rootfs artifact suites
  7. Import verification — all test suites import cleanly
  8. Schema drift check — regenerates settings schema and verifies no uncommitted changes

Coverage is uploaded to Codecov with flags:

FlagSourceFloor
unitRust unit tests (macOS)70% lines
linux-unitRust unit tests (Linux/KVM)70% lines
integrationRust integration tests
unit (frontend)vitest coverage
unit (Python)pytest coverage90%

Component-level targets in codecov.yml:

ComponentTarget
capsem-service80%
capsem-mcp80%
capsem-gateway80%
capsem (CLI)80%
capsem-core70%
capsem-agent70%

Triggered by pushing a vX.Y.Z tag. Parallelized pipeline (~18 min wall clock):

preflight (30s) --> build-assets (arm64 + x86_64, 10 min) --> build-app-macos (15 min) --+
+-> test (8 min) --------------------------------------------------------+--> create-release
+-----------------------------------------> build-app-linux (10 min) ----+
JobRunnerWhat it produces
preflightmacos-14Validates Apple cert, Tauri signing key, notarization creds
build-assetsubuntu arm64 + x86_64vmlinuz, initrd.img, rootfs.squashfs per arch
testmacos-14Unit tests + coverage + audit (gates release)
build-app-macosmacos-14.pkg package, host binaries, signed manifest payload
build-app-linuxubuntu arm64 + x86_64.deb packages for both arches
create-releaseubuntuSigns manifest, verifies package payloads, creates GitHub release

The macOS build signs all binaries with a Developer ID certificate:

  • Certificate stored as APPLE_CERTIFICATE secret (base64-encoded p12)
  • Must be legacy PKCS12 format (3DES/SHA1) — OpenSSL 3.x defaults to PBES2/AES which macOS Keychain rejects
  • Notarization via xcrun notarytool with Apple API key

Each release publishes:

  • Capsem-{version}.pkg — macOS installer package
  • capsem_{version}_{arch}.deb — Linux package
  • {arch}-vmlinuz, {arch}-initrd.img, {arch}-rootfs.squashfs — VM images
  • manifest.json — asset manifest with BLAKE3 hashes
  • manifest.json.minisig — minisign signature for the asset manifest
  • capsem-sbom.spdx.json — release SBOM

The desktop auto-updater is disabled for this release line unless a future release ships a verified full-package updater feed.

Before pushing a PR, run the same checks CI will:

Terminal window
# Full test suite (what CI runs)
just test
# Individual components
just test-unit # Rust unit tests
just test-frontend # Frontend type check + vitest + build
just test-python # Python schema tests
# Quick smoke test
just smoke # Fast path: doctor + integration tests

Common failure patterns:

SymptomCauseFix
”No Developer ID signing identity”p12 uses PBES2/AES encryptionRe-export with scripts/fix_p12_legacy.sh
KVM tests skipped/dev/kvm not available on runnerCheck udev rules in workflow
Schema driftsettings-schema.json out of syncRun just schema and commit
Frontend build failsMissing @source directiveAdd pattern to global.css
Coverage below floorNew code without testsAdd tests to meet 70%/80%/90% threshold
Python import errorsNew test file with bad importFix the import path