Add CI/CD pipelines, NSIS installer, ES module bundling, and ruff linting
- 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
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
#!/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}"
|
||||
Reference in New Issue
Block a user