diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..b59006ef --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,342 @@ +name: Release + +on: + push: + tags: ['v*'] + +permissions: + contents: write + +jobs: + build-linux: + name: Build (linux-${{ matrix.arch }}) + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: false + matrix: + include: + - arch: x86-64 + runner: ubuntu-24.04 + - arch: arm64 + runner: ubuntu-24.04-arm + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: true + + - name: Cache apt packages + uses: actions/cache@v4 + with: + path: /var/cache/apt/archives/*.deb + key: apt-${{ matrix.arch }}-${{ hashFiles('.github/workflows/release.yml') }} + restore-keys: apt-${{ matrix.arch }}- + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + cmake \ + build-essential \ + pkg-config \ + libsdl2-dev \ + libglew-dev \ + libglm-dev \ + libssl-dev \ + zlib1g-dev \ + libvulkan-dev \ + vulkan-tools \ + glslc \ + libavformat-dev \ + libavcodec-dev \ + libswscale-dev \ + libavutil-dev \ + libunicorn-dev \ + libx11-dev + sudo apt-get install -y libstormlib-dev || true + + - name: Configure + run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release + + - name: Build + run: cmake --build build --parallel $(nproc) + + - name: Package + run: | + TAG="${GITHUB_REF_NAME}" + STAGING="wowee-${TAG}-linux-${{ matrix.arch }}" + mkdir -p "${STAGING}" + + # Binary + cp build/bin/wowee "${STAGING}/" + + # Asset extraction tool (if built — requires StormLib) + if [ -f build/bin/asset_extract ]; then + cp build/bin/asset_extract "${STAGING}/" + fi + + # Extraction scripts and GUI + cp extract_assets.sh "${STAGING}/" + cp tools/asset_pipeline_gui.py "${STAGING}/" + + # Assets (exclude proprietary music) + rsync -a --exclude='Original Music' build/bin/assets/ "${STAGING}/assets/" + + # Data directory (git-tracked files only) + git ls-files Data/ | while read -r f; do + mkdir -p "${STAGING}/$(dirname "$f")" + cp "$f" "${STAGING}/$f" + done + + tar czf "${STAGING}.tar.gz" "${STAGING}" + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: wowee-linux-${{ matrix.arch }} + path: wowee-*.tar.gz + if-no-files-found: error + + build-macos: + name: Build (macOS arm64) + runs-on: macos-15 + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: true + + - name: Install dependencies + run: | + brew install cmake pkg-config sdl2 glew glm openssl@3 zlib ffmpeg unicorn \ + stormlib vulkan-loader vulkan-headers shaderc dylibbundler || true + brew install dylibbundler 2>/dev/null || true + + - name: Configure + run: | + BREW=$(brew --prefix) + export PKG_CONFIG_PATH="$BREW/lib/pkgconfig:$(brew --prefix ffmpeg)/lib/pkgconfig:$(brew --prefix openssl@3)/lib/pkgconfig:$(brew --prefix vulkan-loader)/lib/pkgconfig:$(brew --prefix shaderc)/lib/pkgconfig" + cmake -S . -B build \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_PREFIX_PATH="$BREW" \ + -DOPENSSL_ROOT_DIR="$(brew --prefix openssl@3)" + + - name: Build + run: cmake --build build --parallel $(sysctl -n hw.logicalcpu) + + - name: Create .app bundle + run: | + TAG="${GITHUB_REF_NAME}" + mkdir -p Wowee.app/Contents/{MacOS,Frameworks,Resources} + + # Wrapper launch script + printf '#!/bin/bash\ncd "$(dirname "$0")"\nexec ./wowee_bin "$@"\n' \ + > Wowee.app/Contents/MacOS/wowee + chmod +x Wowee.app/Contents/MacOS/wowee + + # Actual binary + cp build/bin/wowee Wowee.app/Contents/MacOS/wowee_bin + + # Asset extraction tool (if built — requires StormLib) + if [ -f build/bin/asset_extract ]; then + cp build/bin/asset_extract Wowee.app/Contents/MacOS/ + fi + + # Extraction scripts and GUI + cp extract_assets.sh Wowee.app/Contents/MacOS/ + cp tools/asset_pipeline_gui.py Wowee.app/Contents/MacOS/ + + # Assets (exclude proprietary music) + rsync -a --exclude='Original Music' build/bin/assets/ \ + Wowee.app/Contents/MacOS/assets/ + + # Data directory (git-tracked files only) + git ls-files Data/ | while read -r f; do + mkdir -p "Wowee.app/Contents/MacOS/$(dirname "$f")" + cp "$f" "Wowee.app/Contents/MacOS/$f" + done + + # Bundle dylibs + if command -v dylibbundler &>/dev/null; then + dylibbundler -od -b \ + -x Wowee.app/Contents/MacOS/wowee_bin \ + -d Wowee.app/Contents/Frameworks/ \ + -p @executable_path/../Frameworks/ + if [ -f Wowee.app/Contents/MacOS/asset_extract ]; then + dylibbundler -od -b \ + -x Wowee.app/Contents/MacOS/asset_extract \ + -d Wowee.app/Contents/Frameworks/ \ + -p @executable_path/../Frameworks/ + fi + fi + + # Info.plist + cat > Wowee.app/Contents/Info.plist << EOF + + + + CFBundleExecutablewowee + CFBundleIdentifiercom.wowee.app + CFBundleNameWowee + CFBundleVersion${TAG} + CFBundleShortVersionString${TAG} + CFBundlePackageTypeAPPL + + EOF + + # Ad-hoc codesign + codesign --force --deep --sign - Wowee.app + + - name: Create DMG + run: | + TAG="${GITHUB_REF_NAME}" + hdiutil create -volname Wowee -srcfolder Wowee.app -ov -format UDZO \ + "wowee-${TAG}-macos-arm64.dmg" + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: wowee-macos-arm64 + path: wowee-*.dmg + if-no-files-found: error + + build-windows: + name: Build (windows-${{ matrix.arch }}) + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: false + matrix: + include: + - arch: x86-64 + runner: windows-latest + msystem: MINGW64 + prefix: mingw-w64-x86_64 + - arch: arm64 + runner: windows-11-arm + msystem: CLANGARM64 + prefix: mingw-w64-clang-aarch64 + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: true + + - name: Set up MSYS2 + uses: msys2/setup-msys2@v2 + with: + msystem: ${{ matrix.msystem }} + update: ${{ matrix.arch == 'arm64' }} + install: >- + ${{ matrix.prefix }}-cmake + ${{ matrix.arch == 'x86-64' && format('{0}-gcc', matrix.prefix) || format('{0}-clang', matrix.prefix) }} + ${{ matrix.prefix }}-ninja + ${{ matrix.prefix }}-pkgconf + ${{ matrix.prefix }}-SDL2 + ${{ matrix.prefix }}-glew + ${{ matrix.prefix }}-glm + ${{ matrix.prefix }}-openssl + ${{ matrix.prefix }}-zlib + ${{ matrix.prefix }}-ffmpeg + ${{ matrix.prefix }}-unicorn + ${{ matrix.prefix }}-vulkan-loader + ${{ matrix.prefix }}-vulkan-headers + ${{ matrix.prefix }}-shaderc + git + + - name: Install optional packages + shell: msys2 {0} + run: pacman -S --noconfirm --needed ${{ matrix.prefix }}-stormlib || true + + - name: Configure + shell: msys2 {0} + run: cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release + + - name: Build + shell: msys2 {0} + run: cmake --build build --parallel $(nproc) + + - name: Bundle DLLs + shell: msys2 {0} + run: | + for exe in build/bin/wowee.exe build/bin/asset_extract.exe; do + [ -f "$exe" ] || continue + ldd "$exe" \ + | awk '/=> \// { print $3 }' \ + | grep -iv '^/c/Windows' \ + | xargs -I{} sh -c 'cp -n "{}" build/bin/ 2>/dev/null || true' + done + + - name: Package + shell: msys2 {0} + run: | + TAG="${GITHUB_REF_NAME}" + STAGING="wowee-${TAG}-windows-${{ matrix.arch }}" + mkdir -p "${STAGING}" + + # Binary and DLLs + cp build/bin/wowee.exe "${STAGING}/" + cp build/bin/*.dll "${STAGING}/" 2>/dev/null || true + + # Asset extraction tool (if built — requires StormLib) + if [ -f build/bin/asset_extract.exe ]; then + cp build/bin/asset_extract.exe "${STAGING}/" + fi + + # Extraction scripts and GUI + cp extract_assets.ps1 "${STAGING}/" + cp extract_assets.bat "${STAGING}/" + cp tools/asset_pipeline_gui.py "${STAGING}/" + + # Assets (exclude proprietary music) + rsync -a --exclude='Original Music' build/bin/assets/ "${STAGING}/assets/" || \ + cp -r build/bin/assets "${STAGING}/assets" + + # Data directory (git-tracked files only) + git ls-files Data/ | while read -r f; do + mkdir -p "${STAGING}/$(dirname "$f")" + cp "$f" "${STAGING}/$f" + done + + # Create ZIP using 7z (available in MSYS2) + 7z a -tzip "${STAGING}.zip" "${STAGING}" || \ + (cd "${STAGING}" && zip -r "../${STAGING}.zip" .) + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: wowee-windows-${{ matrix.arch }} + path: wowee-*.zip + if-no-files-found: error + + release: + name: Create Release + needs: [build-linux, build-macos, build-windows] + runs-on: ubuntu-latest + + steps: + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts/ + + - name: Create GitHub Release + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + run: | + TAG="${GITHUB_REF_NAME}" + + # Collect all release files + FILES=() + for f in artifacts/*/*; do + FILES+=("$f") + done + + gh release create "${TAG}" \ + --title "Wowee ${TAG}" \ + --generate-notes \ + "${FILES[@]}"