Skip to content

Linux Release

Linux Release #8

Workflow file for this run

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.