5439af1955
- Add Gitea Actions workflows: test.yml (lint + test on push/PR) and release.yml (build + NSIS installer + upload on v* tags) - Add NSIS installer with optional desktop shortcut and auto-start - Add esbuild bundler: ES module migration with IIFE bundle output - Add build-dist-windows.sh for cross-building Windows distribution - Fix all ruff lint errors (import sorting, unused imports, line length) - Remove redundant scripts (start-server.bat, stop-server.bat, start-server-background.vbs) - Update CLAUDE.md with CI/CD and release documentation
152 lines
4.2 KiB
Bash
152 lines
4.2 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# Cross-build Windows distribution on Linux
|
|
# Usage: ./build-dist-windows.sh [VERSION]
|
|
|
|
# --- Version detection ---
|
|
VERSION="${1:-}"
|
|
|
|
if [ -z "$VERSION" ]; then
|
|
VERSION=$(git describe --tags --exact-match 2>/dev/null || true)
|
|
fi
|
|
|
|
if [ -z "$VERSION" ]; then
|
|
VERSION="${GITEA_REF_NAME:-${GITHUB_REF_NAME:-}}"
|
|
fi
|
|
|
|
if [ -z "$VERSION" ]; then
|
|
VERSION=$(grep -oP '__version__\s*=\s*"\K[^"]+' \
|
|
media_server/__init__.py 2>/dev/null || echo "0.0.0")
|
|
fi
|
|
|
|
VERSION_CLEAN="${VERSION#v}"
|
|
echo "Building Media Server v${VERSION_CLEAN} for Windows"
|
|
|
|
# --- Configuration ---
|
|
PYTHON_VERSION="3.11.9"
|
|
PYTHON_SHORT="311"
|
|
DIST_DIR="dist/media-server"
|
|
WHEEL_DIR="build/win-wheels"
|
|
SITE_PACKAGES="${DIST_DIR}/python/Lib/site-packages"
|
|
BUILD_OUTPUT="build/MediaServer-v${VERSION_CLEAN}-win-x64"
|
|
|
|
rm -rf dist build
|
|
mkdir -p "${DIST_DIR}" "${WHEEL_DIR}" "${SITE_PACKAGES}"
|
|
|
|
# --- Download embedded Python ---
|
|
echo "Downloading embedded Python ${PYTHON_VERSION}..."
|
|
curl -sL "https://www.python.org/ftp/python/${PYTHON_VERSION}/python-${PYTHON_VERSION}-embed-amd64.zip" \
|
|
-o build/python-embed.zip
|
|
unzip -qo build/python-embed.zip -d "${DIST_DIR}/python"
|
|
|
|
# Patch ._pth to enable site-packages and app source
|
|
PTH_FILE=$(ls "${DIST_DIR}"/python/python*._pth | head -1)
|
|
sed -i 's/^#\s*import site/import site/' "$PTH_FILE"
|
|
echo 'Lib\site-packages' >> "$PTH_FILE"
|
|
echo '..\..\app' >> "$PTH_FILE"
|
|
|
|
# --- Download Windows wheels ---
|
|
echo "Downloading Windows wheels..."
|
|
|
|
# Core dependencies
|
|
CORE_DEPS=(
|
|
"fastapi>=0.109.0"
|
|
"uvicorn[standard]>=0.27.0"
|
|
"pydantic>=2.0"
|
|
"pydantic-settings>=2.0"
|
|
"pyyaml>=6.0"
|
|
"mutagen>=1.47.0"
|
|
"pillow>=10.0.0"
|
|
)
|
|
|
|
# Windows-specific dependencies
|
|
WIN_DEPS=(
|
|
"winsdk>=1.0.0b10"
|
|
"pywin32>=306"
|
|
"comtypes>=1.2.0"
|
|
"pycaw>=20230407"
|
|
"screen-brightness-control>=0.20.0"
|
|
"monitorcontrol>=3.0.0"
|
|
)
|
|
|
|
# Visualizer dependencies
|
|
VIS_DEPS=(
|
|
"soundcard>=0.4.0"
|
|
"numpy>=1.24.0"
|
|
)
|
|
|
|
ALL_DEPS=("${CORE_DEPS[@]}" "${WIN_DEPS[@]}" "${VIS_DEPS[@]}")
|
|
|
|
for dep in "${ALL_DEPS[@]}"; do
|
|
pip download --quiet --dest "$WHEEL_DIR" \
|
|
--platform win_amd64 --python-version "${PYTHON_SHORT}" \
|
|
--implementation cp --only-binary :all: \
|
|
"$dep" 2>/dev/null || \
|
|
pip download --quiet --dest "$WHEEL_DIR" "$dep"
|
|
done
|
|
|
|
# Install wheels into site-packages
|
|
echo "Installing wheels..."
|
|
for whl in "$WHEEL_DIR"/*.whl; do
|
|
unzip -qo "$whl" -d "$SITE_PACKAGES"
|
|
done
|
|
|
|
# --- Size optimization ---
|
|
echo "Optimizing size..."
|
|
find "$SITE_PACKAGES" -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
|
|
find "$SITE_PACKAGES" -type d -name tests -exec rm -rf {} + 2>/dev/null || true
|
|
find "$SITE_PACKAGES" -type d -name "*.dist-info" -exec rm -rf {} + 2>/dev/null || true
|
|
find "$SITE_PACKAGES" -name "*.pyi" -delete 2>/dev/null || true
|
|
rm -rf "$SITE_PACKAGES"/{pip,setuptools,pkg_resources}* 2>/dev/null || true
|
|
|
|
# Trim numpy if present
|
|
rm -rf "$SITE_PACKAGES"/numpy/{tests,f2py,typing} 2>/dev/null || true
|
|
|
|
# --- Verify frontend bundle ---
|
|
if [ ! -f "media_server/static/dist/app.bundle.js" ]; then
|
|
echo "ERROR: Frontend bundle not found. Run 'npm ci && npm run build' first."
|
|
exit 1
|
|
fi
|
|
|
|
# --- Copy application ---
|
|
echo "Copying application files..."
|
|
mkdir -p "${DIST_DIR}/app"
|
|
cp -r media_server "${DIST_DIR}/app/"
|
|
|
|
# Remove source JS (bundle is in dist/)
|
|
rm -rf "${DIST_DIR}/app/media_server/static/js"
|
|
# Remove source maps from release
|
|
rm -f "${DIST_DIR}/app/media_server/static/dist/"*.map
|
|
|
|
# Copy config example
|
|
cp config.example.yaml "${DIST_DIR}/"
|
|
|
|
# Copy scripts needed for auto-start
|
|
mkdir -p "${DIST_DIR}/scripts"
|
|
cp scripts/start-hidden.vbs "${DIST_DIR}/scripts/"
|
|
|
|
# --- Write version ---
|
|
echo "$VERSION_CLEAN" > "${DIST_DIR}/VERSION"
|
|
|
|
# --- Create launcher ---
|
|
cat > "${DIST_DIR}/media-server.bat" << 'LAUNCHER'
|
|
@echo off
|
|
setlocal
|
|
set "ROOT=%~dp0"
|
|
"%ROOT%python\python.exe" -m media_server.main %*
|
|
LAUNCHER
|
|
|
|
# --- Package ---
|
|
echo "Creating archives..."
|
|
mkdir -p build
|
|
|
|
# Portable ZIP
|
|
cp -r "${DIST_DIR}" "${BUILD_OUTPUT}"
|
|
cd build
|
|
zip -qr "MediaServer-v${VERSION_CLEAN}-win-x64.zip" "MediaServer-v${VERSION_CLEAN}-win-x64"
|
|
cd ..
|
|
|
|
echo "Build complete: build/MediaServer-v${VERSION_CLEAN}-win-x64.zip"
|
|
echo "Dist directory ready for NSIS: ${DIST_DIR}"
|