Skip to content

CI and releases

CI answers two different questions: did the checks selected for this patch pass? and, after a successful push to main, is there an unpublished binary change? A green first answer does not imply a new release.

For local commands, use Testing. Workflow files and the path classifier are authoritative when changing automation.

ci.yml runs for pushes and pull requests to main, and manual dispatch. It does not use workflow-level path filters that could leave required checks pending.

flowchart TB
    accTitle: CI gates documentation and binary publication separately
    accDescr: Changed paths select CI jobs. The ci-success gate verifies their results. Only a successful same-repository push to main can publish the checked documentation artifact or select an unpublished binary or publishing change for release.
    Changes["Changed paths"] --> Jobs["Selected CI jobs"]
    Jobs --> Gate["ci-success"]
    Gate --> Push{"Successful push to main?"}
    Push -- No --> Done["No publication"]
    Push -- Yes --> Docs["Publish tested site artifact"]
    Push -- Yes --> Select{"Binary or publishing change pending?"}
    Select -- Yes --> Release["Build and publish release"]
    Select -- No --> Done

The last step applies only to successful, same-repository push CI on main. Pull request CI cannot publish a release.

Always-on work includes documentation consistency and the Starlight site build and browser regressions, actionlint, workflow contracts, and classification regressions. The ci-success job runs even after an earlier failure and verifies that every selected gating job succeeded and every unselected job was skipped. Missing outputs, cancellation, unexpected skips, and classification failure fail the gate.

After a push, inspect the run for the exact pushed SHA, not simply the newest green run on the branch. Read coverage and mutation results separately: they are informational and excluded from the aggregate gate.

Checks are selected by input, platform, linter, and application. The table is a guide, not a replacement for the classifier’s exact rules.

Changed inputTypical additional coverage
Docs, skills, known editor/repository metadataAlways-on checks only; the docs selector fixture still compiles Rust
Rust source, embedded assets, Cargo/build/toolchain configurationBoth platform builds, Rust checks, profiles, round-trips, wrappers, and applications
Standalone Rust integration tests/fixturesBoth platform builds and Rust checks, not unrelated application/wrapper jobs
Cargo manifests/lockfilesDependency audit and deny in addition to Rust checks
cli/deny.toml or formatting policyDependency-policy or formatting check respectively
conf/Validation, drift, and profile/round-trip checks; some files also select applications
system/Linux configuration and profile/round-trip checks
packages/ recipesLinux build/Rust checks, config validation/drift, and ShellCheck
Managed application filesConfiguration checks and the affected application/script fixtures
QuickshellPython fixtures on both platforms and Linux offscreen QML
WrappersThe affected platform’s build and wrapper integration
HooksHook and staged-input fixtures; hook-only edits do not request CLI builds
Shell/PowerShell filesThe matching linter in addition to other selected work
CI workflow/shared orchestration, manual dispatch, or unknown inputsConservative full CI
Release workflow/selectionPublication eligibility without unrelated CI compilation

Deletions and both sides of renames count. Invalid Git ranges fail; a valid empty diff runs the always-on checks. Unavailable comparison ranges use conservative full CI instead. Full CI alone does not request publication or mutation testing.

Cargo verification uses the ci profile and --locked. Downstream integration jobs reuse uploaded binaries rather than downloading an unrelated release. Configuration-only Linux checks use config_drift instead of the full Rust suite when full Rust coverage was not selected.

Coverage below is conditional on the relevant job being selected.

BoundaryLinuxWindowsStill not proved
Rust build, Clippy, testsNativeNativeEvery architecture/runtime environment
base / desktop preview and config checkYesYesExternal installs deliberately omitted by those jobs
Update/remove round-tripYesYesEvery pre-existing user configuration
WrappersPOSIXPowerShellEvery network/authentication environment
Application integrationGit, zsh, Vim, NeovimGitFull Windows package and VS Code installation
Managed Python/PowerShell fixturesYesYesA real compositor, network, or lock session
Quickshell QMLOffscreen with mocksNoNative rendering and focus
Full commit-hook suiteYesNo native jobGit-for-Windows hook runtime
Coverage reportsInformationalInformationalCorrectness merely from a coverage percentage
Mutation testingChanged-code shardsNoNative Windows mutation behavior

Profile previews skip VS Code, and profile check runs omit external APM and script analyzers. Windows application integration skips APM, packages, and VS Code. Separate jobs or local checks cover some of those concerns; do not call the profile matrix a full clean-machine installation test.

Mutation jobs run for PR/main-push changes to Rust source under cli/src/, with eight shards covering the applicable changed-code set. Standalone test, lockfile-only, and CI-only changes do not select them. Linux excludes native Windows code and one equivalent mutation; see the mutation testing guide.

dependency-security.yml runs cargo audit and cargo deny check all weekly and on manual dispatch, independently of changed paths. The policy check still runs after an advisory failure unless the workflow is cancelled.

This separate workflow neither satisfies ci-success nor triggers a release. Enable Actions failure notifications and configure Dependabot alerts/security updates in repository settings separately. Check that the schedule remains enabled; GitHub can disable schedules in inactive repositories.

The documentation job checks source links, builds Starlight, checks the output routes and assets, and exercises navigation, search, themes, and diagrams in Chromium. It is part of ci-success, including on docs-only changes.

For a push to main, it uploads the verified static site. After the aggregate succeeds, Publish documentation downloads the artifact from that exact CI run and deploys it to GitHub Pages. PRs and fork runs cannot publish. A completed build that no longer matches main is skipped rather than rolling the site backward.

See Documentation site for Pages settings, domain configuration, local preview, and publishing troubleshooting.

release.yml starts after successful same-repository push CI on main. A read-only selection job compares that exact tested SHA with the latest published, non-draft, non-prerelease release.

Publication is selected for binary or publishing-pipeline changes, or when no release exists. Docs, ordinary configuration, wrappers, standalone test fixtures, and lint-policy files do not by themselves require a new binary. Changes inside cli/src/, including inline tests and embedded assets, are conservatively treated as binary inputs. Unknown inputs also select publication.

The baseline is the last published release, not the previous push. If a binary change passed CI but publication failed, a later docs-only push can finish that pending release. If there is nothing unpublished, it skips instead. Older/already-released commits skip; API errors, missing release tags, and divergent release history fail explicitly.

Once selected, the workflow:

  1. Resolves the version and source commit once for all builds.
  2. Builds Linux x86_64/aarch64 and Windows x86_64 assets without dependency caches.
  3. Generates checksums and build attestations with publishing permissions scoped to the publishing job.
  4. Verifies attestations are retrievable, then publishes.

Release runs are serialized without cancelling an active publisher. Tags use vYYYY.MM.DD-N, where N counts releases on that UTC day; the package version in Cargo.toml is not the release version.

When reporting a release, verify the published tag, commit, and expected assets. A successful CI gate or a running release workflow is not publication. See download verification for the client side.

Keep job conditions, classifier outputs, and aggregate dependencies aligned. Make a new job’s gating or informational status explicit. Use least-privilege permissions, pin Actions to full commit SHAs, and reuse shared helpers under workflow scripts.

Run the local ci stage and the affected job’s focused command. The selection fixtures use temporary Git repositories and mocked release APIs; they do not publish. Preserve tests for both affected and unaffected paths, deletions/renames, and unintended skips.