# CI & pipelines PearData mirrors the PearDock-style **cross-compile from one Linux runner** pipeline: Bare servers + Electron clients, published as rolling / tagged releases. ## Host matrix | Product | Hosts | |---------|-------| | Server (Bare) | `linux-x64`, `linux-arm64` **only** | | Client (Electron) | `linux-x64`, `linux-arm64`, `darwin-x64`, `darwin-arm64`, `win32-x64`, `win32-arm64` | ## Gitea Actions | Workflow | Trigger | Job | |----------|---------|-----| | `.gitea/workflows/ci.yml` | push / PR / manual | install, `npm test` | | `.gitea/workflows/release-rolling.yml` | **every** push to `main`/`master` + manual | test → build matrix → Gitea tag **`rolling`** | Rolling job (ubuntu-latest, ~180m timeout): 1. System deps for Electron packaging 2. `npm ci --ignore-scripts` + esbuild install 3. `SKIP_INTEGRATION=1 npm test` 4. Install vendored `tools/rcodesign` (darwin client seal-sign) 5. `scripts/gitea-rolling-release.sh` - `make.cjs server` → `peardata-server-*.tar.gz` - `make.cjs client` → `peardata-client-*.tar.gz` - upload to `rolling` prerelease Manual inputs: `skip_client`, `server_hosts`, `client_hosts`, `dry_run`. **Secret:** `RELEASE_TOKEN` (required to publish). Optional `GITEA_URL`. ## GitHub Actions | Workflow | Trigger | Jobs | |----------|---------|------| | `.github/workflows/ci.yml` | push / PR / manual | **test** (Node 20+22), **lint-docs** | | `.github/workflows/release.yml` | `v*` tags / manual | Same binary matrix → GitHub Release assets | ## Local parity ```bash npm ci # includes @qvac/sdk for desktop LLM packaging SKIP_INTEGRATION=1 npm test # Server (Linux Bare) npm run make:server:linux-x64 # Client (Electron) — ships QVAC Bare worker via @qvac/sdk/electron-forge npm run make:client:linux-x64 # Tools-only client (skip native QVAC worker / larger natives) PEARDATA_SKIP_QVAC=1 npm run make:client:linux-x64 # Full matrix (slow; QVAC raises client package time) npm run make # Stage release archives without upload DRY_RUN=1 bash scripts/gitea-rolling-release.sh ``` ## QVAC in release clients | Item | Detail | |------|--------| | Dependency | `@qvac/sdk` in `package.json` **dependencies** (must install under this repo — not a parent hoist) | | Forge plugin | `@qvac/sdk/electron-forge` + `@electron-forge/plugin-base` | | Config | `qvac.config.json` — LLM plugin only | | Asar | Forced **off** when QVAC packaging is enabled (Bare needs real paths) | | Bare runtime | `bare-runtime` + `bare-runtime-` must be **inside** the app `node_modules` | | Skip | `PEARDATA_SKIP_QVAC=1` → tools-only client | | Auto tools-only | `win32-arm64` (no `@qvac/llm-llamacpp` prebuild) — still ships client | | Docs | [QVAC.md](./QVAC.md) · [Electron tutorial](https://docs.qvac.tether.io/tutorials/electron) | ### Cross-host Bare runtime (issue #1492) CI runs on **Linux** but packages **darwin/win32** clients. `npm ci` skips optional `bare-runtime-darwin-*` / `bare-runtime-win32-*` packages (os/cpu filters). Without them the packaged app falls back to: ```text Ready (tools-only: Could not load the Bare runtime binary for darwin-arm64 … ``` Release workflows and `make.cjs` install platform binaries via `scripts/ensure-qvac-bare-runtimes.cjs`, which **`npm pack` + extracts** each `bare-runtime-` into `node_modules` (plain `npm install --force` is not enough — npm's os/cpu filters drop foreign packages and can prune siblings). ```bash # after npm ci on the Linux runner node scripts/ensure-qvac-bare-runtimes.cjs --all # or only hosts you will package node scripts/ensure-qvac-bare-runtimes.cjs --hosts darwin-arm64,darwin-x64,linux-x64,win32-x64 ``` `forge.config.cjs` also: 1. Runs the ensure script in `prePackage` for the target host 2. Copies `bare-runtime` + `bare-runtime-` into the app if packager omitted them 3. **Fails the package** if the Bare binary is still missing when QVAC is enabled Client package default timeout is **20 minutes** when QVAC is enabled (`PEARDATA_CLIENT_TIMEOUT_MS`). ## Key scripts | Script | Role | |--------|------| | `scripts/install.sh` | End-user installer (server systemd + desktop client; journal + Docker socket when detected) | | `scripts/hosts.cjs` | Host lists (`SERVER_LINUX`, `ALL_64`) | | `scripts/make.cjs` | Orchestrate server/client builds | | `scripts/bare-standalone.cjs` | Pack Bare `peardata-server` | | `scripts/build-client-bundle.cjs` | esbuild GUI → `electron/app.bundle.cjs` | | `scripts/build-qvac-worker.cjs` | Local `bundleSdk` for QVAC Bare worker | | `scripts/ensure-qvac-bare-runtimes.cjs` | Force-install `bare-runtime-` for cross-host QVAC clients | | `scripts/predownload-electron.cjs` | Pre-fetch Electron zips for CI | | `scripts/sign-macos-app.cjs` | Darwin client codesign | | `scripts/gitea-rolling-release.sh` | Build + stage + Gitea upload | | `forge.config.cjs` | Electron Forge + optional `QvacForgePlugin` | ## Integration tests in CI Release workflows set `SKIP_INTEGRATION=1`. Unit CI may run integration when the runner allows DHT/UDP. See [TESTING.md](./TESTING.md). ## Related - [RELEASE.md](./RELEASE.md) - [TECH-CHOICES.md](./TECH-CHOICES.md) — Bare import maps