Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ jobs:
# 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
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
scan-type: fs
scan-ref: .
Expand All @@ -190,7 +190,7 @@ jobs:
skip-dirs: .venv,jobs
# Dedicated Dockerfile + compose static analysis (Trivy's IaC linter).
- name: trivy config
uses: aquasecurity/trivy-action@master
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
with:
scan-type: config
scan-ref: build/
Expand Down
19 changes: 19 additions & 0 deletions .github/workflows/linux-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,24 @@ jobs:
clamscan --recursive --infected --bell /scan
echo "ClamAV scan completed successfully. No infected files reported."

# macos-release.yml asserts its assets exist before uploading; this did
# not. action-gh-release defaults fail_on_unmatched_files to false, so a
# missing updater asset published a release that looked fine and went
# green, and the in-app updater then 404'd for every installed user.
- name: verify every asset exists
if: github.event_name == 'release'
run: |
for f in \
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 \
dist/StemDeck-Linux-x64-app.tar.gz \
dist/StemDeck-Linux-x64-app.tar.gz.sha256 \
dist/StemDeck-Linux-x64-runtime-version.json; do
test -f "$f" || { echo "missing release asset: $f" >&2; exit 1; }
done

- name: upload artifacts
# Only attach to a real release; a manual test build has nothing to upload to.
if: github.event_name == 'release'
Expand All @@ -150,6 +168,7 @@ jobs:
# pushing :latest to GHCR, and makes the in-app updater offer a build
# that was never verified.
prerelease: ${{ github.event.release.prerelease }}
fail_on_unmatched_files: true
files: |
dist/StemDeck-Linux-x64.tar.gz
dist/StemDeck-Linux-x64.tar.gz.sha256
Expand Down
20 changes: 20 additions & 0 deletions .github/workflows/windows-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,25 @@ jobs:
}
Write-Host "ClamAV scan completed successfully. No infected files reported."

# See the same guard in linux-release.yml: action-gh-release silently
# tolerates missing files, so an absent updater asset shipped a green
# release the in-app updater could not use.
- name: verify every asset exists
shell: powershell
run: |
$required = @(
"dist/StemDeck-Windows-x64.NVIDIA.zip",
"dist/StemDeck-Windows-x64.NVIDIA.zip.sha256",
"dist/StemDeck-Windows-x64.zip",
"dist/StemDeck-Windows-x64.zip.sha256",
"dist/StemDeck-Windows-x64-app.zip",
"dist/StemDeck-Windows-x64-app.zip.sha256",
"dist/StemDeck-Windows-x64-runtime-version.json"
)
foreach ($f in $required) {
if (-not (Test-Path $f)) { Write-Error "missing release asset: $f"; exit 1 }
}

- name: upload artifacts
uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2
with:
Expand All @@ -101,6 +120,7 @@ jobs:
# pushing :latest to GHCR, and makes the in-app updater offer a build
# that was never verified.
prerelease: ${{ github.event.release.prerelease }}
fail_on_unmatched_files: true
files: |
dist/StemDeck-Windows-x64.NVIDIA.zip
dist/StemDeck-Windows-x64.NVIDIA.zip.sha256
Expand Down
16 changes: 13 additions & 3 deletions build/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,23 @@ RUN apt-get update \

