|
| 1 | +# SPDX-FileCopyrightText: 2026 VisorCraft LLC |
| 2 | +# SPDX-License-Identifier: GPL-3.0-only |
| 3 | +# |
| 4 | +# Per-job CI layout. Each gate runs in its own runner |
| 5 | +# so failures pinpoint quickly and the build job (which needs Arch's |
| 6 | +# fresher Kirigami) doesn't block the faster checks. Workflow-level |
| 7 | +# interpolations are limited to `env:` values, which are safe against |
| 8 | +# shell injection from PR titles, branch names, or commit messages. |
| 9 | + |
| 10 | +name: ci |
| 11 | + |
| 12 | +on: |
| 13 | + push: |
| 14 | + branches: [master] |
| 15 | + pull_request: |
| 16 | + branches: [master] |
| 17 | + |
| 18 | +env: |
| 19 | + CARGO_TERM_COLOR: always |
| 20 | + RUST_BACKTRACE: 1 |
| 21 | + # cxx-qt's build script picks Qt up via qmake6 on $PATH. |
| 22 | + QMAKE: /usr/bin/qmake6 |
| 23 | + |
| 24 | +jobs: |
| 25 | + fmt: |
| 26 | + name: rustfmt |
| 27 | + runs-on: ubuntu-latest |
| 28 | + steps: |
| 29 | + - uses: actions/checkout@v6 |
| 30 | + - uses: dtolnay/rust-toolchain@stable |
| 31 | + with: |
| 32 | + components: rustfmt |
| 33 | + - run: cargo fmt --all -- --check |
| 34 | + |
| 35 | + lint: |
| 36 | + name: clippy |
| 37 | + runs-on: ubuntu-latest |
| 38 | + steps: |
| 39 | + - uses: actions/checkout@v6 |
| 40 | + - name: Install Qt 6 + C++ toolchain + mold/sccache |
| 41 | + run: | |
| 42 | + sudo apt-get update |
| 43 | + sudo apt-get install -y --no-install-recommends \ |
| 44 | + qt6-base-dev \ |
| 45 | + qt6-declarative-dev \ |
| 46 | + qt6-tools-dev \ |
| 47 | + libgl1-mesa-dev \ |
| 48 | + clang \ |
| 49 | + mold \ |
| 50 | + sccache \ |
| 51 | + ninja-build |
| 52 | + - uses: dtolnay/rust-toolchain@stable |
| 53 | + with: |
| 54 | + components: clippy |
| 55 | + - uses: Swatinem/rust-cache@v2 |
| 56 | + - run: cargo clippy --workspace --all-targets -- -D warnings |
| 57 | + |
| 58 | + test: |
| 59 | + name: test (stable) |
| 60 | + runs-on: ubuntu-latest |
| 61 | + steps: |
| 62 | + - uses: actions/checkout@v6 |
| 63 | + - name: Install Qt 6 + runtime + C++ toolchain + mold/sccache |
| 64 | + run: | |
| 65 | + sudo apt-get update |
| 66 | + sudo apt-get install -y --no-install-recommends \ |
| 67 | + qt6-base-dev \ |
| 68 | + qt6-declarative-dev \ |
| 69 | + qt6-tools-dev \ |
| 70 | + libgl1-mesa-dev \ |
| 71 | + clang \ |
| 72 | + mold \ |
| 73 | + sccache \ |
| 74 | + ninja-build |
| 75 | + - uses: dtolnay/rust-toolchain@stable |
| 76 | + - uses: Swatinem/rust-cache@v2 |
| 77 | + - run: cargo test --workspace |
| 78 | + env: |
| 79 | + # The Landlock/seccomp enforcement tests assert kernel behavior that |
| 80 | + # shared GitHub runners (and some dev/CI shells) do not reliably provide: |
| 81 | + # Landlock reports ABI >= 1 (detection succeeds), but the fs read |
| 82 | + # restriction can be a no-op (e.g. namespaces, container policy). |
| 83 | + # `cat_denied_outside_allowed_path` would flake. Per the contract in |
| 84 | + # crates/linsync-sandbox/tests/sandbox_integration.rs (and Justfile |
| 85 | + # test recipe), the standard CI job + `just ci`/`just test` run with |
| 86 | + # LINSYNC_SANDBOX_SKIP=1. Real enforcement is exercised on a good |
| 87 | + # kernel+env by: env -u LINSYNC_SANDBOX_SKIP cargo test -p linsync-sandbox --test sandbox_integration |
| 88 | + LINSYNC_SANDBOX_SKIP: "1" |
| 89 | + |
| 90 | + sandbox-stress: |
| 91 | + name: sandbox enforcement (best-effort) |
| 92 | + runs-on: ubuntu-latest |
| 93 | + continue-on-error: true |
| 94 | + # This job does not install mold/sccache; disable the workspace's |
| 95 | + # forced wrapper/linker settings so the stock ubuntu-latest toolchain |
| 96 | + # can compile and run the sandbox integration tests. |
| 97 | + env: |
| 98 | + CARGO_BUILD_RUSTC_WRAPPER: "" |
| 99 | + RUSTFLAGS: "" |
| 100 | + steps: |
| 101 | + - uses: actions/checkout@v6 |
| 102 | + - uses: dtolnay/rust-toolchain@stable |
| 103 | + - uses: Swatinem/rust-cache@v2 |
| 104 | + - run: cargo test -p linsync-sandbox --test sandbox_integration |
| 105 | + |
| 106 | + build: |
| 107 | + name: build + smoke GUI (Arch) |
| 108 | + runs-on: ubuntu-latest |
| 109 | + container: |
| 110 | + image: archlinux:base-devel |
| 111 | + steps: |
| 112 | + - name: Install Arch Qt 6 + Kirigami dependencies |
| 113 | + run: | |
| 114 | + pacman -Sy --noconfirm --needed archlinux-keyring |
| 115 | + pacman -Syu --noconfirm --needed \ |
| 116 | + git \ |
| 117 | + curl \ |
| 118 | + ca-certificates \ |
| 119 | + pkgconf \ |
| 120 | + qt6-base \ |
| 121 | + qt6-declarative \ |
| 122 | + qt6-tools \ |
| 123 | + kirigami \ |
| 124 | + clang \ |
| 125 | + mold \ |
| 126 | + sccache \ |
| 127 | + ninja \ |
| 128 | + mesa |
| 129 | + - uses: actions/checkout@v6 |
| 130 | + - uses: dtolnay/rust-toolchain@stable |
| 131 | + - uses: Swatinem/rust-cache@v2 |
| 132 | + - name: Build GUI binary (cxx-qt in-process host) |
| 133 | + env: |
| 134 | + QT_VERSION_MAJOR: "6" |
| 135 | + run: | |
| 136 | + cargo build -p linsync --release \ |
| 137 | + --features 'linsync/cxxqt linsync/cxxqt-app' |
| 138 | + - name: GUI smoke (external QML host) |
| 139 | + env: |
| 140 | + QT_QPA_PLATFORM: offscreen |
| 141 | + run: bash scripts/gui-smoke.sh |
| 142 | + - name: GUI smoke (in-process cxx-qt host) |
| 143 | + env: |
| 144 | + QT_QPA_PLATFORM: offscreen |
| 145 | + run: LINSYNC_GUI_SMOKE_CXXQT=1 bash scripts/gui-smoke.sh |
| 146 | + |
| 147 | + screenshots: |
| 148 | + name: GUI screenshots |
| 149 | + runs-on: ubuntu-latest |
| 150 | + container: |
| 151 | + image: archlinux:base-devel |
| 152 | + needs: build |
| 153 | + env: |
| 154 | + QT_QPA_PLATFORM: offscreen |
| 155 | + steps: |
| 156 | + - name: Install Arch Qt 6 + Kirigami + screenshot dependencies |
| 157 | + run: | |
| 158 | + pacman -Sy --noconfirm --needed archlinux-keyring |
| 159 | + pacman -Syu --noconfirm --needed \ |
| 160 | + git \ |
| 161 | + curl \ |
| 162 | + ca-certificates \ |
| 163 | + pkgconf \ |
| 164 | + qt6-base \ |
| 165 | + qt6-declarative \ |
| 166 | + qt6-tools \ |
| 167 | + kirigami \ |
| 168 | + clang \ |
| 169 | + mold \ |
| 170 | + sccache \ |
| 171 | + ninja \ |
| 172 | + mesa \ |
| 173 | + xorg-server-xvfb \ |
| 174 | + imagemagick |
| 175 | + - uses: actions/checkout@v6 |
| 176 | + - uses: dtolnay/rust-toolchain@stable |
| 177 | + - uses: Swatinem/rust-cache@v2 |
| 178 | + - name: Build GUI binary |
| 179 | + env: |
| 180 | + QT_VERSION_MAJOR: "6" |
| 181 | + run: | |
| 182 | + cargo build -p linsync --release \ |
| 183 | + --features 'linsync/cxxqt linsync/cxxqt-app' |
| 184 | + - name: Capture screenshots |
| 185 | + env: |
| 186 | + LINSYNC_BIN: target/release/linsync |
| 187 | + STARTUP_SLEEP: "6" |
| 188 | + run: bash scripts/gui-screenshot.sh |
| 189 | + - name: Upload screenshots |
| 190 | + if: always() |
| 191 | + uses: actions/upload-artifact@v7 |
| 192 | + with: |
| 193 | + name: linsync-screenshots-${{ github.sha }} |
| 194 | + path: target/screenshots/*.png |
| 195 | + |
| 196 | + a11y-check: |
| 197 | + name: accessibility label grep |
| 198 | + runs-on: ubuntu-latest |
| 199 | + steps: |
| 200 | + - uses: actions/checkout@v6 |
| 201 | + - name: Check every Button/Switch/SpinBox/ComboBox/TextField has a label |
| 202 | + run: bash scripts/gui-smoke.sh --check-a11y |
| 203 | + |
| 204 | + docs-drift-check: |
| 205 | + name: docs drift check (advisory) |
| 206 | + runs-on: ubuntu-latest |
| 207 | + if: github.event_name == 'pull_request' |
| 208 | + steps: |
| 209 | + - uses: actions/checkout@v6 |
| 210 | + with: |
| 211 | + fetch-depth: 0 |
| 212 | + - name: Check for feature-matrix / known-limitations drift |
| 213 | + env: |
| 214 | + GITHUB_BASE_REF: ${{ github.base_ref }} |
| 215 | + PR_TITLE: ${{ github.event.pull_request.title }} |
| 216 | + run: bash scripts/check-docs-drift.sh |
| 217 | + |
| 218 | + deny: |
| 219 | + name: cargo-deny |
| 220 | + runs-on: ubuntu-latest |
| 221 | + # cargo-deny-action installs its own musl toolchain. Disable the |
| 222 | + # workspace's sccache wrapping (force = true in .cargo/config.toml) |
| 223 | + # for this job — the wrapper isn't available on the action's |
| 224 | + # toolchain path. RUSTFLAGS is similarly cleared so mold isn't a |
| 225 | + # hard requirement. |
| 226 | + env: |
| 227 | + CARGO_BUILD_RUSTC_WRAPPER: "" |
| 228 | + RUSTFLAGS: "" |
| 229 | + CC: cc |
| 230 | + CXX: c++ |
| 231 | + steps: |
| 232 | + - uses: actions/checkout@v6 |
| 233 | + - uses: dtolnay/rust-toolchain@stable |
| 234 | + - uses: EmbarkStudios/cargo-deny-action@v2 |
| 235 | + |
| 236 | + audit: |
| 237 | + name: cargo-audit |
| 238 | + runs-on: ubuntu-latest |
| 239 | + steps: |
| 240 | + - uses: actions/checkout@v6 |
| 241 | + - uses: dtolnay/rust-toolchain@stable |
| 242 | + - name: Install cargo-audit |
| 243 | + run: cargo install cargo-audit --locked |
| 244 | + - run: cargo audit |
| 245 | + |
| 246 | + appstream-validate: |
| 247 | + name: appstream metadata |
| 248 | + runs-on: ubuntu-latest |
| 249 | + steps: |
| 250 | + - uses: actions/checkout@v6 |
| 251 | + - run: sudo apt-get update && sudo apt-get install -y appstream |
| 252 | + - run: appstreamcli validate --no-net packaging/com.visorcraft.LinSync.metainfo.xml |
| 253 | + |
| 254 | + desktop-validate: |
| 255 | + name: desktop file |
| 256 | + runs-on: ubuntu-latest |
| 257 | + steps: |
| 258 | + - uses: actions/checkout@v6 |
| 259 | + - run: sudo apt-get update && sudo apt-get install -y desktop-file-utils |
| 260 | + - run: desktop-file-validate packaging/com.visorcraft.LinSync.desktop |
| 261 | + |
| 262 | + # NOTE: Flatpak CI is currently disabled. |
| 263 | + # bilelmoussaoui/flatpak-github-actions only publishes kde-5.15 tags |
| 264 | + # (verified 2026-05-26); there's no KDE 6 base image. The Flatpak |
| 265 | + # manifest pins org.kde.Platform//6.10 at *runtime* but the CI host |
| 266 | + # needs a matching base. To re-enable, either: |
| 267 | + # 1. Set up flatpak-builder on a stock ubuntu-latest runner with |
| 268 | + # apt-get install flatpak flatpak-builder and `flatpak install` |
| 269 | + # org.kde.Platform//6.10 from flathub, OR |
| 270 | + # 2. Use a published flatpak-builder image that has KDE 6 SDK. |
| 271 | + # Local builds via `just flatpak-bundle` continue to work for now. |
0 commit comments