Repository navigation
Continuous Integration
SnipVault ships with three CI pipeline definitions. All build the same installers; pick whichever matches where you host.
| File | System | Builds |
|---|---|---|
.github/workflows/release.yml |
GitHub Actions (hosted) | Windows + macOS (Intel & Apple Silicon) |
.gitlab-ci.yml |
GitLab CI (self-hosted) | Windows + macOS (universal) + Linux, plus a GitLab Release |
Jenkinsfile |
Jenkins (self-hosted) | Windows + macOS (universal) + Linux in parallel |
The macOS constraint applies everywhere: Tauri cannot cross-compile, so a macOS
.dmgmust be built on real macOS hardware, a Windows installer on Windows, and Linux bundles on Linux. CI just orchestrates one machine per platform.
.github/workflows/release.yml runs on version tags (v*) and manual dispatch. It has three jobs:
-
create-release— reads the version fromtauri.conf.jsonand creates a single draft release forv<version>, outputting its release id. -
build— a matrix that compiles each platform and uploads its bundles to that shared release id:-
windows-latest→.msi+.exe -
macos-latestwith--target aarch64-apple-darwin→ Apple Silicon.dmg -
macos-latestwith--target x86_64-apple-darwin→ Intel.dmg
-
-
publish— un-drafts the release and marks it latest.
The build matrix uses pnpm/action-setup, Node 22 (pnpm 11 needs ≥ 22.13), dtolnay/rust-toolchain, a Rust cache, then tauri-apps/tauri-action. Each build job receives the updater signing secrets (below), so it signs the update artifacts and contributes to a latest.json manifest that the in-app updater reads.
Why create the release first? If the matrix jobs each let
tauri-actioncreate the release, they race — GitHub's "find release by tag" lookup ignores drafts, so every parallel job spawns its own draft, splitting the installers across releases and leaving eachlatest.jsonmissing the other platforms. Creating one draft up front and passing itsreleaseIdto every job makes all platforms land on the same release, solatest.jsonmerges correctly. The finalpublishjob then un-drafts it — no manual step needed.
Release:
git tag v1.2.0 && git push --follow-tags
# or trigger manually:
gh workflow run release.yml --ref mainThe workflow publishes the release automatically once all platforms build; there's no manual "un-draft" step.
A separate workflow, .github/workflows/docker-image.yml, builds and publishes the sync server container (the SnipVault web app; see Syncing) to the GitHub Container Registry at ghcr.io/franciszekryszka/snippet-vault.
- It runs on version tags (
v*) and manual dispatch, usingdocker/build-push-action+docker/metadata-action. - A real tag push mints both
:X.Y.Z(viatype=semver) and:latest; aworkflow_dispatchfrommainonly updates:latest. - The image name is lowercased (registries require it). Package visibility is set once in the GitHub web UI — there's no REST/CLI endpoint for it.
This is independent of the desktop installer build above — a tag push triggers both workflows.
.github/workflows/test.yml runs the JS/TS test suites on every push to main and every pull request — separate from the installer builds above, which only run on tags. Two jobs:
-
unit—vitestover the pure API validators and the prompt-variable parser inlib/(fast; no server or database). -
api— builds the server bundle, boots it on a throwaway SQLite database (via theSNIPVAULT_DB_PATHoverride), and runs a dependency-freenode:testHTTP integration suite against it: the auth gate, CRUD, the newest-wins sync merge, Trash, and library round-trips.
Run both locally with pnpm test (or pnpm test:unit / pnpm test:api individually). These complement the Rust tests (cargo test in src-tauri/) and the dependency Security audit workflow (.github/workflows/audit.yml — pnpm audit + cargo audit, which waives build-time-only transitive advisories via pnpm-workspace.yaml and still fails on runtime-reachable ones).
Both target three machines, each registered as a runner/agent with a tag/label:
| Tag / label | Machine role | Output |
|---|---|---|
windows |
Windows host |
.msi + .exe
|
macos |
macOS host | universal .dmg (Intel + Apple Silicon) |
linux |
Linux host |
.deb / .AppImage / .rpm
|
Every runner needs Node ≥ 22 and Rust installed, plus its OS's Tauri prerequisites (see Development Setup). The macOS runner builds a universal binary via rustup target add aarch64-apple-darwin x86_64-apple-darwin + --target universal-apple-darwin.
- Build jobs run on tags and manual ("web") pipelines; broaden the
.baserulesto build on every push. - On a tag, each build job uploads its installer to the project's generic package registry and records an asset-link line.
- A final
releasejob (on thelinuxrunner) downloadsrelease-cliand creates a GitLab Release linking every installer. - If your Linux runner has no outbound internet, pre-install
release-cliand replace the download step with its path. - Validate edits with CI/CD → Editor → Lint in your GitLab project.
- A declarative pipeline with a
parallelblock — Windows (bat), macOS + Linux (sh). - Installers are archived per node via
archiveArtifacts. - Extend the
post { success { … } }blocks to publish to a release target if desired.
Published binaries are unsigned, so end users see first-launch warnings (see Installation). To sign:
- macOS — an Apple Developer ID certificate + notarization. Tauri supports signing env vars in the build step.
- Windows — an OV/EV code-signing certificate.
Store certificates as CI secrets and wire them into the respective build step. This is optional and only affects the OS trust prompts, not functionality.
Separate from the OS code signing above, the in-app auto-updater (added in v1.4.0) requires its own signature so the app only accepts genuine updates. This is a free Tauri/minisign keypair, unrelated to Apple/Windows certificates.
-
Public key lives in
src-tauri/tauri.conf.jsonunderplugins.updater.pubkey; the updater endpoint points at the release'slatest.json. -
Private key + password are stored as the GitHub secrets
TAURI_SIGNING_PRIVATE_KEYandTAURI_SIGNING_PRIVATE_KEY_PASSWORD, passed totauri-actioninrelease.yml. -
bundle.createUpdaterArtifactsis enabled so signed update bundles +latest.jsonare produced and uploaded to the Release. - Generate a keypair with
pnpm tauri signer generate. Keep the private key safe — if it's lost, no future release can be signed and already-installed apps (which trust the old public key) can no longer auto-update. - To reproduce this on the self-hosted GitLab/Jenkins pipelines, expose the same two variables to the build job.
Using SnipVault
Development
Operations