# deno -- yt-dlp uses it as the JS runtime for YouTube format
# extraction. Staged here so the runner doesn't need curl/unzip.
# Pinned and checksummed, not "latest". Deno is the JS runtime yt-dlp feeds
# YouTube's challenge payload to, so it executes against untrusted input --
# and an unverified binary fetched at build time is a supply-chain hole
# whether or not it is small. The desktop packaging scripts already pin and
# verify QuickJS for exactly this reason; this was the one path that did not.
# Bump DENO_VERSION and both hashes together.
ARG TARGETARCH
ARG DENO_VERSION=v2.9.6
ARG DENO_SHA256_AMD64=394f07f4da2bebe6ce6f1e7ce0fa16429b29b08c35e3fac3fe25972676dff4b2
ARG DENO_SHA256_ARM64=9a46afc6c392c7cd2ff71a31558935545b46408d0e87f7a86908c712721c046e
RUN case "${TARGETARCH:-$(dpkg --print-architecture)}" in \
amd64) DENO_ARCH=x86_64-unknown-linux-gnu ;; \
arm64) DENO_ARCH=aarch64-unknown-linux-gnu ;; \
amd64) DENO_ARCH=x86_64-unknown-linux-gnu; DENO_SHA256="${DENO_SHA256_AMD64}" ;; \
arm64) DENO_ARCH=aarch64-unknown-linux-gnu; DENO_SHA256="${DENO_SHA256_ARM64}" ;; \
*) echo "Unsupported arch: ${TARGETARCH}" && exit 1 ;; \
esac \
&& curl -fsSL -o /tmp/deno.zip "https://github.com/denoland/deno/releases/latest/download/deno-${DENO_ARCH}.zip" \
&& curl -fsSL -o /tmp/deno.zip "https://github.com/denoland/deno/releases/download/${DENO_VERSION}/deno-${DENO_ARCH}.zip" \
&& echo "${DENO_SHA256} /tmp/deno.zip" | sha256sum -c - \
&& unzip /tmp/deno.zip -d /usr/local/bin \
&& rm /tmp/deno.zip \
&& /usr/local/bin/deno --version
Expand Down
19 changes: 19 additions & 0 deletions scripts/windows/make-portable.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,19 @@ param(
)

$ErrorActionPreference = "Stop"
# PowerShell 7+ only. CI invokes this script with `powershell` (Windows
# PowerShell 5.1), where this variable does nothing and $ErrorActionPreference
# does not cover native commands either -- so a failed pip install was ignored
# and the build carried on. Kept for a pwsh run; Assert-LastExitCode below is
# what actually enforces it on 5.1 (#517).
$PSNativeCommandErrorActionPreference = "Stop"

function Assert-LastExitCode {
param([Parameter(Mandatory)][string]$What)
if ($LASTEXITCODE -ne 0) {
throw "$What failed with exit code $LASTEXITCODE"
}
}
Set-StrictMode -Version Latest

if ($env:OS -ne "Windows_NT") {
Expand Down Expand Up @@ -231,15 +243,18 @@ if (Get-Command "py" -ErrorAction SilentlyContinue) {
} else {
& python -m venv $PythonDir
}
Assert-LastExitCode "creating the virtualenv"

& $PythonExe -m pip install --upgrade pip
Assert-LastExitCode "pip self-upgrade"

# The project version is git-derived (hatch-vcs). Pin it from $PackageVersion so
# the install doesn't depend on git tags in the build checkout (#169).
if ($PackageVersion) {
$env:SETUPTOOLS_SCM_PRETEND_VERSION = ($PackageVersion -replace '^v', '')
}
& $PythonExe -m pip install "$Root"
Assert-LastExitCode "installing the StemDeck package"

if ($CpuOnly) {
# Force the slim CPU-only wheel. On Windows the default PyPI torch wheel is
Expand All @@ -249,6 +264,10 @@ if ($CpuOnly) {
& $PythonExe -m pip install torch==2.6.0+cpu torchaudio==2.6.0+cpu `
--index-url https://download.pytorch.org/whl/cpu `
--force-reinstall --no-deps
# Unchecked, a transient network failure here left whatever torch was already
# resolved in place and the zip labelled CPU shipped a non-CPU torch. The
# import checks later still pass, because torch imports fine either way.
Assert-LastExitCode "installing the CPU-only torch wheel"
}
# Do NOT bundle CUDA torch into the NVIDIA (non-CpuOnly) package. It ships base
# torch and the desktop app installs the CUDA build on first run via
Expand Down
Loading