Linux Release #8
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Linux Release | |
| on: | |
| release: | |
| types: [published] | |
| # Manual test build: builds and scans both variants on the self-hosted runner | |
| # but does NOT upload (no release to attach to). Lets you validate the runner | |
| # toolchain and the CUDA build without cutting a release tag. | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: "Version for the test build (must be valid PEP 440, e.g. 0.0.0)" | |
| required: false | |
| default: "0.0.0" | |
| permissions: {} | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| build-and-upload: | |
| # Runner must be linux/x64 with rustup, Node.js, Docker, and sudo apt access. | |
| runs-on: [self-hosted, linux, x64] | |
| timeout-minutes: 90 | |
| permissions: | |
| contents: write | |
| defaults: | |
| run: | |
| shell: bash | |
| steps: | |
| - name: clean workspace | |
| run: rm -rf .build dist | |
| - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | |
| - name: install build dependencies | |
| env: | |
| DEBIAN_FRONTEND: noninteractive | |
| run: | | |
| # Tauri v2 build deps + tooling. webkit2gtk-4.1 matches the tauri = "2" | |
| # crate; ffmpeg is a runtime dependency, not bundled. | |
| # | |
| # The self-hosted runner does NOT have passwordless sudo, so a bare | |
| # `sudo apt-get` hangs forever at the password prompt. These packages are | |
| # already installed on the persistent runner, so check first (dpkg needs | |
| # no sudo) and skip apt entirely when everything is present. Only if a | |
| # package is genuinely missing do we touch apt, via `sudo -n` (fails fast | |
| # instead of prompting) so a release never hangs on sudo again. | |
| PKGS="build-essential curl file wget libssl-dev libxdo-dev \ | |
| libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev" | |
| missing="" | |
| for p in $PKGS; do | |
| dpkg -s "$p" >/dev/null 2>&1 || missing="$missing $p" | |
| done | |
| if [ -z "$missing" ]; then | |
| echo "All build dependencies already installed; skipping apt." | |
| exit 0 | |
| fi | |
| echo "Missing packages:$missing" | |
| if ! sudo -n true 2>/dev/null; then | |
| echo "ERROR: build deps missing and passwordless sudo is not available." >&2 | |
| echo "Install on the runner: sudo apt-get install -y$missing" >&2 | |
| exit 1 | |
| fi | |
| sudo -n apt-get update -o DPkg::Lock::Timeout=120 | |
| sudo -n apt-get install -y --no-install-recommends -o DPkg::Lock::Timeout=120 $missing | |
| - name: install uv | |
| run: | | |
| curl -LsSf https://astral.sh/uv/install.sh | sh | |
| echo "$HOME/.local/bin" >> "$GITHUB_PATH" | |
| - name: install rust toolchain | |
| run: rustup default stable | |
| - name: write version files | |
| env: | |
| DISPATCH_VERSION: ${{ inputs.version }} | |
| # github.ref_name (evaluated by Actions) instead of $GITHUB_REF_NAME, | |
| # which is only injected by runner >= 2.290 — empty on older | |
| # self-hosted runners. | |
| REF_NAME: ${{ github.ref_name }} | |
| run: | | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| VERSION="${DISPATCH_VERSION#v}" | |
| else | |
| VERSION="${REF_NAME#v}" | |
| fi | |
| if [ -z "$VERSION" ]; then | |
| echo "Could not determine a version" >&2 | |
| exit 1 | |
| fi | |
| printf '{ "version": "%s" }\n' "$VERSION" > static/version.json | |
| sed -i "s/^version = \".*\"/version = \"$VERSION\"/" desktop/src-tauri/Cargo.toml | |
| sed -i "s/\"version\": \"[^\"]*\"/\"version\": \"$VERSION\"/" desktop/src-tauri/tauri.conf.json | |
| sed -i "s/^version = \".*\"/version = \"$VERSION\"/" pyproject.toml | |
| sed -i "s/\"version\": \"[^\"]*\"/\"version\": \"$VERSION\"/" desktop/package.json | |
| echo "VERSION=$VERSION" >> "$GITHUB_ENV" | |
| echo "Wrote version $VERSION to all version files" | |
| - name: build Linux CPU | |
| run: | | |
| PACKAGE_NAME=StemDeck-Linux-x64 \ | |
| PACKAGE_VERSION="$VERSION" \ | |
| bash scripts/linux/make-portable.sh | |
| # Drop the uncompressed stage but keep the tarball; frees disk for the | |
| # larger NVIDIA build. The Tauri binary under target/ is preserved. | |
| rm -rf dist/StemDeck-Linux-x64 | |
| - name: build Linux NVIDIA | |
| run: | | |
| # CPU_ONLY=0 keeps the CUDA torch wheel; SKIP_TAURI_BUILD=1 reuses the | |
| # binary built in the CPU step (identical for both variants). | |
| PACKAGE_NAME=StemDeck-Linux-x64.NVIDIA \ | |
| PACKAGE_VERSION="$VERSION" \ | |
| CPU_ONLY=0 \ | |
| SKIP_TAURI_BUILD=1 \ | |
| bash scripts/linux/make-portable.sh | |
| rm -rf dist/StemDeck-Linux-x64.NVIDIA | |
| - name: scan artifacts | |
| run: | | |
| echo "Artifacts staged in: $PWD/dist" | |
| ls -lh dist | |
| echo "SHA256 checksums:" | |
| ( cd dist && sha256sum *.tar.gz ) | |
| echo "Pulling latest ClamAV scanner image..." | |
| docker pull clamav/clamav:latest | |
| echo "Running ClamAV scan over dist/..." | |
| docker run --rm -v "$PWD/dist:/scan:ro" clamav/clamav:latest \ | |
| clamscan --recursive --infected --bell /scan | |
| echo "ClamAV scan completed successfully. No infected files reported." | |
| - name: upload artifacts | |
| # Only attach to a real release; a manual test build has nothing to upload to. | |
| if: github.event_name == 'release' | |
| uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0 | |
| with: | |
| files: | | |
| dist/StemDeck-Linux-x64.tar.gz | |
| dist/StemDeck-Linux-x64.tar.gz.sha256 | |
| dist/StemDeck-Linux-x64.NVIDIA.tar.gz | |
| dist/StemDeck-Linux-x64.NVIDIA.tar.gz.sha256 | |
| append_body: true | |
| body: | | |
| ### Artifact scan | |
| - The Linux portable packages (CPU and NVIDIA) were scanned with ClamAV in CI before upload. |