name: Linux egui build and backend checks # CI and independent manual Linux builds share this deb/rpm verification path. # No standalone push trigger: a Linux PR must show one combined Linux check. on: workflow_call: inputs: # 'none' skips a change set that is only prose (see # scripts/ci-changed-areas.sh): the callers that must always build the # packages, including accepted tag builds, leave the default in place. scope: required: false type: string default: full permissions: contents: read concurrency: group: linux-egui-build-${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: build-linux-egui: # PipeWire/libspa in this host needs 24.04. Packages target glibc >= 2.39. runs-on: ubuntu-24.04 # A cold dependency tree costs ~18 minutes; anything past this is a hang. timeout-minutes: 90 env: RELEASE_TAG: ${{ github.ref_type == 'tag' && startsWith(github.ref, 'refs/tags/v') && endsWith(github.ref, '-tauri') && github.ref_name || '' }} # Debug info dominates both compile time and cache size for this host, and # the gate below never reads it. The Linux job keeps its own cache so a # red run still starts warm. CARGO_PROFILE_DEV_DEBUG: 0 CARGO_PROFILE_TEST_DEBUG: 0 steps: - uses: actions/checkout@v6 with: submodules: false - name: Install native build and packaging dependencies if: inputs.scope != 'none' run: | sudo add-apt-repository -y universe || true sudo apt-get update sudo apt-get install -y \ appstream build-essential cmake cpio desktop-file-utils \ fcitx5-modules-dev file libasound2-dev libdbus-1-dev \ libfcitx5config-dev libfcitx5core-dev libfcitx5utils-dev \ liblzma-dev libpipewire-0.3-dev libpulse-dev libssl-dev \ libwayland-dev libx11-dev libxkbcommon-dev pkg-config ripgrep rpm - uses: dtolnay/rust-toolchain@stable with: components: clippy if: inputs.scope != 'none' - uses: actions/setup-node@v4 with: node-version: '22' if: inputs.scope != 'none' - name: Resolve package version and validate release tag id: version working-directory: openless-all/app env: VERSION_TAG: ${{ env.RELEASE_TAG }} run: | set -euo pipefail VERSION=$(node -p "require('./package.json').version") if [ -n "$VERSION_TAG" ]; then TAG_VERSION=${VERSION_TAG#v} TAG_VERSION=${TAG_VERSION%-tauri} if [ "${VERSION%%+*}" != "$TAG_VERSION" ]; then echo "::error::tag $VERSION_TAG does not match package.json version $VERSION" exit 1 fi fi # Linux packages use the same explicitly incremented -N revision as # earlier Beta.2-78/-79 builds. Keep it separate from the Tauri tag. REVISION=$(< linux-egui/package-revision) [[ "$REVISION" =~ ^[1-9][0-9]*$ ]] echo "version=${VERSION%%+*}-$REVISION" >> "$GITHUB_OUTPUT" - uses: swatinem/rust-cache@v2 if: inputs.scope != 'none' with: workspaces: 'openless-all/app -> target' # Saving after a failed test step keeps the next push incremental # instead of repeating a cold build of the whole dependency tree. cache-on-failure: true - name: Build and test fcitx5 plugin if: inputs.scope != 'none' working-directory: openless-all/scripts/linux-fcitx5-plugin run: | cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr cmake --build build --parallel ctest --test-dir build --output-on-failure test -s build/libopenless.so test -s build/openless.conf - name: Test Core, Linux host, and platform boundaries if: inputs.scope != 'none' working-directory: openless-all/app run: | cargo test --locked -p openless-core cargo clippy --locked -p openless-core --all-targets -- -D warnings cargo test --locked -p openless-linux-egui --all-targets cargo clippy --locked -p openless-linux-egui --all-targets -- -D warnings cargo check --locked -p openless-linux-egui --all-targets node scripts/linux-egui-tauri-free-contract.test.mjs node scripts/linux-egui-release-contract.test.mjs node scripts/ci-changed-areas.test.mjs node scripts/tauri-linux-free-contract.test.mjs pwsh -NoProfile -File ./scripts/check-core-deps.ps1 pwsh -NoProfile -File ./scripts/check-core-deps.ps1 openless-linux-egui pwsh -NoProfile -File ./scripts/check-core-secret-surface.ps1 pwsh -NoProfile -File ./scripts/check-core-test-isolation.ps1 pwsh -NoProfile -File ./scripts/check-core-runtime-seam.ps1 pwsh -NoProfile -File ./scripts/check-linux-public-surface.ps1 - name: Test remote input TLS identity and Unix key permissions if: inputs.scope != 'none' working-directory: openless-all/app run: cargo test --locked --manifest-path src-tauri/backend-tests/Cargo.toml --test remote_tls - name: Validate Linux desktop metadata if: inputs.scope != 'none' run: | desktop-file-validate openless-all/app/linux-egui/packaging/openless.desktop appstreamcli validate --no-net openless-all/app/linux-egui/packaging/top.openless.OpenLess.metainfo.xml - name: Build Linux egui host if: inputs.scope != 'none' working-directory: openless-all/app run: cargo build --locked --release -p openless-linux-egui - name: Package deb and rpm without Tauri if: inputs.scope != 'none' working-directory: openless-all/app env: OPENLESS_LINUX_VERSION: ${{ steps.version.outputs.version }} OPENLESS_LINUX_ARCH: x86_64 run: | # CI runners are disposable. Ignore old outputs restored by a cache. rm -f target/linux-egui-packages/*.deb target/linux-egui-packages/*.rpm \ target/linux-egui-packages/SHA256SUMS bash scripts/package-linux-egui.sh - name: Generate checksums and verify distributable packages if: inputs.scope != 'none' working-directory: openless-all/app run: | set -euo pipefail OUTPUT=target/linux-egui-packages ( cd "$OUTPUT" && sha256sum ./*.deb ./*.rpm > SHA256SUMS ) bash scripts/verify-linux-egui-packages.sh - name: Upload verified Linux packages as workflow artifacts # Push builds keep the release path warm but do not publish artifacts; # the tag release and pull request runs are the consumers. if: inputs.scope != 'none' && (github.event_name != 'push' || env.RELEASE_TAG != '') uses: actions/upload-artifact@v7 with: name: openless-linux-egui-x86_64 path: openless-all/app/target/linux-egui-packages/* if-no-files-found: error