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@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - 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@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1 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