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
+74
View File
@@ -0,0 +1,74 @@
#!/usr/bin/env bash
set -euo pipefail
ARCH="${ARCH:-arm64}"
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
# Default the version to the current git tag so local builds match the tag with
# no VERSION passed; falls back to a dev label outside a tagged checkout. (#169)
VERSION="${VERSION:-$(git -C "$REPO_ROOT" describe --tags --always 2>/dev/null || echo 0.0.0-dev)}"
VERSION="${VERSION#v}"
BUILD_DIR="${REPO_ROOT}/.build"
if [[ "$(uname)" != "Darwin" ]]; then
echo "ERROR: make-app.sh must run on macOS" >&2
exit 1
fi
if [[ "$ARCH" != "arm64" && "$ARCH" != "x64" ]]; then
echo "ERROR: ARCH must be arm64 or x64, got '${ARCH}'" >&2
exit 1
fi
for cmd in node npm cargo; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "ERROR: required command not found on PATH: $cmd" >&2
exit 1
fi
done
mkdir -p "$BUILD_DIR"
echo "==> Stamping version ${VERSION}"
sed -i '' "s/^version = \".*\"/version = \"${VERSION}\"/" "$REPO_ROOT/desktop/src-tauri/Cargo.toml"
sed -i '' "s/\"version\": \".*\"/\"version\": \"${VERSION}\"/" "$REPO_ROOT/desktop/src-tauri/tauri.conf.json"
sed -i '' "s/\"version\": \".*\"/\"version\": \"${VERSION}\"/" "$REPO_ROOT/desktop/package.json"
cd "$REPO_ROOT/desktop"
# Tauri CLI reads CI env var as a boolean flag; Woodpecker sets CI=woodpecker
# which fails the boolean parser. Force a valid value.
export CI=true
npm ci
if [[ "$ARCH" == "arm64" ]]; then
npx tauri build --bundles app --target aarch64-apple-darwin
TARGET_DIR="$REPO_ROOT/desktop/src-tauri/target/aarch64-apple-darwin/release"
else
npx tauri build --bundles app --target x86_64-apple-darwin
TARGET_DIR="$REPO_ROOT/desktop/src-tauri/target/x86_64-apple-darwin/release"
fi
APP_DIR="${TARGET_DIR}/bundle/macos/StemDeck.app"
if [[ ! -d "$APP_DIR" ]]; then
APP_DIR="$(find "$REPO_ROOT/desktop/src-tauri/target" -path '*/bundle/macos/StemDeck.app' -type d | head -1)"
fi
if [[ -z "$APP_DIR" || ! -d "$APP_DIR" ]]; then
echo "ERROR: could not find built StemDeck.app" >&2
exit 1
fi
RESOURCES="${APP_DIR}/Contents/Resources"
mkdir -p "$RESOURCES"
if [[ -f "$BUILD_DIR/runtime-manifest-${ARCH}.json" ]]; then
cp "$BUILD_DIR/runtime-manifest-${ARCH}.json" "$RESOURCES/runtime-manifest.json"
else
cp "$REPO_ROOT/desktop/ui/runtime-manifest.json" "$RESOURCES/runtime-manifest.json"
fi
if [[ -f "$REPO_ROOT/packaging/macos/THIRD_PARTY_NOTICES.txt" ]]; then
cp "$REPO_ROOT/packaging/macos/THIRD_PARTY_NOTICES.txt" "$RESOURCES/THIRD_PARTY_NOTICES.txt"
fi
echo "$APP_DIR" > "$BUILD_DIR/app-path-${ARCH}.txt"
echo "==> App ready: $APP_DIR"
+139
View File
@@ -0,0 +1,139 @@
#!/usr/bin/env bash
set -euo pipefail
ARCH="${ARCH:-arm64}"
VERSION="${VERSION:-LOCAL_DEV_TEST}"
VERSION="${VERSION#v}"
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
BUILD_DIR="${REPO_ROOT}/.build"
DIST_DIR="${BUILD_DIR}/macos-dist"
DMG_STAGING="${BUILD_DIR}/dmg-staging-${ARCH}"
DMG_NAME="StemDeck-macOS-${ARCH}.dmg"
DMG_PATH="${DIST_DIR}/${DMG_NAME}"
DMG_RW_PATH="${DIST_DIR}/StemDeck-macOS-${ARCH}.rw.dmg"
RUNTIME_NAME="StemDeck-runtime-macOS-${ARCH}.tar.zst"
RUNTIME_PATH="${BUILD_DIR}/${RUNTIME_NAME}"
BACKGROUND_SRC="${REPO_ROOT}/packaging/macos/dmg-background.svg"
BACKGROUND_DIR_NAME=".background"
BACKGROUND_PNG_NAME="dmg-background.png"
if [[ "$(uname)" != "Darwin" ]]; then
echo "ERROR: make-dmg.sh must run on macOS" >&2
exit 1
fi
if [[ "$ARCH" != "arm64" && "$ARCH" != "x64" ]]; then
echo "ERROR: ARCH must be arm64 or x64, got '${ARCH}'" >&2
exit 1
fi
for cmd in ditto hdiutil qlmanage shasum; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "ERROR: required command not found on PATH: $cmd" >&2
exit 1
fi
done
if [[ "$ARCH" == "arm64" ]]; then
APP_DIR="${REPO_ROOT}/desktop/src-tauri/target/aarch64-apple-darwin/release/bundle/macos/StemDeck.app"
else
APP_DIR="${REPO_ROOT}/desktop/src-tauri/target/x86_64-apple-darwin/release/bundle/macos/StemDeck.app"
fi
if [[ ! -d "$APP_DIR" ]]; then
echo "ERROR: built app not found: $APP_DIR" >&2
echo "Run: ARCH=${ARCH} scripts/macos/make-app.sh" >&2
exit 1
fi
if [[ ! -f "$RUNTIME_PATH" ]]; then
echo "ERROR: runtime pack not found: $RUNTIME_PATH" >&2
echo "Run: ARCH=${ARCH} VERSION=${VERSION} scripts/macos/make-runtime-pack.sh" >&2
exit 1
fi
rm -rf "$DMG_STAGING"
mkdir -p "$DMG_STAGING" "$DIST_DIR"
mkdir -p "$DMG_STAGING/$BACKGROUND_DIR_NAME"
ditto "$APP_DIR" "$DMG_STAGING/StemDeck.app"
ln -s /Applications "$DMG_STAGING/Applications"
if [[ -f "$BACKGROUND_SRC" ]]; then
qlmanage -t -s 1320 -o "$DMG_STAGING/$BACKGROUND_DIR_NAME" "$BACKGROUND_SRC" >/dev/null 2>&1
mv "$DMG_STAGING/$BACKGROUND_DIR_NAME/dmg-background.svg.png" "$DMG_STAGING/$BACKGROUND_DIR_NAME/$BACKGROUND_PNG_NAME"
fi
if [[ -f "$REPO_ROOT/packaging/macos/README-macOS.txt" ]]; then
cp "$REPO_ROOT/packaging/macos/README-macOS.txt" "$DMG_STAGING/README-macOS.txt"
fi
if [[ -f "$REPO_ROOT/packaging/macos/THIRD_PARTY_NOTICES.txt" ]]; then
cp "$REPO_ROOT/packaging/macos/THIRD_PARTY_NOTICES.txt" "$DMG_STAGING/THIRD_PARTY_NOTICES.txt"
fi
rm -f "$DMG_PATH" "$DMG_RW_PATH"
hdiutil create \
-volname "StemDeck" \
-srcfolder "$DMG_STAGING" \
-ov \
-format UDRW \
"$DMG_RW_PATH"
MOUNT_DIR="$(mktemp -d /tmp/stemdeck-dmg.XXXXXX)"
cleanup_mount() {
hdiutil detach "$MOUNT_DIR" >/dev/null 2>&1 || true
rmdir "$MOUNT_DIR" >/dev/null 2>&1 || true
}
trap cleanup_mount EXIT
hdiutil attach "$DMG_RW_PATH" -readwrite -noverify -nobrowse -mountpoint "$MOUNT_DIR" >/dev/null
if command -v SetFile >/dev/null 2>&1; then
SetFile -a V "$MOUNT_DIR/$BACKGROUND_DIR_NAME" || true
SetFile -a V "$MOUNT_DIR/README-macOS.txt" || true
SetFile -a V "$MOUNT_DIR/THIRD_PARTY_NOTICES.txt" || true
fi
if [[ -f "$MOUNT_DIR/$BACKGROUND_DIR_NAME/$BACKGROUND_PNG_NAME" ]]; then
osascript <<APPLESCRIPT || echo "warning: DMG window styling failed (cosmetic only, DMG is still valid)"
tell application "Finder"
set dmgFolder to POSIX file "$MOUNT_DIR" as alias
open dmgFolder
set current view of container window of dmgFolder to icon view
set toolbar visible of container window of dmgFolder to false
set statusbar visible of container window of dmgFolder to false
set bounds of container window of dmgFolder to {100, 100, 760, 500}
set viewOptions to icon view options of container window of dmgFolder
set arrangement of viewOptions to not arranged
set icon size of viewOptions to 104
set text size of viewOptions to 13
set background picture of viewOptions to POSIX file "$MOUNT_DIR/$BACKGROUND_DIR_NAME/$BACKGROUND_PNG_NAME"
set position of item "StemDeck.app" of dmgFolder to {205, 205}
set position of item "Applications" of dmgFolder to {455, 205}
close container window of dmgFolder
open dmgFolder
update dmgFolder without registering applications
delay 1
close container window of dmgFolder
end tell
APPLESCRIPT
fi
sync
hdiutil detach "$MOUNT_DIR" >/dev/null
rmdir "$MOUNT_DIR"
trap - EXIT
hdiutil convert "$DMG_RW_PATH" -format UDZO -imagekey zlib-level=9 -o "$DMG_PATH" >/dev/null
rm -f "$DMG_RW_PATH"
CHECKSUMS_PATH="${DIST_DIR}/SHA256SUMS-macOS-${ARCH}.txt"
{
shasum -a 256 "$DMG_PATH"
shasum -a 256 "$RUNTIME_PATH"
} | sed "s#${REPO_ROOT}/##" > "$CHECKSUMS_PATH"
echo "==> DMG ready: $DMG_PATH"
echo "==> Runtime pack: $RUNTIME_PATH"
echo "==> Checksums: $CHECKSUMS_PATH"
+54
View File
@@ -0,0 +1,54 @@
#!/usr/bin/env bash
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
SOURCE_SVG="${SOURCE_SVG:-${REPO_ROOT}/imgs/stemdeck-svg-assets/stemdeck-icon.svg}"
ICON_DIR="${REPO_ROOT}/desktop/src-tauri/icons"
WORK_DIR="${TMPDIR:-/tmp}/stemdeck-iconset"
RENDER_DIR="${WORK_DIR}/render"
ICONSET_DIR="${WORK_DIR}/icon.iconset"
if [[ "$(uname)" != "Darwin" ]]; then
echo "ERROR: make-iconset.sh must run on macOS" >&2
exit 1
fi
for cmd in qlmanage sips iconutil; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "ERROR: required command not found on PATH: $cmd" >&2
exit 1
fi
done
if [[ ! -f "$SOURCE_SVG" ]]; then
echo "ERROR: source SVG not found: $SOURCE_SVG" >&2
exit 1
fi
rm -rf "$WORK_DIR"
mkdir -p "$RENDER_DIR" "$ICONSET_DIR" "$ICON_DIR"
qlmanage -t -s 1024 -o "$RENDER_DIR" "$SOURCE_SVG" >/dev/null
RENDERED_PNG="$(find "$RENDER_DIR" -maxdepth 1 -type f -name '*.png' | head -1)"
if [[ -z "$RENDERED_PNG" || ! -f "$RENDERED_PNG" ]]; then
echo "ERROR: qlmanage did not render a PNG from $SOURCE_SVG" >&2
exit 1
fi
cp "$RENDERED_PNG" "$ICON_DIR/icon.png"
for size in 16 32 128 256 512; do
sips -z "$size" "$size" "$ICON_DIR/icon.png" \
--out "$ICONSET_DIR/icon_${size}x${size}.png" >/dev/null
done
sips -z 32 32 "$ICON_DIR/icon.png" --out "$ICONSET_DIR/icon_16x16@2x.png" >/dev/null
sips -z 64 64 "$ICON_DIR/icon.png" --out "$ICONSET_DIR/icon_32x32@2x.png" >/dev/null
sips -z 256 256 "$ICON_DIR/icon.png" --out "$ICONSET_DIR/icon_128x128@2x.png" >/dev/null
sips -z 512 512 "$ICON_DIR/icon.png" --out "$ICONSET_DIR/icon_256x256@2x.png" >/dev/null
cp "$ICON_DIR/icon.png" "$ICONSET_DIR/icon_512x512@2x.png"
iconutil -c icns "$ICONSET_DIR" -o "$ICON_DIR/icon.icns"
echo "==> Icon PNG: $ICON_DIR/icon.png"
echo "==> Icon ICNS: $ICON_DIR/icon.icns"
+209
View File
@@ -0,0 +1,209 @@
#!/usr/bin/env bash
set -euo pipefail
ARCH="${ARCH:-arm64}"
VERSION="${VERSION:-LOCAL_DEV_TEST}"
VERSION="${VERSION#v}"
RELEASE_BASE_URL="${RELEASE_BASE_URL:-https://github.com/stemdeckapp/stemdeck/releases/download/v${VERSION}}"
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
BUILD_DIR="${REPO_ROOT}/.build"
STAGING="${BUILD_DIR}/runtime-staging-${ARCH}"
RUNTIME_DIR="${STAGING}/runtime"
PYTHON_DIR="${RUNTIME_DIR}/python"
BACKEND_DIR="${RUNTIME_DIR}/backend"
if [[ "$(uname)" != "Darwin" ]]; then
echo "ERROR: make-runtime-pack.sh must run on macOS" >&2
exit 1
fi
if [[ "$ARCH" != "arm64" && "$ARCH" != "x64" ]]; then
echo "ERROR: ARCH must be arm64 or x64, got '${ARCH}'" >&2
exit 1
fi
PYTHON_BIN="${PYTHON_BIN:-}"
if [[ -z "$PYTHON_BIN" ]]; then
for candidate in python3.12 python3.11 python3.10 python3; do
if command -v "$candidate" >/dev/null 2>&1; then
PYTHON_BIN="$candidate"
break
fi
done
fi
for cmd in ditto shasum tar "$PYTHON_BIN"; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "ERROR: required command not found on PATH: $cmd" >&2
exit 1
fi
done
PYTHON_VERSION="$("$PYTHON_BIN" - <<'PY'
import sys
print(f"{sys.version_info.major}.{sys.version_info.minor}")
PY
)"
PYTHON_MACHINE="$("$PYTHON_BIN" - <<'PY'
import platform
print(platform.machine())
PY
)"
case "$PYTHON_VERSION" in
3.10|3.11|3.12|3.13) ;;
*)
echo "ERROR: ${PYTHON_BIN} is Python ${PYTHON_VERSION}; Torch 2.6 runtime builds require Python 3.10-3.13." >&2
echo "Set PYTHON_BIN=/path/to/python3.12 or PYTHON_BIN=/path/to/python3.11." >&2
exit 1
;;
esac
HOST_ARCH="$(uname -m)"
if [[ "$ARCH" == "arm64" && "$PYTHON_MACHINE" != "arm64" ]]; then
echo "ERROR: arm64 runtime requires an arm64 Python, got ${PYTHON_MACHINE}" >&2
exit 1
fi
if [[ "$ARCH" == "x64" && "$PYTHON_MACHINE" != "x86_64" ]]; then
echo "ERROR: x64 runtime requires an x86_64 Python, got ${PYTHON_MACHINE}" >&2
exit 1
fi
if [[ "$ARCH" == "x64" && "$HOST_ARCH" == "arm64" ]]; then
if ! arch -x86_64 /usr/bin/true >/dev/null 2>&1; then
echo "ERROR: x64 runtime on arm64 hosts requires Rosetta 2." >&2
exit 1
fi
fi
rm -rf "$STAGING"
mkdir -p "$PYTHON_DIR" "$BACKEND_DIR" "$BUILD_DIR"
echo "==> Bundling Python installation (${ARCH})"
echo "==> Python: $("$PYTHON_BIN" --version)"
echo "==> Python architecture: ${PYTHON_MACHINE}"
# Get the full PBS (python-build-standalone) installation root.
# This directory has the complete stdlib in lib/pythonX.Y/ — unlike a venv,
# which only creates site-packages/ and relies on the original base_prefix
# (a path that won't exist on user machines) for stdlib.
PYTHON_BASE_PREFIX="$("$PYTHON_BIN" - <<'PY'
import sys
print(sys.base_prefix)
PY
)"
echo "==> Python base prefix: ${PYTHON_BASE_PREFIX}"
if [[ ! -d "${PYTHON_BASE_PREFIX}/lib" ]]; then
echo "ERROR: Python base prefix has no lib/ dir: ${PYTHON_BASE_PREFIX}" >&2
echo " Make sure PYTHON_BIN points to a python-build-standalone (UV) Python." >&2
exit 1
fi
# Verify the stdlib is actually present in base_prefix before copying.
STDLIB_CHECK="$("$PYTHON_BIN" - <<'PY'
import sys, pathlib
ver = f"python{sys.version_info.major}.{sys.version_info.minor}"
p = pathlib.Path(sys.base_prefix) / "lib" / ver / "encodings" / "__init__.py"
print("ok" if p.is_file() else f"missing:{p}")
PY
)"
if [[ "$STDLIB_CHECK" != "ok" ]]; then
echo "ERROR: stdlib not found in base_prefix (${STDLIB_CHECK})" >&2
echo " base_prefix: ${PYTHON_BASE_PREFIX}" >&2
exit 1
fi
# Copy the entire PBS Python installation into the runtime bundle.
# ditto preserves symlinks, HFS+ metadata, and extended attributes.
ditto "$PYTHON_BASE_PREFIX" "$PYTHON_DIR"
# PBS Python ships an EXTERNALLY-MANAGED marker that blocks uv from installing
# packages into it. Remove it so we can treat this copy as our own install.
find "$PYTHON_DIR/lib" -name "EXTERNALLY-MANAGED" -delete
echo "==> Installing packages into bundled Python"
# --system is required because $PYTHON_DIR is not a venv (it's a full Python install).
uv pip install --system --python "$PYTHON_DIR/bin/python" pip setuptools wheel
# The project version is git-derived (hatch-vcs). Pin it explicitly from $VERSION
# so the install doesn't depend on git tags being present in the build checkout (#169).
SETUPTOOLS_SCM_PRETEND_VERSION="${VERSION#v}" \
uv pip install --system --python "$PYTHON_DIR/bin/python" "$REPO_ROOT"
echo "==> Verifying stdlib and imports"
PYTHON_DIR="$PYTHON_DIR" PYTHONHOME="$PYTHON_DIR" "$PYTHON_DIR/bin/python" - <<'PY'
import importlib, os, pathlib, sys
ver = f"python{sys.version_info.major}.{sys.version_info.minor}"
stdlib = pathlib.Path(os.environ["PYTHON_DIR"]) / "lib" / ver
if not (stdlib / "encodings" / "__init__.py").is_file():
print(f"ERROR: encodings not found in {stdlib}", file=sys.stderr)
sys.exit(1)
print(f" stdlib OK at {stdlib}")
packages = [
"fastapi", "uvicorn", "yt_dlp", "demucs", "torch", "torchaudio",
"librosa", "pyloudnorm", "soundfile",
]
for package in packages:
importlib.import_module(package)
print(f" OK {package}")
PY
echo "==> Staging backend"
cp -R "$REPO_ROOT/app" "$BACKEND_DIR/app"
cp -R "$REPO_ROOT/static" "$BACKEND_DIR/static"
cp "$REPO_ROOT/pyproject.toml" "$BACKEND_DIR/pyproject.toml"
cp "$REPO_ROOT/uv.lock" "$BACKEND_DIR/uv.lock"
cat > "$BACKEND_DIR/static/version.json" <<JSON
{
"version": "${VERSION}",
"arch": "${ARCH}"
}
JSON
echo "==> Capturing dependency inventory"
mkdir -p "$RUNTIME_DIR/licenses"
uv pip list --system --python "$PYTHON_DIR/bin/python" --format=json > "$RUNTIME_DIR/licenses/pip-list.json"
cat > "$RUNTIME_DIR/runtime-manifest.json" <<JSON
{
"version": "${VERSION}",
"arch": "${ARCH}",
"createdBy": "scripts/macos/make-runtime-pack.sh"
}
JSON
echo "==> Stripping Python caches"
find "$PYTHON_DIR" -type d -name "__pycache__" -prune -exec rm -rf {} + 2>/dev/null || true
find "$PYTHON_DIR" -type f \( -name "*.pyc" -o -name "*.pyo" \) -delete
ARCHIVE_NAME="StemDeck-runtime-macOS-${ARCH}.tar.zst"
ARCHIVE_PATH="${BUILD_DIR}/${ARCHIVE_NAME}"
if command -v zstd >/dev/null 2>&1; then
tar --zstd -cf "$ARCHIVE_PATH" -C "$STAGING" runtime
else
ARCHIVE_NAME="StemDeck-runtime-macOS-${ARCH}.tar.gz"
ARCHIVE_PATH="${BUILD_DIR}/${ARCHIVE_NAME}"
tar -czf "$ARCHIVE_PATH" -C "$STAGING" runtime
fi
SIZE="$(stat -f%z "$ARCHIVE_PATH")"
SHA256="$(shasum -a 256 "$ARCHIVE_PATH" | awk '{print $1}')"
RUNTIME_URL="${RELEASE_BASE_URL}/${ARCHIVE_NAME}"
cat > "${BUILD_DIR}/runtime-manifest-${ARCH}.json" <<JSON
{
"version": "${VERSION}",
"arch": "${ARCH}",
"runtimeUrl": "${RUNTIME_URL}",
"runtimeSha256": "${SHA256}",
"runtimeSize": ${SIZE},
"archiveName": "${ARCHIVE_NAME}"
}
JSON
echo "==> Runtime pack ready"
echo "Archive: ${ARCHIVE_PATH}"
echo "Size: ${SIZE}"
echo "SHA256: ${SHA256}"
echo "Manifest: ${BUILD_DIR}/runtime-manifest-${ARCH}.json"