chore: import upstream snapshot with attribution
CI / lint (push) Has been cancelled
CI / js-syntax (push) Successful in 10m24s
CI / deps-audit (push) Has been cancelled
CI / test (push) Failing after 24m55s
CI / sast-bandit (push) Failing after 11m13s
CI / trivy (push) Failing after 9m32s
Docker Publish / build-and-push (push) Failing after 34m3s

This commit is contained in:
wehub-resource-sync
2026-07-13 12:32:09 +08:00
commit 8f10353f0c
135 changed files with 37786 additions and 0 deletions
+72
View File
@@ -0,0 +1,72 @@
name: Bug report
description: Something isn't working as expected.
title: "[Bug]: "
labels: ["bug"]
body:
- type: markdown
attributes:
value: Thanks for reporting a bug. Please fill in the details so we can reproduce it.
- type: checkboxes
id: preflight
attributes:
label: Before reporting
options:
- label: I'm on the latest release and the issue still happens.
required: true
- label: I searched existing issues and didn't find a duplicate.
required: true
- type: textarea
id: what
attributes:
label: What happened?
description: A clear description of the bug and what you expected instead.
placeholder: When I ..., StemDeck ... but I expected ...
validations:
required: true
- type: textarea
id: steps
attributes:
label: Steps to reproduce
placeholder: |
1. Open StemDeck
2. Import ...
3. Click ...
4. See error
validations:
required: true
- type: dropdown
id: os
attributes:
label: Operating system
options:
- macOS (Apple Silicon)
- macOS (Intel)
- Windows
- Linux
- Other
validations:
required: true
- type: input
id: version
attributes:
label: StemDeck version
description: Shown in the About dialog (Help icon).
placeholder: e.g. v0.7.0-alpha.12
validations:
required: true
- type: dropdown
id: install
attributes:
label: How did you install it?
options:
- macOS DMG
- Windows ZIP
- From source
- Docker / self-hosted
validations:
required: true
- type: textarea
id: extra
attributes:
label: Logs / screenshots
description: Drag in screenshots or a recording, and paste any error text as code.
+11
View File
@@ -0,0 +1,11 @@
blank_issues_enabled: false
contact_links:
- name: Questions & Discussion
url: https://github.com/stemdeckapp/stemdeck/discussions
about: Ask questions, share setups, or discuss ideas with the community.
- name: Discord
url: https://discord.gg/2MVsWqaPRe
about: Chat with the StemDeck community in real time.
- name: Report a security vulnerability
url: https://github.com/stemdeckapp/stemdeck/security/advisories/new
about: Please report security issues privately, not as a public issue.
@@ -0,0 +1,25 @@
name: Feature request
description: Suggest an idea or improvement.
title: "[Feature]: "
labels: ["enhancement"]
body:
- type: markdown
attributes:
value: Thanks for the suggestion! Tell us what you'd like and why.
- type: textarea
id: problem
attributes:
label: What problem does this solve?
description: What are you trying to do that's hard or impossible today?
validations:
required: true
- type: textarea
id: proposal
attributes:
label: Proposed solution
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives considered
+6
View File
@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
+143
View File
@@ -0,0 +1,143 @@
# GitHub Actions: lint + unit tests + security scans.
# Does not build or publish artifacts. Image scanning is done via
# trivy fs on the project tree (covers deps, secrets, and Dockerfile
# misconfig) so CI does not need a docker-in-docker setup.
name: CI
on:
pull_request:
push:
branches: [main]
release:
types: [published]
env:
UV_LINK_MODE: copy
# Version is git-derived (hatch-vcs). CI's clone is shallow/tagless, which
# makes setuptools_scm raise, so pin a placeholder for the build -- CI only
# lints/tests and never publishes. #169
SETUPTOOLS_SCM_PRETEND_VERSION: "0.0.0"
jobs:
lint:
runs-on: ubuntu-latest
container:
image: ghcr.io/astral-sh/uv:python3.12-bookworm-slim
steps:
- uses: actions/checkout@v7
- run: uv sync --frozen --all-extras
- run: uv run ruff check app/ tests/
- run: uv run ruff format --check app/ tests/
- run: bash -n run.sh
test:
runs-on: ubuntu-latest
container:
image: ghcr.io/astral-sh/uv:python3.12-bookworm-slim
steps:
- uses: actions/checkout@v7
- run: apt-get update && apt-get install -y --no-install-recommends ffmpeg
- run: uv sync --frozen --all-extras
- run: uv run pytest tests/ -q
js-syntax:
runs-on: ubuntu-latest
container:
image: node:20-alpine
steps:
- uses: actions/checkout@v7
- run: for f in static/js/*.js; do node --check "$f"; done
sast-bandit:
runs-on: ubuntu-latest
container:
image: ghcr.io/astral-sh/uv:python3.12-bookworm-slim
steps:
- uses: actions/checkout@v7
- run: uv tool install bandit
- run: uv tool run bandit -r app/ -ll # fail on medium+ severity
deps-audit:
runs-on: ubuntu-latest
container:
image: ghcr.io/astral-sh/uv:python3.12-bookworm-slim
steps:
- uses: actions/checkout@v7
- run: uv tool install pip-audit
- run: uv pip compile pyproject.toml -o /tmp/requirements.txt
# Ignored CVEs (review when upgrading torch or demucs):
#
# torch 2.6.0 -- pinned to <2.7 because torchaudio 2.7+ removed its
# built-in audio writer and now requires torchcodec, which has ABI
# issues that break demucs 4.0.1's torchaudio.save() path. All torch
# CVEs below are in ops that StemDeck does not invoke; risk on a
# local-only, single-user app is negligible. Re-evaluate once demucs
# supports torch 2.7+ without torchcodec.
#
# joblib PYSEC-2024-277 -- no fix version available as of 2026-05-21
# (1.5.3 is latest). joblib is a transitive dep via demucs/librosa;
# StemDeck does not directly invoke joblib serialization. Drop once
# a patched release is available.
- run: |
uv tool run pip-audit -r /tmp/requirements.txt --strict \
--ignore-vuln CVE-2025-2953 \
--ignore-vuln CVE-2025-3730 \
--ignore-vuln PYSEC-2025-189 \
--ignore-vuln PYSEC-2025-190 \
--ignore-vuln PYSEC-2025-192 \
--ignore-vuln PYSEC-2025-193 \
--ignore-vuln PYSEC-2025-194 \
--ignore-vuln PYSEC-2025-195 \
--ignore-vuln PYSEC-2025-196 \
--ignore-vuln PYSEC-2025-197 \
--ignore-vuln PYSEC-2025-198 \
--ignore-vuln PYSEC-2025-199 \
--ignore-vuln PYSEC-2025-200 \
--ignore-vuln PYSEC-2025-201 \
--ignore-vuln PYSEC-2025-202 \
--ignore-vuln PYSEC-2025-203 \
--ignore-vuln PYSEC-2025-204 \
--ignore-vuln PYSEC-2025-205 \
--ignore-vuln PYSEC-2025-206 \
--ignore-vuln PYSEC-2025-207 \
--ignore-vuln PYSEC-2025-208 \
--ignore-vuln PYSEC-2025-209 \
--ignore-vuln PYSEC-2025-210 \
--ignore-vuln PYSEC-2026-139 \
--ignore-vuln PYSEC-2024-277 \
--ignore-vuln CVE-2025-2148 \
--ignore-vuln CVE-2025-2149 \
--ignore-vuln CVE-2025-2998 \
--ignore-vuln CVE-2025-2999 \
--ignore-vuln CVE-2025-3000 \
--ignore-vuln CVE-2025-3001
trivy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
# Scans the source tree for: known CVEs in deps, leaked secrets,
# and Dockerfile / compose misconfigurations. Skips .venv (it can
# be left over from earlier steps in the shared workspace; trivy
# would scan its bundled extractor files and flag false-positive
# secrets that ship inside third-party packages like yt-dlp).
- name: trivy fs
uses: aquasecurity/trivy-action@master
with:
scan-type: fs
scan-ref: .
scanners: vuln,secret,misconfig
severity: HIGH,CRITICAL
exit-code: '1'
ignore-unfixed: true
trivyignores: .trivyignore
skip-dirs: .venv,jobs
# Dedicated Dockerfile + compose static analysis (Trivy's IaC linter).
- name: trivy config
uses: aquasecurity/trivy-action@master
with:
scan-type: config
scan-ref: build/
severity: HIGH,CRITICAL
exit-code: '1'
+94
View File
@@ -0,0 +1,94 @@
name: Docker Publish
# Builds the server container (build/Dockerfile) and pushes it to GHCR so it can
# be pulled by self-hosted deployments and the Unraid Community Applications app
# (ghcr.io/stemdeckapp/stemdeck). The image keeps the default Linux x86_64 torch
# wheel, which is the CUDA build -- so the same image runs on CPU by default and
# uses the GPU automatically when started with `--runtime=nvidia` (see the
# Unraid template in templates/unraid/).
on:
# Every merge to main publishes a rolling :edge image. The version is derived
# from git (hatch-vcs style); :edge never clobbers :latest.
push:
branches: [main]
release:
# prereleased: a prerelease is published -> push the version tag only.
# released: a stable release is published, OR a prerelease is promoted to
# a full/Latest release -> push the version tag AND :latest.
# (Listening to these two instead of `published` avoids a double run on a
# stable publish, which fires both `published` and `released`.)
types: [prereleased, released]
# Same as a main push but on demand, from any ref.
workflow_dispatch:
permissions: {}
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
IMAGE: ghcr.io/${{ github.repository_owner }}/stemdeck
jobs:
build-and-push:
runs-on: ubuntu-latest
timeout-minutes: 60
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
# Full history + tags so git describe can derive a version on manual runs.
fetch-depth: 0
# Derive the version fed to the Dockerfile's VERSION build-arg (consumed as
# SETUPTOOLS_SCM_PRETEND_VERSION, so it MUST be PEP 440-valid). On a
# release, use the tag. Otherwise derive from git: `git describe --long`
# yields `<tag>-<N>-g<sha>`, which is NOT PEP 440 -- rewrite the trailing
# `-N-gSHA` into a `+N.gSHA` local segment (e.g. 0.8.0-alpha.5+3.gce86e8a).
- name: resolve version
id: ver
run: |
if [ "${{ github.event_name }}" = "release" ]; then
v="${{ github.event.release.tag_name }}"
else
raw="$(git describe --tags --long 2>/dev/null || echo "0.0.0-0-g$(git rev-parse --short HEAD)")"
v="$(printf '%s' "${raw#v}" | sed -E 's/-([0-9]+)-g([0-9a-f]+)$/+\1.g\2/')"
fi
echo "value=${v#v}" >> "$GITHUB_OUTPUT"
- name: docker metadata (tags/labels)
id: meta
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
with:
images: ${{ env.IMAGE }}
tags: |
type=raw,value=edge,enable=${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }}
type=raw,value=${{ steps.ver.outputs.value }},enable=${{ github.event_name == 'release' }}
type=raw,value=latest,enable=${{ github.event_name == 'release' && github.event.action == 'released' }}
- uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
- name: log in to GHCR
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4.4.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: build and push
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
with:
context: .
file: build/Dockerfile
# Unraid is x86_64; arm64 has no CUDA torch and is not a target.
platforms: linux/amd64
push: true
build-args: |
VERSION=${{ steps.ver.outputs.value }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
provenance: false
+147
View File
@@ -0,0 +1,147 @@
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
+128
View File
@@ -0,0 +1,128 @@
name: macOS Release
on:
release:
types: [published]
permissions: {}
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build-and-upload:
# Runner must be darwin/arm64 with Rosetta 2, Xcode CLT, Homebrew, rustup, and zstd.
runs-on: [self-hosted, macOS, ARM64]
timeout-minutes: 120
permissions:
contents: write
defaults:
run:
shell: bash
steps:
- name: clean workspace
run: rm -rf .build dist
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: write version files
env:
# 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 [ -z "${REF_NAME:-}" ]; then
echo "REF_NAME is not set" >&2
exit 1
fi
VERSION="${REF_NAME#v}"
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 macOS arm64
env:
SSL_CERT_FILE: /etc/ssl/cert.pem
REQUESTS_CA_BUNDLE: /etc/ssl/cert.pem
run: |
command -v zstd
command -v uv >/dev/null 2>&1 || brew install uv
uv python install cpython-3.12-macos-aarch64-none
ARM64_PYTHON="$(uv python find cpython-3.12-macos-aarch64-none)"
scripts/macos/make-iconset.sh
ARCH=arm64 VERSION="$VERSION" PYTHON_BIN="$ARM64_PYTHON" scripts/macos/make-runtime-pack.sh
ARCH=arm64 VERSION="$VERSION" scripts/macos/make-app.sh
ARCH=arm64 VERSION="$VERSION" scripts/macos/make-dmg.sh
- name: build macOS x64
env:
SSL_CERT_FILE: /etc/ssl/cert.pem
REQUESTS_CA_BUNDLE: /etc/ssl/cert.pem
run: |
command -v zstd
if ! arch -x86_64 /usr/bin/true >/dev/null 2>&1; then
echo "ERROR: Rosetta 2 is required to build the x64 runtime on this agent." >&2
exit 1
fi
rustup default stable
rustup target add x86_64-apple-darwin
command -v uv >/dev/null 2>&1 || brew install uv
uv python install cpython-3.12-macos-x86_64-none
X64_PYTHON="$(uv python find cpython-3.12-macos-x86_64-none)"
ARCH=x64 VERSION="$VERSION" PYTHON_BIN="$X64_PYTHON" scripts/macos/make-runtime-pack.sh
ARCH=x64 VERSION="$VERSION" scripts/macos/make-app.sh
ARCH=x64 VERSION="$VERSION" scripts/macos/make-dmg.sh
- name: inspect artifacts
run: |
test -f .build/macos-dist/StemDeck-macOS-arm64.dmg
test -f .build/StemDeck-runtime-macOS-arm64.tar.zst
test -f .build/macos-dist/SHA256SUMS-macOS-arm64.txt
test -f .build/macos-dist/StemDeck-macOS-x64.dmg
test -f .build/StemDeck-runtime-macOS-x64.tar.zst
test -f .build/macos-dist/SHA256SUMS-macOS-x64.txt
cat .build/macos-dist/SHA256SUMS-macOS-arm64.txt
cat .build/macos-dist/SHA256SUMS-macOS-x64.txt
du -sh .build/macos-dist/StemDeck-macOS-arm64.dmg .build/StemDeck-runtime-macOS-arm64.tar.zst
du -sh .build/macos-dist/StemDeck-macOS-x64.dmg .build/StemDeck-runtime-macOS-x64.tar.zst
if find desktop/src-tauri/target/aarch64-apple-darwin/release/bundle/macos/StemDeck.app \
\( -iname '*python*' -o -iname '*torch*' -o -iname '*ffmpeg*' -o -iname '*ffprobe*' \) |
grep -q .; then
echo "arm64 StemDeck.app contains runtime binaries that should stay outside the DMG." >&2
exit 1
fi
if find desktop/src-tauri/target/x86_64-apple-darwin/release/bundle/macos/StemDeck.app \
\( -iname '*python*' -o -iname '*torch*' -o -iname '*ffmpeg*' -o -iname '*ffprobe*' \) |
grep -q .; then
echo "x64 StemDeck.app contains runtime binaries that should stay outside the DMG." >&2
exit 1
fi
for arch in arm64 x64; do
mountpoint="$(mktemp -d /tmp/stemdeck-dmg.XXXXXX)"
hdiutil attach ".build/macos-dist/StemDeck-macOS-$arch.dmg" -readonly -nobrowse -mountpoint "$mountpoint"
trap 'hdiutil detach "$mountpoint" >/dev/null 2>&1 || true; rmdir "$mountpoint" >/dev/null 2>&1 || true' EXIT
test -d "$mountpoint/StemDeck.app"
test -L "$mountpoint/Applications"
test -f "$mountpoint/README-macOS.txt"
test -f "$mountpoint/THIRD_PARTY_NOTICES.txt"
hdiutil detach "$mountpoint"
rmdir "$mountpoint"
trap - EXIT
done
- name: upload artifacts
uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1
with:
files: |
.build/macos-dist/StemDeck-macOS-arm64.dmg
.build/StemDeck-runtime-macOS-arm64.tar.zst
.build/macos-dist/SHA256SUMS-macOS-arm64.txt
.build/macos-dist/StemDeck-macOS-x64.dmg
.build/StemDeck-runtime-macOS-x64.tar.zst
.build/macos-dist/SHA256SUMS-macOS-x64.txt
+103
View File
@@ -0,0 +1,103 @@
name: Windows Release
on:
release:
types: [published]
permissions: {}
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build-and-upload:
# Runner must be windows/x64 with PowerShell, Docker, and rustup.
runs-on: [self-hosted, windows, x64]
timeout-minutes: 90
permissions:
contents: write
# Source the tag from the github context (evaluated by Actions) rather than
# $env:GITHUB_REF_NAME, which is only injected by runner >= 2.290. Keeps the
# build working on older self-hosted runners. (#212 follow-up)
env:
REF_NAME: ${{ github.ref_name }}
defaults:
run:
shell: powershell
steps:
- name: clean workspace
run: |
Remove-Item -Recurse -Force .build, dist -ErrorAction SilentlyContinue
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: write version files
run: |
$tag = $env:REF_NAME
if (-not $tag) { throw "REF_NAME is not set" }
$version = $tag -replace '^v', ''
$json = "{`"version`": `"$version`"}"
Set-Content -Path "static/version.json" -Value $json -Encoding UTF8
(Get-Content "desktop/src-tauri/Cargo.toml") -replace '^version = ".*"', "version = `"$version`"" |
Set-Content "desktop/src-tauri/Cargo.toml"
(Get-Content "desktop/src-tauri/tauri.conf.json") -replace '"version": "[^"]*"', "`"version`": `"$version`"" |
Set-Content "desktop/src-tauri/tauri.conf.json"
(Get-Content "pyproject.toml") -replace '^version = ".*"', "version = `"$version`"" |
Set-Content "pyproject.toml"
(Get-Content "desktop/package.json") -replace '"version": "[^"]*"', "`"version`": `"$version`"" |
Set-Content "desktop/package.json"
Write-Host "Wrote version $version to all version files"
- name: build Windows NVIDIA
run: |
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/windows/make-portable.ps1 `
-PackageName StemDeck-Windows-x64.NVIDIA `
-PackageVersion "$env:REF_NAME" `
-StripVenv
- name: build Windows CPU
run: |
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/windows/make-portable.ps1 `
-PackageName StemDeck-Windows-x64 `
-PackageVersion "$env:REF_NAME" `
-CpuOnly `
-StripVenv
- name: scan artifacts
run: |
Write-Host "Preparing ClamAV scan for Windows release artifacts..."
Write-Host "Artifacts staged in: $PWD\dist"
Get-ChildItem -Path "dist" -File |
Sort-Object Name |
Select-Object Name,
@{Name="SizeMB"; Expression={ [math]::Round($_.Length / 1MB, 2) }} |
Format-Table -AutoSize
Write-Host "SHA256 checksums:"
Get-ChildItem -Path "dist" -Filter "*.zip" -File |
Sort-Object Name |
ForEach-Object {
$hash = Get-FileHash -Algorithm SHA256 $_.FullName
Write-Host " $($hash.Hash) $($_.Name)"
}
Write-Host "Pulling latest ClamAV scanner image..."
docker pull clamav/clamav:latest
Write-Host "Running ClamAV scan over dist/..."
docker run --rm -v "${PWD}/dist:/scan:ro" clamav/clamav:latest `
clamscan --recursive --infected --bell /scan
if ($LASTEXITCODE -ne 0) {
throw "ClamAV scan failed or reported infected files. Exit code: $LASTEXITCODE"
}
Write-Host "ClamAV scan completed successfully. No infected files reported."
- name: upload artifacts
uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1
with:
files: |
dist/StemDeck-Windows-x64.NVIDIA.zip
dist/StemDeck-Windows-x64.NVIDIA.zip.sha256
dist/StemDeck-Windows-x64.zip
dist/StemDeck-Windows-x64.zip.sha256