diff --git a/.woodpecker/ci.yml b/.woodpecker/ci.yml deleted file mode 100644 index 5f683f78..00000000 --- a/.woodpecker/ci.yml +++ /dev/null @@ -1,134 +0,0 @@ -# Woodpecker CI: 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. - -when: - - event: pull_request - - event: push - branch: main - - event: release - -labels: - platform: linux/amd64 - backend: kubernetes - -steps: - lint: - image: ghcr.io/astral-sh/uv:python3.12-bookworm-slim - environment: - 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" - commands: - - uv sync --frozen --all-extras - - uv run ruff check app/ tests/ - - uv run ruff format --check app/ tests/ - - bash -n run.sh - - test: - image: ghcr.io/astral-sh/uv:python3.12-bookworm-slim - environment: - 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" - commands: - - apt-get update && apt-get install -y --no-install-recommends ffmpeg - - uv sync --frozen --all-extras - - uv run pytest tests/ -q - - js-syntax: - image: node:20-alpine - commands: - - for f in static/js/*.js; do node --check "$f"; done - - sast-bandit: - image: ghcr.io/astral-sh/uv:python3.12-bookworm-slim - environment: - 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" - commands: - - uv tool install bandit - - uv tool run bandit -r app/ -ll # fail on medium+ severity - - deps-audit: - image: ghcr.io/astral-sh/uv:python3.12-bookworm-slim - environment: - 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" - commands: - - uv tool install pip-audit - - 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. - - 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 - - trivy-fs: - # 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). - image: aquasec/trivy:latest - commands: - - trivy fs - --scanners vuln,secret,misconfig - --severity HIGH,CRITICAL - --exit-code 1 - --ignore-unfixed - --ignorefile .trivyignore - --skip-dirs .venv - --skip-dirs jobs - . - - trivy-config: - # Dedicated Dockerfile + compose static analysis (Trivy's IaC linter). - image: aquasec/trivy:latest - commands: - - trivy config --severity HIGH,CRITICAL --exit-code 1 build/ diff --git a/.woodpecker/macos-release.yml b/.woodpecker/macos-release.yml deleted file mode 100644 index 8640b64b..00000000 --- a/.woodpecker/macos-release.yml +++ /dev/null @@ -1,166 +0,0 @@ -when: - - event: release - - event: tag - -labels: - platform: darwin/arm64 - backend: local - -steps: - prepare-macos-version: - image: bash - commands: - - | - if [ -z "${CI_COMMIT_TAG:-}" ]; then - echo "CI_COMMIT_TAG is not set" >&2 - exit 1 - fi - VERSION="${CI_COMMIT_TAG#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" > .macos-release.env - echo "Wrote version $VERSION to all version files" - - build-macos-arm64: - image: bash - depends_on: - - prepare-macos-version - environment: - SSL_CERT_FILE: /etc/ssl/cert.pem - REQUESTS_CA_BUNDLE: /etc/ssl/cert.pem - commands: - - . ./.macos-release.env - - 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)" - echo "ARM64_PYTHON=$ARM64_PYTHON" >> .macos-release.env - - scripts/macos/make-iconset.sh - - . ./.macos-release.env && 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 - - build-macos-x64: - image: bash - depends_on: - - prepare-macos-version - environment: - SSL_CERT_FILE: /etc/ssl/cert.pem - REQUESTS_CA_BUNDLE: /etc/ssl/cert.pem - commands: - - . ./.macos-release.env - - 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)" - echo "X64_PYTHON=$X64_PYTHON" >> .macos-release.env - - . ./.macos-release.env && 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 - - inspect-macos-artifacts: - image: bash - depends_on: - - build-macos-arm64 - - build-macos-x64 - commands: - - 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 - - upload-macos-artifacts: - image: bash - when: - - event: release - depends_on: - - inspect-macos-artifacts - environment: - GH_TOKEN: - from_secret: github_token - commands: - - | - if [ -z "${CI_COMMIT_TAG:-}" ]; then - echo "CI_COMMIT_TAG is required to upload release assets." >&2 - exit 1 - fi - - | - if ! command -v gh >/dev/null 2>&1; then - echo "GitHub CLI is required on the macOS agent to upload release assets." >&2 - exit 1 - fi - - | - scan_note="macOS arm64 and x64 DMGs and runtime packs were built and inspected on a macOS Woodpecker agent before upload." - body="$(gh release view "$CI_COMMIT_TAG" --repo "stemdeckapp/stemdeck" --json body --jq ".body")" - case "$body" in - *"$scan_note"*) ;; - *) - notes_path="$(mktemp /tmp/stemdeck-release-notes.XXXXXX.md)" - { - printf '%s\n\n' "$body" - printf '### Artifact build\n\n' - printf -- '- %s\n' "$scan_note" - } > "$notes_path" - gh release edit "$CI_COMMIT_TAG" --repo "stemdeckapp/stemdeck" --notes-file "$notes_path" - rm -f "$notes_path" - ;; - esac - - | - existing=$(gh release view "$CI_COMMIT_TAG" --repo "stemdeckapp/stemdeck" --json assets --jq '[.assets[].name] | join(" ")') - if echo "$existing" | grep -q "StemDeck-macOS-arm64.dmg"; then - echo "macOS assets already present for $CI_COMMIT_TAG — skipping upload (pre-release promotion)." - exit 0 - fi - gh release upload "$CI_COMMIT_TAG" \ - ".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" \ - --repo "stemdeckapp/stemdeck" diff --git a/.woodpecker/windows-release.yml b/.woodpecker/windows-release.yml deleted file mode 100644 index 4d016657..00000000 --- a/.woodpecker/windows-release.yml +++ /dev/null @@ -1,122 +0,0 @@ -when: - - event: release - - event: tag - -labels: - platform: windows/amd64 - backend: local - -steps: - write-version: - image: powershell - commands: - - | - $tag = $env:CI_COMMIT_TAG - if (-not $tag) { throw "CI_COMMIT_TAG 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" - - build-windows-nvidia: - depends_on: - - write-version - image: powershell - commands: - - powershell -NoProfile -ExecutionPolicy Bypass -File scripts/windows/make-portable.ps1 - -PackageName StemDeck-Windows-x64.NVIDIA - -PackageVersion "$env:CI_COMMIT_TAG" - -StripVenv - - build-windows-cpu: - image: powershell - depends_on: - - build-windows-nvidia - commands: - - powershell -NoProfile -ExecutionPolicy Bypass -File scripts/windows/make-portable.ps1 - -PackageName StemDeck-Windows-x64 - -PackageVersion "$env:CI_COMMIT_TAG" - -CpuOnly - -StripVenv - - scan-windows-artifacts: - image: powershell - depends_on: - - build-windows-cpu - commands: - - | - 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." - - upload-windows-artifacts: - image: powershell - when: - - event: release - depends_on: - - scan-windows-artifacts - environment: - GH_TOKEN: - from_secret: github_token - commands: - - | - if (-not $env:CI_COMMIT_TAG) { - throw "CI_COMMIT_TAG is required to upload a release asset." - } - - | - if (-not (Get-Command gh -ErrorAction SilentlyContinue)) { - throw "GitHub CLI is required on the Windows agent to upload release assets." - } - - | - $scanNote = "Windows portable packages were scanned with ClamAV in CI before upload." - $bodyRaw = gh release view "$env:CI_COMMIT_TAG" --repo "stemdeckapp/stemdeck" --json body --jq ".body" - $body = ($bodyRaw -join "`n") - if ($body -notlike "*$scanNote*") { - $notesPath = Join-Path $env:TEMP "stemdeck-release-notes.md" - $updatedBody = ($body.TrimEnd(), "", "### Artifact scan", "", "- $scanNote") -join "`n" - Set-Content -Path $notesPath -Value $updatedBody -Encoding UTF8 - gh release edit "$env:CI_COMMIT_TAG" --repo "stemdeckapp/stemdeck" --notes-file $notesPath - } - - | - $existing = gh release view "$env:CI_COMMIT_TAG" --repo "stemdeckapp/stemdeck" --json assets --jq '[.assets[].name] | join(" ")' - if ($existing -like "*StemDeck-Windows-x64.zip*") { - Write-Host "Windows assets already present for $env:CI_COMMIT_TAG — skipping upload (pre-release promotion)." - exit 0 - } - gh release upload "$env:CI_COMMIT_TAG" ` - "dist/StemDeck-Windows-x64.NVIDIA.zip" ` - "dist/StemDeck-Windows-x64.NVIDIA.zip.sha256" ` - "dist/StemDeck-Windows-x64.zip" ` - "dist/StemDeck-Windows-x64.zip.sha256" ` - --repo "stemdeckapp/stemdeck"