diff --git a/.github/scripts/init-submodules.sh b/.github/scripts/init-submodules.sh new file mode 100755 index 0000000..0c3817f --- /dev/null +++ b/.github/scripts/init-submodules.sh @@ -0,0 +1,243 @@ +#!/bin/bash +# Initialize submodules for CI/CD builds +# This script initializes only the submodules needed for building, +# skipping examples and demos to speed up checkout and avoid path length issues on Windows. + +set -e + +echo "========================================" +echo "Initializing Build Submodules" +echo "========================================" +echo "" + +# Configure git for long paths (Windows compatibility) +git config --global core.longpaths true 2>/dev/null || true + +# Configure git to use HTTPS instead of SSH for GitHub (needed for CI without SSH keys) +git config --global url."https://github.com/".insteadOf "git@github.com:" 2>/dev/null || true + +# Initialize top-level submodules +echo "Initializing top-level submodules..." +git submodule update --init --depth 1 m1-monitor +git submodule update --init --depth 1 m1-panner +git submodule update --init --depth 1 m1-player +git submodule update --init --depth 1 m1-orientationmanager +git submodule update --init --depth 1 m1-transcoder +git submodule update --init --depth 1 services/m1-system-helper 2>/dev/null || true + +# Function to initialize nested submodules for a project +init_project_submodules() { + local project=$1 + echo "" + echo "Initializing $project submodules..." + + cd "$project" + + # Core dependencies + git submodule update --init --depth 1 JUCE 2>/dev/null || true + git submodule update --init --depth 1 Modules/juce_murka 2>/dev/null || true + git submodule update --init --depth 1 Modules/juce_libvlc 2>/dev/null || true + git submodule update --init --depth 1 Modules/m1-sdk 2>/dev/null || true + # m1_orientation_client needs recursive init for its nested submodules (m1-mathematics) + git submodule update --init --recursive --depth 1 Modules/m1_orientation_client 2>/dev/null || { + # Fallback: init without recursive, then manually init nested + git submodule update --init --depth 1 Modules/m1_orientation_client 2>/dev/null || true + } + + # m1-sdk build dependencies (skip examples - they have deep paths) + if [ -d "Modules/m1-sdk" ]; then + cd Modules/m1-sdk + git submodule update --init --depth 1 libmach1spatial/deps/glm 2>/dev/null || true + git submodule update --init --depth 1 libmach1spatial/deps/nlohmann 2>/dev/null || true + git submodule update --init --depth 1 libmach1spatial/deps/pugixml 2>/dev/null || true + git submodule update --init --depth 1 libmach1spatial/deps/yaml 2>/dev/null || true + git submodule update --init --depth 1 libmach1spatial/deps/acutest 2>/dev/null || true + # SKIP: examples/* - These have deep paths and aren't needed for builds + cd ../.. + fi + + # juce_murka dependencies (Murka uses SSH URL that needs HTTPS conversion) + if [ -d "Modules/juce_murka" ]; then + cd Modules/juce_murka + + # fontstash uses HTTPS, should work directly + git submodule update --init --depth 1 fontstash 2>/dev/null || true + + # Murka uses SSH URL git@github.com:Kiberchaika/Murka.git - needs HTTPS conversion + git submodule init Murka 2>/dev/null || true + git submodule update --depth 1 Murka 2>/dev/null || { + echo " Murka shallow update failed, trying with URL conversion..." + rm -rf Murka 2>/dev/null || true + MURKA_URL=$(git config -f .gitmodules --get submodule.Murka.url 2>/dev/null || echo "git@github.com:Kiberchaika/Murka.git") + MURKA_URL_HTTPS=$(echo "$MURKA_URL" | sed 's|git@github.com:|https://github.com/|') + git clone --depth 1 "$MURKA_URL_HTTPS" Murka 2>/dev/null || true + } + + # Verify + if [ -f "Murka/src/Murka.h" ]; then + echo " Murka initialized" + else + echo " WARNING: Murka.h not found" + fi + cd ../.. + fi + + # juce_libvlc dependencies (m1-player only) + if [ -d "Modules/juce_libvlc" ]; then + cd Modules/juce_libvlc + git submodule update --init --depth 1 vlc 2>/dev/null || true + cd ../.. + fi + + # m1_orientation_client dependencies (includes m1-mathematics) + if [ -d "Modules/m1_orientation_client" ]; then + cd Modules/m1_orientation_client + # Initialize all direct submodules first + git submodule update --init --depth 1 2>/dev/null || true + + # Explicitly initialize m1-mathematics (REQUIRED for build) + echo " Initializing libs/m1-mathematics..." + + # Configure git to use HTTPS instead of SSH for GitHub (needed for CI) + git config --global url."https://github.com/".insteadOf "git@github.com:" 2>/dev/null || true + + git submodule init libs/m1-mathematics 2>/dev/null || true + git submodule update --depth 1 --force libs/m1-mathematics 2>/dev/null || { + echo " Shallow update failed, trying full clone..." + git submodule update --force libs/m1-mathematics 2>/dev/null || { + echo " Standard update failed, trying manual clone..." + # Last resort: manually clone the submodule + rm -rf libs/m1-mathematics 2>/dev/null || true + # Get the URL and convert SSH to HTTPS + MATH_URL=$(git config -f .gitmodules --get submodule.libs/m1-mathematics.url 2>/dev/null) + # Convert git@github.com:org/repo.git to https://github.com/org/repo.git + MATH_URL_HTTPS=$(echo "$MATH_URL" | sed 's|git@github.com:|https://github.com/|') + echo " Cloning from: $MATH_URL_HTTPS" + git clone --depth 1 "$MATH_URL_HTTPS" libs/m1-mathematics 2>/dev/null || { + # Try without .git suffix + MATH_URL_HTTPS="${MATH_URL_HTTPS%.git}" + git clone --depth 1 "$MATH_URL_HTTPS" libs/m1-mathematics 2>/dev/null || true + } + } + } + + # Verify m1-mathematics was initialized + if [ -f "libs/m1-mathematics/CMakeLists.txt" ]; then + echo " m1-mathematics initialized successfully" + else + echo " ERROR: m1-mathematics CMakeLists.txt not found!" + echo " Contents of libs/m1-mathematics:" + ls -la libs/m1-mathematics/ 2>/dev/null || echo " (empty or not found)" + fi + cd ../.. + fi + + cd .. +} + +# Initialize each project's submodules +echo "" +echo "--- Processing m1-monitor ---" +init_project_submodules "m1-monitor" + +echo "" +echo "--- Processing m1-panner ---" +init_project_submodules "m1-panner" + +echo "" +echo "--- Processing m1-player ---" +init_project_submodules "m1-player" + +# m1-orientationmanager - needs juce_murka (with Murka submodule) and m1_orientation_client (with m1-mathematics) +echo "" +echo "Initializing m1-orientationmanager submodules..." +cd m1-orientationmanager +git submodule update --init --depth 1 2>/dev/null || true + +# juce_murka has nested submodules (Murka uses SSH URL that needs conversion) +if [ -d "Modules/juce_murka" ]; then + echo " Initializing juce_murka submodules (Murka, fontstash)..." + cd Modules/juce_murka + + # Initialize fontstash (uses HTTPS, should work) + git submodule update --init --depth 1 fontstash 2>/dev/null || true + + # Initialize Murka (uses SSH URL git@github.com:Kiberchaika/Murka.git) + git submodule init Murka 2>/dev/null || true + git submodule update --depth 1 Murka 2>/dev/null || { + echo " Murka shallow update failed, trying with URL conversion..." + # Manual clone with HTTPS conversion + rm -rf Murka 2>/dev/null || true + mkdir -p Murka + MURKA_URL=$(git config -f .gitmodules --get submodule.Murka.url 2>/dev/null || echo "git@github.com:Kiberchaika/Murka.git") + MURKA_URL_HTTPS=$(echo "$MURKA_URL" | sed 's|git@github.com:|https://github.com/|') + echo " Cloning from: $MURKA_URL_HTTPS" + git clone --depth 1 "$MURKA_URL_HTTPS" Murka 2>/dev/null || { + echo " ERROR: Could not clone Murka" + } + } + + # Verify Murka was initialized (check for Murka.h header) + if [ -f "Murka/src/Murka.h" ]; then + echo " Murka initialized successfully" + else + echo " WARNING: Murka.h not found - juce_murka may fail to build" + ls -la Murka/ 2>/dev/null || echo " (Murka directory empty or missing)" + fi + cd ../.. +fi + +# Init nested submodules for m1_orientation_client (contains m1-mathematics) +if [ -d "Modules/m1_orientation_client" ]; then + cd Modules/m1_orientation_client + git submodule update --init --depth 1 2>/dev/null || true + + echo " Initializing libs/m1-mathematics..." + git config --global url."https://github.com/".insteadOf "git@github.com:" 2>/dev/null || true + + git submodule init libs/m1-mathematics 2>/dev/null || true + git submodule update --depth 1 --force libs/m1-mathematics 2>/dev/null || { + git submodule update --force libs/m1-mathematics 2>/dev/null || { + rm -rf libs/m1-mathematics 2>/dev/null || true + MATH_URL=$(git config -f .gitmodules --get submodule.libs/m1-mathematics.url 2>/dev/null) + MATH_URL_HTTPS=$(echo "$MATH_URL" | sed 's|git@github.com:|https://github.com/|') + git clone --depth 1 "$MATH_URL_HTTPS" libs/m1-mathematics 2>/dev/null || true + } + } + + if [ -f "libs/m1-mathematics/CMakeLists.txt" ]; then + echo " m1-mathematics initialized in m1-orientationmanager" + else + echo " WARNING: m1-mathematics not found in m1-orientationmanager" + fi + cd ../.. +fi +cd .. + +# m1-transcoder (electron app, skip juce_plugin deep submodules) +echo "" +echo "Initializing m1-transcoder submodules..." +cd m1-transcoder +# Only init top level, skip juce_plugin as it has the problematic deep paths +git submodule update --init --depth 1 2>/dev/null || { + echo " Note: Some m1-transcoder submodules skipped (not needed for electron build)" +} +cd .. + +# services/m1-system-helper +if [ -d "services/m1-system-helper" ]; then + echo "" + echo "Initializing m1-system-helper submodules..." + cd services/m1-system-helper + git submodule update --init --depth 1 2>/dev/null || true + cd ../.. +fi + +echo "" +echo "========================================" +echo "Submodules initialized successfully!" +echo "========================================" +echo "" +echo "Note: Example submodules (Unity, Unreal, FMOD, etc.) were skipped" +echo " to speed up builds and avoid path length issues on Windows." + diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..70c89ed --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,1218 @@ +name: Build and Release + +# This workflow builds all components and uploads artifacts. +# AAX signing requires a physical USB iLok dongle and must be done locally. +# +# Workflow: +# 1. CI/CD builds everything, signs non-AAX plugins, uploads to S3 +# 2. Local machine downloads artifacts, signs AAX, creates installer +# +# Use `make package-from-ci` locally to complete the release. + +on: + push: + tags: + - 'v*' + - 'test-cd' + branches: + - main + - master + pull_request: + branches: + - main + - master + workflow_dispatch: + inputs: + version: + description: 'Version number (e.g., 2.0.1) - leave empty to use commit SHA' + required: false + type: string + build_macos_arm64: + description: 'Build macOS ARM64' + required: false + default: true + type: boolean + build_macos_x86: + description: 'Build macOS x86_64' + required: false + default: true + type: boolean + build_windows: + description: 'Build Windows' + required: false + default: true + type: boolean + +env: + # Version - from tag, input, or commit SHA + VERSION: ${{ github.event.inputs.version || github.ref_name || github.sha }} + # S3 bucket for build artifacts + ARTIFACTS_BUCKET: mach1-build-artifacts + +jobs: + # ============================================================================= + # macOS ARM64 Build (Apple Silicon) + # ============================================================================= + build-macos-arm64: + if: ${{ github.event.inputs.build_macos_arm64 != 'false' }} + runs-on: macos-15 # ARM64 runner + timeout-minutes: 180 + + env: + APPLE_TEAM_CODE: ${{ secrets.APPLE_TEAM_CODE }} + APPLE_CODESIGN_CODE: ${{ secrets.APPLE_CODESIGN_CODE }} + APPLE_CODESIGN_ID: ${{ secrets.APPLE_CODESIGN_ID }} + APPLE_CODESIGN_INSTALLER_ID: ${{ secrets.APPLE_CODESIGN_INSTALLER_ID }} + APPLE_USERNAME: ${{ secrets.APPLE_USERNAME }} + ALTOOL_APPPASS: ${{ secrets.ALTOOL_APPPASS }} + PANNER_FREE_GUID: ${{ secrets.PANNER_FREE_GUID }} + MONITOR_FREE_GUID: ${{ secrets.MONITOR_FREE_GUID }} + # Aliases for electron-builder notarization (m1-transcoder/scripts/notarize.js) + APPLEID: ${{ secrets.APPLE_USERNAME }} + APPLEIDPASS: ${{ secrets.ALTOOL_APPPASS }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_CODE }} + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: false # Handle submodules manually for speed + fetch-depth: 0 + + # Initialize only build-required submodules (skip examples for faster builds) + - name: Initialize submodules + run: | + chmod +x .github/scripts/init-submodules.sh 2>/dev/null || true + .github/scripts/init-submodules.sh || { + # Fallback: recursive with depth + git submodule update --init --recursive --depth 1 + } + + - name: Setup Xcode + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: latest-stable + + # =========================================== + # Caching for faster builds + # =========================================== + # NOTE: Homebrew caching removed - symlinks don't cache properly + # VLC is cached in ~/.vlc-cache (outside build dir so `clean` doesn't delete it) + + - name: Cache VLC build + uses: actions/cache@v4 + id: vlc-cache + with: + path: ~/.vlc-cache + key: vlc-macos-${{ runner.arch }}-${{ hashFiles('m1-player/build_vlc.sh', 'm1-player/CMakeLists.txt') }} + restore-keys: | + vlc-macos-${{ runner.arch }}- + + - name: Cache npm dependencies + uses: actions/cache@v4 + with: + path: | + m1-transcoder/node_modules + ~/.npm + key: npm-macos-${{ runner.arch }}-${{ hashFiles('m1-transcoder/package-lock.json') }} + restore-keys: | + npm-macos-${{ runner.arch }}- + + - name: Cache Electron + uses: actions/cache@v4 + with: + path: | + ~/Library/Caches/electron + ~/Library/Caches/electron-builder + key: electron-macos-${{ runner.arch }}-${{ hashFiles('m1-transcoder/package-lock.json') }} + restore-keys: | + electron-macos-${{ runner.arch }}- + + - name: Cache Python venv + uses: actions/cache@v4 + with: + path: ~/.transcoder-venv + key: python-venv-macos-${{ runner.arch }}-pyinstaller + restore-keys: | + python-venv-macos-${{ runner.arch }}- + + # =========================================== + # Install dependencies + # =========================================== + + - name: Install Homebrew dependencies + run: | + brew update + brew install cmake ninja pkg-config autoconf automake libtool + brew install ffmpeg@6 && brew link ffmpeg@6 --force + brew install libass flac mpg123 libvpx x264 x265 dav1d aom + brew install opus libvorbis theora speex libogg libpng jpeg-turbo + brew install libssh2 srt libbluray aribb24 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '18' + + - name: Setup Python environment for m1-transcoder + run: | + # Create/reuse venv for Python packages + if [ ! -d "$HOME/.transcoder-venv" ]; then + python3 -m venv $HOME/.transcoder-venv + fi + source $HOME/.transcoder-venv/bin/activate + pip install --upgrade pip + pip install pyinstaller + echo "$HOME/.transcoder-venv/bin" >> $GITHUB_PATH + echo "Python venv ready with pyinstaller" + + - name: Import Apple Developer Certificate + env: + APPLE_CERTIFICATE_P12: ${{ secrets.APPLE_CERTIFICATE_P12 }} + APPLE_CERTIFICATE_P12_PWD: ${{ secrets.APPLE_CERTIFICATE_P12_PWD }} + MACOS_CI_KEYCHAIN_PWD: ${{ secrets.MACOS_CI_KEYCHAIN_PWD }} + run: | + # Create a temporary keychain + KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db + security create-keychain -p "$MACOS_CI_KEYCHAIN_PWD" $KEYCHAIN_PATH + security set-keychain-settings -lut 21600 $KEYCHAIN_PATH + security unlock-keychain -p "$MACOS_CI_KEYCHAIN_PWD" $KEYCHAIN_PATH + + # Import certificate + echo "$APPLE_CERTIFICATE_P12" | base64 --decode > $RUNNER_TEMP/certificate.p12 + security import $RUNNER_TEMP/certificate.p12 -P "$APPLE_CERTIFICATE_P12_PWD" \ + -A -t cert -f pkcs12 -k $KEYCHAIN_PATH + security list-keychain -d user -s $KEYCHAIN_PATH + + # Allow codesign to access the keychain + security set-key-partition-list -S apple-tool:,apple:,codesign: \ + -s -k "$MACOS_CI_KEYCHAIN_PWD" $KEYCHAIN_PATH + + echo "Certificate imported successfully" + + - name: Setup notarization credentials + run: | + xcrun notarytool store-credentials 'notarize-app' \ + --apple-id "$APPLE_USERNAME" \ + --team-id "$APPLE_TEAM_CODE" \ + --password "$ALTOOL_APPPASS" + + - name: Setup SDKs (VST2) + env: + VST2_SDK_URL: ${{ secrets.VST2_SDK_URL }} + run: | + mkdir -p SDKs + + # Download VST2 SDK if URL provided + if [ -n "$VST2_SDK_URL" ]; then + echo "Downloading VST2 SDK..." + curl -L "$VST2_SDK_URL" -o SDKs/vst2_sdk.zip + unzip -q SDKs/vst2_sdk.zip -d SDKs/ + + # Handle different archive structures + if [ -d "SDKs/VST2_SDK" ]; then + echo "VST2 SDK extracted to SDKs/VST2_SDK" + elif [ -d "SDKs/vstsdk2.4" ]; then + mv SDKs/vstsdk2.4 SDKs/VST2_SDK + echo "VST2 SDK extracted and renamed" + elif [ -d "SDKs/pluginterfaces" ]; then + mkdir -p SDKs/VST2_SDK + mv SDKs/pluginterfaces SDKs/VST2_SDK/ + echo "VST2 SDK headers extracted" + fi + + echo "BUILD_VST2=ON" >> $GITHUB_ENV + echo "VST2 SDK ready" + else + echo "VST2_SDK_URL not provided - VST2 builds will be skipped" + echo "BUILD_VST2=OFF" >> $GITHUB_ENV + fi + + - name: Setup Makefile.variables + run: | + # Set VST2 path only if SDK exists + VST2_PATH_VALUE="" + if [ "$BUILD_VST2" = "ON" ] && [ -d "${{ github.workspace }}/SDKs/VST2_SDK" ]; then + VST2_PATH_VALUE="${{ github.workspace }}/SDKs/VST2_SDK" + fi + + cat > Makefile.variables << EOF + M1SDK_PATH= + VST2_PATH=$VST2_PATH_VALUE + APPLE_TEAM_CODE=${{ secrets.APPLE_TEAM_CODE }} + APPLE_CODESIGN_ID=${{ secrets.APPLE_CODESIGN_ID }} + APPLE_CODESIGN_CODE=${{ secrets.APPLE_CODESIGN_CODE }} + APPLE_CODESIGN_INSTALLER_ID=${{ secrets.APPLE_CODESIGN_INSTALLER_ID }} + PANNER_FREE_GUID=${{ secrets.PANNER_FREE_GUID }} + MONITOR_FREE_GUID=${{ secrets.MONITOR_FREE_GUID }} + M1_GLOBAL_GUID=${{ secrets.M1_GLOBAL_GUID }} + APPLE_USERNAME=${{ secrets.APPLE_USERNAME }} + ALTOOL_APPPASS=${{ secrets.ALTOOL_APPPASS }} + EOF + echo "Makefile.variables created (VST2: $BUILD_VST2)" + + - name: Update version + run: | + VERSION_RAW="${VERSION#v}" + + # Check if version is valid numeric format (X.Y.Z or X.Y) + # CMake requires numeric-only versions + if [[ "$VERSION_RAW" =~ ^[0-9]+\.[0-9]+(\.[0-9]+)?$ ]]; then + VERSION_CLEAN="$VERSION_RAW" + echo "Release build, using version: $VERSION_CLEAN" + else + # For non-release builds (test-cd, feature branches, SHAs), use 0.0 + # After generate_version.sh appends date, this becomes 0.0.YYYYMMDD (valid semver) + # Using 0.0.0 would result in 0.0.0.YYYYMMDD (4 parts - invalid for npm/electron-builder) + VERSION_CLEAN="0.0" + echo "CI_BUILD_TAG=$VERSION_RAW" >> $GITHUB_ENV + echo "Non-release build ($VERSION_RAW), using CMake version: $VERSION_CLEAN" + fi + + echo "$VERSION_CLEAN" > VERSION + + # Update component versions (may add date suffixes) + make update-versions || echo "Version update completed with warnings" + echo "Final VERSION file:" + cat VERSION + + - name: Configure and build all components + run: | + echo "Configuring all components..." + + # Configure with or without VST2 based on SDK availability + if [ "$BUILD_VST2" = "ON" ]; then + echo "Building with VST2 support" + make configure + else + echo "Building without VST2 (SDK not available)" + # Configure each project manually without VST2 + cmake m1-monitor -Bm1-monitor/build -DBUILD_VST3=ON -DBUILD_AAX=ON -DBUILD_AU=ON -DBUILD_VST=OFF -DJUCE_COPY_PLUGIN_AFTER_BUILD=OFF + cmake m1-panner -Bm1-panner/build -DBUILD_VST3=ON -DBUILD_AAX=ON -DBUILD_AU=ON -DBUILD_VST=OFF -DJUCE_COPY_PLUGIN_AFTER_BUILD=OFF + cmake m1-player -Bm1-player/build -G "Xcode" -DLIBVLC_BUILD_FROM_SOURCE=ON -DLIBVLC_STATIC=OFF || true + cmake m1-orientationmanager -Bm1-orientationmanager/build + cmake services/m1-system-helper -Bservices/m1-system-helper/build + fi + + # Restore VLC from cache if available (cache is stored outside build dir) + if [ -d "$HOME/.vlc-cache/vlc-install" ]; then + echo "Restoring VLC from cache (saves 20-40 min)..." + mkdir -p m1-player/build + cp -r "$HOME/.vlc-cache/vlc-install" m1-player/build/ + cp -r "$HOME/.vlc-cache/vlc-source" m1-player/build/ 2>/dev/null || true + fi + + # Build VLC if needed + if [ -f "m1-player/build/vlc-install/lib/libvlc.dylib" ]; then + echo "VLC available, skipping build" + else + echo "Building VLC from source (this takes 20-40 minutes)..." + cd m1-player && ./build_vlc.sh build && cd .. + # Save to cache for next run + mkdir -p "$HOME/.vlc-cache" + cp -r m1-player/build/vlc-install "$HOME/.vlc-cache/" + cp -r m1-player/build/vlc-source "$HOME/.vlc-cache/" 2>/dev/null || true + fi + + # Always reconfigure m1-player to pick up VLC + cmake m1-player -Bm1-player/build -G "Xcode" -DLIBVLC_BUILD_FROM_SOURCE=ON -DLIBVLC_STATIC=OFF + + echo "Building all components..." + make build + + - name: Code sign non-AAX binaries + run: | + echo "Code signing VST3, AU, and Apps..." + # Sign everything EXCEPT AAX (requires USB iLok) + if [ "$BUILD_VST2" = "ON" ]; then + make codesign-vst || true + fi + make codesign-vst3 + make codesign-au + make codesign-apps + + - name: Notarize applications + run: | + echo "Notarizing applications..." + make notarize + + - name: Package build artifacts + run: | + echo "Packaging build artifacts..." + mkdir -p artifacts/macos-arm64 + + # Copy plugin builds + cp -r m1-monitor/build/M1-Monitor_artefacts/ artifacts/macos-arm64/M1-Monitor/ + cp -r m1-panner/build/M1-Panner_artefacts/ artifacts/macos-arm64/M1-Panner/ + cp -r m1-player/build/M1-Player_artefacts/ artifacts/macos-arm64/M1-Player/ + cp -r m1-orientationmanager/build/m1-orientationmanager_artefacts/ artifacts/macos-arm64/m1-orientationmanager/ + cp -r services/m1-system-helper/build/m1-system-helper_artefacts/ artifacts/macos-arm64/m1-system-helper/ + + # Create archive + cd artifacts && tar -czvf macos-arm64-builds.tar.gz macos-arm64/ + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: macos-arm64-builds + path: artifacts/macos-arm64-builds.tar.gz + retention-days: 30 + + - name: Upload to S3 artifacts bucket + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + run: | + VERSION_RAW="${VERSION#v}" + COMMIT_SHA="${{ github.sha }}" + SHORT_SHA="${COMMIT_SHA:0:8}" + + # Use original tag/branch name for S3 path (not the CMake-safe version) + S3_VERSION="$VERSION_RAW" + + echo "Uploading to S3..." + aws s3 cp artifacts/macos-arm64-builds.tar.gz \ + "s3://$ARTIFACTS_BUCKET/builds/$S3_VERSION/macos-arm64-builds.tar.gz" \ + --region us-east-1 + + # Also upload with commit SHA for exact version matching + aws s3 cp artifacts/macos-arm64-builds.tar.gz \ + "s3://$ARTIFACTS_BUCKET/commits/$SHORT_SHA/macos-arm64-builds.tar.gz" \ + --region us-east-1 + + echo "Artifacts uploaded to:" + echo " s3://$ARTIFACTS_BUCKET/builds/$S3_VERSION/" + echo " s3://$ARTIFACTS_BUCKET/commits/$SHORT_SHA/" + + # ============================================================================= + # macOS x86_64 Build (Intel) - for m1-player only + # ============================================================================= + build-macos-x86: + if: ${{ github.event.inputs.build_macos_x86 != 'false' }} + runs-on: macos-15-intel # Intel runner + timeout-minutes: 180 + + env: + APPLE_TEAM_CODE: ${{ secrets.APPLE_TEAM_CODE }} + APPLE_CODESIGN_CODE: ${{ secrets.APPLE_CODESIGN_CODE }} + APPLE_CODESIGN_ID: ${{ secrets.APPLE_CODESIGN_ID }} + APPLE_CODESIGN_INSTALLER_ID: ${{ secrets.APPLE_CODESIGN_INSTALLER_ID }} + APPLE_USERNAME: ${{ secrets.APPLE_USERNAME }} + ALTOOL_APPPASS: ${{ secrets.ALTOOL_APPPASS }} + PANNER_FREE_GUID: ${{ secrets.PANNER_FREE_GUID }} + MONITOR_FREE_GUID: ${{ secrets.MONITOR_FREE_GUID }} + # Aliases for electron-builder notarization (m1-transcoder/scripts/notarize.js) + APPLEID: ${{ secrets.APPLE_USERNAME }} + APPLEIDPASS: ${{ secrets.ALTOOL_APPPASS }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_CODE }} + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: false # Handle submodules manually for speed + fetch-depth: 0 + + # Initialize only build-required submodules (skip examples for faster builds) + - name: Initialize submodules + run: | + chmod +x .github/scripts/init-submodules.sh 2>/dev/null || true + .github/scripts/init-submodules.sh || { + # Fallback: recursive with depth + git submodule update --init --recursive --depth 1 + } + + - name: Setup Xcode + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: latest-stable + + # =========================================== + # Caching for faster builds + # =========================================== + # NOTE: Homebrew caching removed - symlinks don't cache properly + # VLC is cached in ~/.vlc-cache (outside build dir so `clean` doesn't delete it) + + - name: Cache VLC build + uses: actions/cache@v4 + id: vlc-cache-x86 + with: + path: ~/.vlc-cache + key: vlc-macos-${{ runner.arch }}-${{ hashFiles('m1-player/build_vlc.sh', 'm1-player/CMakeLists.txt') }} + restore-keys: | + vlc-macos-${{ runner.arch }}- + + - name: Cache npm dependencies + uses: actions/cache@v4 + with: + path: | + m1-transcoder/node_modules + ~/.npm + key: npm-macos-${{ runner.arch }}-${{ hashFiles('m1-transcoder/package-lock.json') }} + restore-keys: | + npm-macos-${{ runner.arch }}- + + - name: Cache Electron + uses: actions/cache@v4 + with: + path: | + ~/Library/Caches/electron + ~/Library/Caches/electron-builder + key: electron-macos-${{ runner.arch }}-${{ hashFiles('m1-transcoder/package-lock.json') }} + restore-keys: | + electron-macos-${{ runner.arch }}- + + - name: Cache Python venv + uses: actions/cache@v4 + with: + path: ~/.transcoder-venv + key: python-venv-macos-${{ runner.arch }}-pyinstaller + restore-keys: | + python-venv-macos-${{ runner.arch }}- + + # =========================================== + # Install dependencies + # =========================================== + + - name: Install Homebrew dependencies + run: | + brew update + brew install cmake ninja pkg-config autoconf automake libtool + brew install ffmpeg@6 && brew link ffmpeg@6 --force + brew install libass flac mpg123 libvpx x264 x265 dav1d aom + brew install opus libvorbis theora speex libogg libpng jpeg-turbo + brew install libssh2 srt libbluray aribb24 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '18' + + - name: Setup Python environment for m1-transcoder + run: | + # Create/reuse venv for Python packages + if [ ! -d "$HOME/.transcoder-venv" ]; then + python3 -m venv $HOME/.transcoder-venv + fi + source $HOME/.transcoder-venv/bin/activate + pip install --upgrade pip + pip install pyinstaller + echo "$HOME/.transcoder-venv/bin" >> $GITHUB_PATH + echo "Python venv ready with pyinstaller" + + - name: Import Apple Developer Certificate + env: + APPLE_CERTIFICATE_P12: ${{ secrets.APPLE_CERTIFICATE_P12 }} + APPLE_CERTIFICATE_P12_PWD: ${{ secrets.APPLE_CERTIFICATE_P12_PWD }} + MACOS_CI_KEYCHAIN_PWD: ${{ secrets.MACOS_CI_KEYCHAIN_PWD }} + run: | + KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db + security create-keychain -p "$MACOS_CI_KEYCHAIN_PWD" $KEYCHAIN_PATH + security set-keychain-settings -lut 21600 $KEYCHAIN_PATH + security unlock-keychain -p "$MACOS_CI_KEYCHAIN_PWD" $KEYCHAIN_PATH + + echo "$APPLE_CERTIFICATE_P12" | base64 --decode > $RUNNER_TEMP/certificate.p12 + security import $RUNNER_TEMP/certificate.p12 -P "$APPLE_CERTIFICATE_P12_PWD" \ + -A -t cert -f pkcs12 -k $KEYCHAIN_PATH + security list-keychain -d user -s $KEYCHAIN_PATH + security set-key-partition-list -S apple-tool:,apple:,codesign: \ + -s -k "$MACOS_CI_KEYCHAIN_PWD" $KEYCHAIN_PATH + + - name: Setup notarization credentials + run: | + xcrun notarytool store-credentials 'notarize-app' \ + --apple-id "$APPLE_USERNAME" \ + --team-id "$APPLE_TEAM_CODE" \ + --password "$ALTOOL_APPPASS" + + - name: Setup SDKs (VST2) + env: + VST2_SDK_URL: ${{ secrets.VST2_SDK_URL }} + run: | + mkdir -p SDKs + + if [ -n "$VST2_SDK_URL" ]; then + echo "Downloading VST2 SDK..." + curl -L "$VST2_SDK_URL" -o SDKs/vst2_sdk.zip + unzip -q SDKs/vst2_sdk.zip -d SDKs/ + + # Handle different archive structures + if [ -d "SDKs/VST2_SDK" ]; then + echo "VST2 SDK extracted" + elif [ -d "SDKs/vstsdk2.4" ]; then + mv SDKs/vstsdk2.4 SDKs/VST2_SDK + elif [ -d "SDKs/pluginterfaces" ]; then + mkdir -p SDKs/VST2_SDK + mv SDKs/pluginterfaces SDKs/VST2_SDK/ + fi + + echo "BUILD_VST2=ON" >> $GITHUB_ENV + else + echo "VST2_SDK_URL not provided - VST2 builds will be skipped" + echo "BUILD_VST2=OFF" >> $GITHUB_ENV + fi + + - name: Setup Makefile.variables + run: | + VST2_PATH_VALUE="" + if [ "$BUILD_VST2" = "ON" ] && [ -d "${{ github.workspace }}/SDKs/VST2_SDK" ]; then + VST2_PATH_VALUE="${{ github.workspace }}/SDKs/VST2_SDK" + fi + + cat > Makefile.variables << EOF + M1SDK_PATH= + VST2_PATH=$VST2_PATH_VALUE + APPLE_TEAM_CODE=${{ secrets.APPLE_TEAM_CODE }} + APPLE_CODESIGN_ID=${{ secrets.APPLE_CODESIGN_ID }} + APPLE_CODESIGN_CODE=${{ secrets.APPLE_CODESIGN_CODE }} + APPLE_CODESIGN_INSTALLER_ID=${{ secrets.APPLE_CODESIGN_INSTALLER_ID }} + PANNER_FREE_GUID=${{ secrets.PANNER_FREE_GUID }} + MONITOR_FREE_GUID=${{ secrets.MONITOR_FREE_GUID }} + M1_GLOBAL_GUID=${{ secrets.M1_GLOBAL_GUID }} + APPLE_USERNAME=${{ secrets.APPLE_USERNAME }} + ALTOOL_APPPASS=${{ secrets.ALTOOL_APPPASS }} + EOF + + - name: Update version + run: | + VERSION_RAW="${VERSION#v}" + + # Check if version is valid numeric format (X.Y.Z or X.Y) + if [[ "$VERSION_RAW" =~ ^[0-9]+\.[0-9]+(\.[0-9]+)?$ ]]; then + VERSION_CLEAN="$VERSION_RAW" + echo "Release build, using version: $VERSION_CLEAN" + else + # Use 0.0 so after date append we get 0.0.YYYYMMDD (valid 3-part semver) + VERSION_CLEAN="0.0" + echo "CI_BUILD_TAG=$VERSION_RAW" >> $GITHUB_ENV + echo "Non-release build ($VERSION_RAW), using CMake version: $VERSION_CLEAN" + fi + + echo "$VERSION_CLEAN" > VERSION + make update-versions || echo "Version update completed with warnings" + cat VERSION + + - name: Configure and build all components + run: | + # Configure with or without VST2 based on SDK availability + if [ "$BUILD_VST2" = "ON" ]; then + echo "Building with VST2 support" + make configure + else + echo "Building without VST2 (SDK not available)" + cmake m1-monitor -Bm1-monitor/build -DBUILD_VST3=ON -DBUILD_AAX=ON -DBUILD_AU=ON -DBUILD_VST=OFF -DJUCE_COPY_PLUGIN_AFTER_BUILD=OFF + cmake m1-panner -Bm1-panner/build -DBUILD_VST3=ON -DBUILD_AAX=ON -DBUILD_AU=ON -DBUILD_VST=OFF -DJUCE_COPY_PLUGIN_AFTER_BUILD=OFF + cmake m1-player -Bm1-player/build -G "Xcode" -DLIBVLC_BUILD_FROM_SOURCE=ON -DLIBVLC_STATIC=OFF || true + cmake m1-orientationmanager -Bm1-orientationmanager/build + cmake services/m1-system-helper -Bservices/m1-system-helper/build + fi + + # Restore VLC from cache if available (cache is stored outside build dir) + if [ -d "$HOME/.vlc-cache/vlc-install" ]; then + echo "Restoring VLC from cache (saves 20-40 min)..." + mkdir -p m1-player/build + cp -r "$HOME/.vlc-cache/vlc-install" m1-player/build/ + cp -r "$HOME/.vlc-cache/vlc-source" m1-player/build/ 2>/dev/null || true + fi + + # Build VLC if needed + if [ -f "m1-player/build/vlc-install/lib/libvlc.dylib" ]; then + echo "VLC available, skipping build" + else + echo "Building VLC from source (this takes 20-40 minutes)..." + cd m1-player && ./build_vlc.sh build && cd .. + # Save to cache for next run + mkdir -p "$HOME/.vlc-cache" + cp -r m1-player/build/vlc-install "$HOME/.vlc-cache/" + cp -r m1-player/build/vlc-source "$HOME/.vlc-cache/" 2>/dev/null || true + fi + + # Always reconfigure m1-player to pick up VLC + cmake m1-player -Bm1-player/build -G "Xcode" -DLIBVLC_BUILD_FROM_SOURCE=ON -DLIBVLC_STATIC=OFF + + make build + + - name: Code sign non-AAX binaries + run: | + # Sign everything EXCEPT AAX (requires USB iLok) + if [ "$BUILD_VST2" = "ON" ]; then + make codesign-vst || true + fi + make codesign-vst3 + make codesign-au + make codesign-apps + + - name: Notarize applications + run: | + make notarize + + - name: Package build artifacts + run: | + mkdir -p artifacts/macos-x86 + cp -r m1-monitor/build/M1-Monitor_artefacts/ artifacts/macos-x86/M1-Monitor/ + cp -r m1-panner/build/M1-Panner_artefacts/ artifacts/macos-x86/M1-Panner/ + cp -r m1-player/build/M1-Player_artefacts/ artifacts/macos-x86/M1-Player/ + cp -r m1-orientationmanager/build/m1-orientationmanager_artefacts/ artifacts/macos-x86/m1-orientationmanager/ + cp -r services/m1-system-helper/build/m1-system-helper_artefacts/ artifacts/macos-x86/m1-system-helper/ + cd artifacts && tar -czvf macos-x86-builds.tar.gz macos-x86/ + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: macos-x86-builds + path: artifacts/macos-x86-builds.tar.gz + retention-days: 30 + + - name: Upload to S3 artifacts bucket + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + run: | + VERSION_RAW="${VERSION#v}" + COMMIT_SHA="${{ github.sha }}" + SHORT_SHA="${COMMIT_SHA:0:8}" + + aws s3 cp artifacts/macos-x86-builds.tar.gz \ + "s3://$ARTIFACTS_BUCKET/builds/$VERSION_RAW/macos-x86-builds.tar.gz" \ + --region us-east-1 + + aws s3 cp artifacts/macos-x86-builds.tar.gz \ + "s3://$ARTIFACTS_BUCKET/commits/$SHORT_SHA/macos-x86-builds.tar.gz" \ + --region us-east-1 + + # ============================================================================= + # Windows Build + # ============================================================================= + build-windows: + if: ${{ github.event.inputs.build_windows != 'false' }} + runs-on: windows-latest + timeout-minutes: 180 + + env: + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_SECRET_ID }} + + steps: + # Enable long paths on Windows to handle deeply nested submodules + - name: Enable long paths + run: | + git config --system core.longpaths true + # Also set via registry for good measure + New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force | Out-Null + shell: pwsh + + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: false # We'll handle submodules manually + fetch-depth: 0 + + # Initialize submodules with depth limit to avoid deep nesting issues + - name: Initialize submodules (with depth limit) + run: | + # Use the shared script (works on Windows via Git Bash) + bash .github/scripts/init-submodules.sh + shell: bash + continue-on-error: false + + - name: Setup MSVC + uses: microsoft/setup-msbuild@v2 + + - name: Setup CMake + uses: lukka/get-cmake@latest + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '18' + + # =========================================== + # Caching for faster builds + # =========================================== + + - name: Cache npm dependencies + uses: actions/cache@v4 + with: + path: | + m1-transcoder/node_modules + ~\AppData\Local\npm-cache + key: npm-windows-${{ hashFiles('m1-transcoder/package-lock.json') }} + restore-keys: | + npm-windows- + + - name: Cache Electron + uses: actions/cache@v4 + with: + path: | + ~\AppData\Local\electron\Cache + ~\AppData\Local\electron-builder\Cache + key: electron-windows-${{ hashFiles('m1-transcoder/package-lock.json') }} + restore-keys: | + electron-windows- + + - name: Cache Python venv + uses: actions/cache@v4 + with: + path: ~\.transcoder-venv + key: python-venv-windows-pyinstaller + restore-keys: | + python-venv-windows- + + # =========================================== + # Install dependencies + # =========================================== + + - name: Setup Python environment for m1-transcoder + run: | + if (-not (Test-Path "$env:USERPROFILE\.transcoder-venv")) { + python -m venv $env:USERPROFILE\.transcoder-venv + } + & $env:USERPROFILE\.transcoder-venv\Scripts\Activate.ps1 + pip install --upgrade pip + pip install pyinstaller + echo "$env:USERPROFILE\.transcoder-venv\Scripts" >> $env:GITHUB_PATH + echo "Python venv ready with pyinstaller" + + - name: Install dependencies + run: | + choco install innosetup -y + choco install 7zip -y + + - name: Setup Azure Trusted Signing + run: | + # Download Microsoft Trusted Signing Client (contains the Dlib for signtool) + # This is the same approach used locally via aax-signtool.bat + + $packageName = "Microsoft.Trusted.Signing.Client" + $outputDir = "trusted-signing-client" + + # Use nuget to download the package + nuget install $packageName -OutputDirectory $outputDir -Source https://api.nuget.org/v3/index.json + + # Find the Dlib DLL + $dlibPath = Get-ChildItem -Path $outputDir -Recurse -Filter "Azure.CodeSigning.Dlib.dll" | + Where-Object { $_.FullName -like "*x64*" } | + Select-Object -First 1 + + if (-not $dlibPath) { + # Fallback: try any location + $dlibPath = Get-ChildItem -Path $outputDir -Recurse -Filter "Azure.CodeSigning.Dlib.dll" | + Select-Object -First 1 + } + + if ($dlibPath) { + echo "Found Azure.CodeSigning.Dlib.dll at: $($dlibPath.FullName)" + echo "ACS_DLIB=$($dlibPath.FullName)" >> $env:GITHUB_ENV + } else { + echo "ERROR: Could not find Azure.CodeSigning.Dlib.dll" + Get-ChildItem -Path $outputDir -Recurse | ForEach-Object { echo $_.FullName } + exit 1 + } + + # Set other required paths + echo "ACS_JSON=${{ github.workspace }}\signing-metadata.json" >> $env:GITHUB_ENV + + # Find signtool.exe from Windows SDK + $signtoolPaths = @( + "C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64\signtool.exe", + "C:\Program Files (x86)\Windows Kits\10\bin\10.0.22000.0\x64\signtool.exe", + "C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x64\signtool.exe" + ) + + $signtool = $signtoolPaths | Where-Object { Test-Path $_ } | Select-Object -First 1 + + if (-not $signtool) { + # Find any signtool + $signtool = Get-ChildItem -Path "C:\Program Files (x86)\Windows Kits\10\bin" -Recurse -Filter "signtool.exe" | + Where-Object { $_.FullName -like "*x64*" } | + Select-Object -First 1 -ExpandProperty FullName + } + + if ($signtool) { + echo "Found signtool at: $signtool" + echo "SIGNTOOL_PATH=$signtool" >> $env:GITHUB_ENV + } else { + echo "ERROR: Could not find signtool.exe" + exit 1 + } + + echo "Azure Trusted Signing setup complete" + + - name: Setup SDKs (VST2) + env: + VST2_SDK_URL: ${{ secrets.VST2_SDK_URL }} + run: | + New-Item -ItemType Directory -Force -Path SDKs + + if ($env:VST2_SDK_URL) { + echo "Downloading VST2 SDK..." + Invoke-WebRequest -Uri $env:VST2_SDK_URL -OutFile SDKs\vst2_sdk.zip + Expand-Archive -Path SDKs\vst2_sdk.zip -DestinationPath SDKs + + # Handle different archive structures + if (Test-Path "SDKs\VST2_SDK") { + echo "VST2 SDK extracted" + } elseif (Test-Path "SDKs\vstsdk2.4") { + Rename-Item "SDKs\vstsdk2.4" "SDKs\VST2_SDK" + } elseif (Test-Path "SDKs\pluginterfaces") { + New-Item -ItemType Directory -Force -Path "SDKs\VST2_SDK" + Move-Item "SDKs\pluginterfaces" "SDKs\VST2_SDK\" + } + + echo "BUILD_VST2=ON" >> $env:GITHUB_ENV + } else { + echo "VST2_SDK_URL not provided - VST2 builds will be skipped" + echo "BUILD_VST2=OFF" >> $env:GITHUB_ENV + } + + - name: Create signing metadata + run: | + @" + { + "Endpoint": "https://eus.codesigning.azure.net/", + "CodeSigningAccountName": "Mach1", + "CertificateProfileName": "mach1-cert" + } + "@ | Out-File -FilePath signing-metadata.json -Encoding UTF8 + + - name: Setup Makefile.variables + shell: bash + run: | + # Set VST2 path only if SDK exists + VST2_PATH_VALUE="" + if [ "$BUILD_VST2" = "ON" ] && [ -d "SDKs/VST2_SDK" ]; then + VST2_PATH_VALUE="${{ github.workspace }}/SDKs/VST2_SDK" + fi + + cat > Makefile.variables << EOF + M1SDK_PATH= + VST2_PATH=$VST2_PATH_VALUE + WIN_INNO_PATH=C:\Program Files (x86)\Inno Setup 6\ISCC.exe + WIN_SIGNTOOL_PATH=C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64\signtool.exe + AZURE_DLIB_PATH=${{ github.workspace }}\azure-codesigning\lib\net8.0\any\Azure.CodeSigning.Dlib.dll + AZURE_METADATA_PATH=${{ github.workspace }}\signing-metadata.json + AZURE_TIMESTAMP_URL=http://timestamp.acs.microsoft.com + AZURE_CLIENT_ID=${{ secrets.AZURE_CLIENT_ID }} + AZURE_TENANT_ID=${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_SECRET=${{ secrets.AZURE_SECRET_ID }} + PANNER_FREE_GUID=${{ secrets.PANNER_FREE_GUID }} + MONITOR_FREE_GUID=${{ secrets.MONITOR_FREE_GUID }} + M1_GLOBAL_GUID=${{ secrets.M1_GLOBAL_GUID }} + EOF + echo "Makefile.variables created (VST2: $BUILD_VST2)" + + - name: Update version + shell: bash + run: | + VERSION_RAW="${VERSION#v}" + + # Check if version is valid numeric format (X.Y.Z or X.Y) + if [[ "$VERSION_RAW" =~ ^[0-9]+\.[0-9]+(\.[0-9]+)?$ ]]; then + VERSION_CLEAN="$VERSION_RAW" + echo "Release build, using version: $VERSION_CLEAN" + else + # Use 0.0 so after date append we get 0.0.YYYYMMDD (valid 3-part semver) + VERSION_CLEAN="0.0" + echo "CI_BUILD_TAG=$VERSION_RAW" >> $GITHUB_ENV + echo "Non-release build ($VERSION_RAW), using CMake version: $VERSION_CLEAN" + fi + + echo "$VERSION_CLEAN" > VERSION + + # Update component versions (propagates to package.json for electron-builder) + # Note: make update-versions skips Windows in Makefile, so run script directly + chmod +x ./installer/generate_version.sh + ./installer/generate_version.sh || echo "Version update completed with warnings" + echo "Final versions:" + cat VERSION + cat m1-transcoder/VERSION 2>/dev/null || true + + - name: Download VLC SDK for Windows + id: vlc-download + run: | + cd m1-player + call build_vlc.bat build + if exist "build\vlc-install\lib\libvlc.lib" ( + echo VLC SDK downloaded successfully + echo vlc_available=true >> %GITHUB_OUTPUT% + ) else ( + echo WARNING: VLC SDK download may have failed + echo vlc_available=false >> %GITHUB_OUTPUT% + ) + shell: cmd + continue-on-error: true + + - name: Configure projects + shell: bash + run: | + cmake m1-monitor -Bm1-monitor/build -DBUILD_VST3=ON -DBUILD_AAX=ON -DJUCE_COPY_PLUGIN_AFTER_BUILD=OFF + cmake m1-panner -Bm1-panner/build -DBUILD_VST3=ON -DBUILD_AAX=ON -DJUCE_COPY_PLUGIN_AFTER_BUILD=OFF + + # Only configure m1-player if VLC is available + if [ -f "m1-player/build/vlc-install/lib/libvlc.lib" ]; then + echo "VLC SDK found, configuring m1-player..." + cmake m1-player -Bm1-player/build -DLIBVLC_BUILD_FROM_SOURCE=ON -DLIBVLC_STATIC=OFF + else + echo "WARNING: VLC SDK not found, skipping m1-player configuration" + fi + + cmake m1-orientationmanager -Bm1-orientationmanager/build + cmake services/m1-system-helper -Bservices/m1-system-helper/build + + - name: Build projects + shell: bash + run: | + cmake --build m1-monitor/build --config Release + cmake --build m1-panner/build --config Release + + # Only build m1-player if it was configured + if [ -d "m1-player/build" ] && [ -f "m1-player/build/vlc-install/lib/libvlc.lib" ]; then + echo "Building m1-player..." + cmake --build m1-player/build --config Release + else + echo "WARNING: Skipping m1-player build (VLC SDK not available)" + fi + + cmake --build m1-orientationmanager/build --config Release + cmake --build services/m1-system-helper/build --config Release + + - name: Build Transcoder + run: | + cd m1-transcoder + npm install + npm run package-win + + - name: Code sign Windows binaries with Azure Trusted Signing + timeout-minutes: 10 + env: + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_SECRET_ID }} + run: | + # Sign binaries using signtool + Azure Trusted Signing Dlib + # This matches the local aax-signtool.bat approach + + # Skip if secrets not configured + if ([string]::IsNullOrEmpty($env:AZURE_CLIENT_ID)) { + echo "Azure Trusted Signing not configured, skipping code signing" + exit 0 + } + + # Verify Azure credentials are set + if ([string]::IsNullOrEmpty($env:AZURE_CLIENT_SECRET)) { + echo "WARNING: AZURE_CLIENT_SECRET (from AZURE_SECRET_ID) is empty" + echo "Code signing may fail - check org secrets" + } + + # Verify required files exist + if (-not (Test-Path $env:SIGNTOOL_PATH)) { + echo "ERROR: signtool.exe not found at $env:SIGNTOOL_PATH" + exit 1 + } + if (-not (Test-Path $env:ACS_DLIB)) { + echo "ERROR: Azure.CodeSigning.Dlib.dll not found at $env:ACS_DLIB" + exit 1 + } + if (-not (Test-Path $env:ACS_JSON)) { + echo "ERROR: signing-metadata.json not found at $env:ACS_JSON" + exit 1 + } + + # Collect files to sign + $filesToSign = @() + + # Find VST3 plugins (the actual .vst3 DLL inside the bundle) + Get-ChildItem -Path "m1-monitor/build" -Recurse -Filter "*.vst3" -File | ForEach-Object { $filesToSign += $_.FullName } + Get-ChildItem -Path "m1-panner/build" -Recurse -Filter "*.vst3" -File | ForEach-Object { $filesToSign += $_.FullName } + + # Find executables + @( + "m1-player/build/M1-Player_artefacts/Release/M1-Player.exe", + "m1-orientationmanager/build/m1-orientationmanager_artefacts/Release/m1-orientationmanager.exe", + "services/m1-system-helper/build/m1-system-helper_artefacts/Release/m1-system-helper.exe" + ) | ForEach-Object { + if (Test-Path $_) { $filesToSign += $_ } + } + + echo "Files to sign: $($filesToSign.Count)" + echo "Using signtool: $env:SIGNTOOL_PATH" + echo "Using Dlib: $env:ACS_DLIB" + echo "Using metadata: $env:ACS_JSON" + + $successCount = 0 + $failCount = 0 + + foreach ($file in $filesToSign) { + echo "" + echo "Signing: $file" + + # Use the same command as aax-signtool.bat + & $env:SIGNTOOL_PATH sign /v /fd SHA256 ` + /tr "http://timestamp.acs.microsoft.com" /td SHA256 ` + /dlib $env:ACS_DLIB ` + /dmdf $env:ACS_JSON ` + $file + + if ($LASTEXITCODE -eq 0) { + echo "SUCCESS: Signed $file" + $successCount++ + } else { + echo "WARNING: Failed to sign $file (exit code: $LASTEXITCODE)" + $failCount++ + } + } + + echo "" + echo "==========================================" + echo "Signing complete: $successCount succeeded, $failCount failed" + echo "==========================================" + shell: pwsh + continue-on-error: true + + - name: Package build artifacts + run: | + mkdir -p artifacts/windows + + # Copy each artifact, handling missing ones gracefully + if [ -d "m1-monitor/build/M1-Monitor_artefacts/Release" ]; then + cp -r m1-monitor/build/M1-Monitor_artefacts/Release/ artifacts/windows/M1-Monitor/ + echo "Packaged: M1-Monitor" + else + echo "WARNING: M1-Monitor artifacts not found" + fi + + if [ -d "m1-panner/build/M1-Panner_artefacts/Release" ]; then + cp -r m1-panner/build/M1-Panner_artefacts/Release/ artifacts/windows/M1-Panner/ + echo "Packaged: M1-Panner" + else + echo "WARNING: M1-Panner artifacts not found" + fi + + if [ -d "m1-player/build/M1-Player_artefacts/Release" ]; then + cp -r m1-player/build/M1-Player_artefacts/Release/ artifacts/windows/M1-Player/ + echo "Packaged: M1-Player" + else + echo "WARNING: M1-Player artifacts not found (VLC SDK may have failed to download)" + fi + + if [ -d "m1-orientationmanager/build/m1-orientationmanager_artefacts/Release" ]; then + cp -r m1-orientationmanager/build/m1-orientationmanager_artefacts/Release/ artifacts/windows/m1-orientationmanager/ + echo "Packaged: m1-orientationmanager" + else + echo "WARNING: m1-orientationmanager artifacts not found" + fi + + if [ -d "services/m1-system-helper/build/m1-system-helper_artefacts/Release" ]; then + cp -r services/m1-system-helper/build/m1-system-helper_artefacts/Release/ artifacts/windows/m1-system-helper/ + echo "Packaged: m1-system-helper" + else + echo "WARNING: m1-system-helper artifacts not found" + fi + + echo "" + echo "Artifacts packaged:" + ls -la artifacts/windows/ || true + shell: bash + + - name: Create artifacts archive + run: | + cd artifacts + 7z a -tzip windows-builds.zip windows/ + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: windows-builds + path: artifacts/windows-builds.zip + retention-days: 30 + + - name: Upload to S3 artifacts bucket + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + run: | + $VersionRaw = "$env:VERSION" -replace '^v', '' + $CommitSha = "${{ github.sha }}" + $ShortSha = $CommitSha.Substring(0, 8) + + aws s3 cp artifacts/windows-builds.zip ` + "s3://$env:ARTIFACTS_BUCKET/builds/$VersionRaw/windows-builds.zip" ` + --region us-east-1 + + aws s3 cp artifacts/windows-builds.zip ` + "s3://$env:ARTIFACTS_BUCKET/commits/$ShortSha/windows-builds.zip" ` + --region us-east-1 + + # ============================================================================= + # Create Build Summary + # ============================================================================= + summary: + needs: [build-macos-arm64, build-macos-x86, build-windows] + if: always() + runs-on: ubuntu-latest + + steps: + - name: Determine version info + id: version + run: | + VERSION="${{ github.event.inputs.version || github.ref_name }}" + VERSION="${VERSION#v}" + COMMIT_SHA="${{ github.sha }}" + SHORT_SHA="${COMMIT_SHA:0:8}" + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "short_sha=$SHORT_SHA" >> $GITHUB_OUTPUT + + - name: Create build summary + run: | + VERSION="${{ steps.version.outputs.version }}" + SHORT_SHA="${{ steps.version.outputs.short_sha }}" + + echo "## Build Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Version:** $VERSION" >> $GITHUB_STEP_SUMMARY + echo "**Commit:** $SHORT_SHA" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + echo "### Build Status" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + if [ "${{ needs.build-macos-arm64.result }}" == "success" ]; then + echo "- macOS ARM64 (Apple Silicon)" >> $GITHUB_STEP_SUMMARY + else + echo "- macOS ARM64 (Apple Silicon) - ${{ needs.build-macos-arm64.result }}" >> $GITHUB_STEP_SUMMARY + fi + + if [ "${{ needs.build-macos-x86.result }}" == "success" ]; then + echo "- macOS x86_64 (Intel)" >> $GITHUB_STEP_SUMMARY + else + echo "- macOS x86_64 (Intel) - ${{ needs.build-macos-x86.result }}" >> $GITHUB_STEP_SUMMARY + fi + + if [ "${{ needs.build-windows.result }}" == "success" ]; then + echo "- Windows x64" >> $GITHUB_STEP_SUMMARY + else + echo "- Windows x64 - ${{ needs.build-windows.result }}" >> $GITHUB_STEP_SUMMARY + fi + + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Download Artifacts" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Artifacts uploaded to S3:" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "s3://mach1-build-artifacts/builds/$VERSION/" >> $GITHUB_STEP_SUMMARY + echo "s3://mach1-build-artifacts/commits/$SHORT_SHA/" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + echo "### Complete Release Locally" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "AAX signing requires a physical USB iLok dongle. Run locally:" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY + echo "# Download CI artifacts and complete the release" >> $GITHUB_STEP_SUMMARY + echo "make package-from-ci VERSION=$VERSION" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "# Or by commit SHA" >> $GITHUB_STEP_SUMMARY + echo "make package-from-ci COMMIT=$SHORT_SHA" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + diff --git a/Makefile b/Makefile index 24ae395..672438a 100755 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ # MACH1 SPATIAL SYSTEM MakeFile -.PHONY: help +.PHONY: help show-arch help: @echo "MACH1 SPATIAL SYSTEM - Available Commands:" @echo "" @@ -15,10 +15,27 @@ help: @echo " make clean - Clean all build directories" @echo " make configure - Configure for release build" @echo " make build - Build all components" + @echo " make build-vlc - Build VLC from source (for m1-player)" + @echo "" + @echo "Cross-compilation (macOS only - requires x86_64 Homebrew at /usr/local):" + @echo " make dev-player-x86 - Configure m1-player for x86_64 (dev build)" + @echo " make configure-player-x86 - Configure m1-player for x86_64 (release build)" + @echo " make build-player-x86 - Build m1-player for x86_64" + @echo " make show-arch - Show current build architecture info" @echo "" @echo "Packaging:" - @echo " make package - Full package build (configure, build, codesign, notarize, installer)" - @echo " make installer-pkg - Build installer packages (Windows installer is signed automatically)" + @echo " make package - Full local build (configure, build, codesign, notarize, installer)" + @echo " make package-from-ci VERSION=x.x.x - Download CI builds, sign AAX locally, create installer" + @echo " make package-from-ci COMMIT=abc123 - Same as above, but by commit SHA" + @echo " make list-ci-builds - List available CI builds in S3" + @echo " make installer-pkg - Build installer packages only" + @echo "" + @echo "CI/CD Testing (local simulation):" + @echo " make test-ci-build - Simulate full CI build locally" + @echo " make test-ci-build-player-only - Test just m1-player build (fastest)" + @echo " make test-ci-yaml - Validate workflow YAML syntax" + @echo " make test-ci-act-arm - Run macOS ARM64 job with act" + @echo " make test-ci-act-arm DRYRUN=1 - Dry-run (show what would run)" @echo "" @echo "Code Signing:" @echo " make codesign - Sign all binaries (macOS/Windows)" @@ -29,6 +46,35 @@ help: @echo "" @echo "For more details, see the Makefile or run individual commands with --help" +# Show current build architecture (useful for CI/CD and distribution) +show-arch: +ifeq ($(detected_OS),Darwin) + @echo "=== Build Architecture Information ===" + @echo "Host OS: macOS" + @echo "Host Architecture: $$(uname -m)" + @if [ "$$(uname -m)" = "arm64" ]; then \ + echo "Target: Apple Silicon (ARM64)"; \ + echo "Homebrew: /opt/homebrew"; \ + echo "Deployment Target: macOS 11.0+"; \ + else \ + echo "Target: Intel (x86_64)"; \ + echo "Homebrew: /usr/local"; \ + echo "Deployment Target: macOS 10.14+"; \ + fi + @echo "" + @echo "NOTE: m1-player builds for HOST architecture only." + @echo " For distribution, build on each target platform separately." +else ifeq ($(detected_OS),Windows) + @echo "=== Build Architecture Information ===" + @echo "Host OS: Windows" + @if "%PROCESSOR_ARCHITECTURE%"=="AMD64" echo "Architecture: x86_64 (64-bit)" + @if "%PROCESSOR_ARCHITECTURE%"=="x86" echo "Architecture: x86 (32-bit)" +else + @echo "=== Build Architecture Information ===" + @echo "Host OS: $(detected_OS)" + @echo "Architecture: $$(uname -m)" +endif + # Auto-generate Makefile.variables from example if it doesn't exist Makefile.variables: @if [ ! -f "Makefile.variables" ] && [ -f "Makefile.variables.example" ]; then \ @@ -82,6 +128,7 @@ ifneq ($(detected_OS),Windows) @echo "m1-player: $$(cat m1-player/VERSION)" @echo "m1-orientationmanager: $$(cat m1-orientationmanager/VERSION)" @echo "m1-system-helper: $$(cat services/m1-system-helper/VERSION)" + @echo "m1-transcoder: $$(cat m1-transcoder/VERSION)" @echo "" @echo "Installer versions updated:" @echo "macOS: $$(grep -c "$(VERSION)" installer/osx/Mach1\ Spatial\ System\ Installer.pkgproj) packages" @@ -92,6 +139,8 @@ endif .PHONY: test-aax-monitor test-aax-panner test-aax-plugins test-aax-release .PHONY: verify-aax-signing diagnose-aax +.PHONY: test-ci-build test-ci-build-player-only test-ci-yaml +.PHONY: test-ci-act-arm pull: git pull --recurse-submodules @@ -120,6 +169,9 @@ setup: ifeq ($(detected_OS),Darwin) # Assumes you have installed Homebrew package manager brew install yasm cmake p7zip ninja act pre-commit launchcontrol + # VLC 3.x build dependencies (use FFmpeg 6.x for compatibility) + brew install autoconf automake libtool pkg-config + brew install ffmpeg@6 && brew link ffmpeg@6 npm install -g nodemon cd m1-transcoder && ./scripts/setup.sh cd m1-panner && pre-commit install @@ -128,6 +180,8 @@ else ifeq ($(detected_OS),Windows) @choco version >nul || (echo "chocolately is not working or installed" && exit 1) @echo "choco is installed and working" @choco install cmake --installargs 'ADD_CMAKE_TO_PATH=System' --apply-install-arguments-to-dependencies + @choco install pkgconfiglite autoconf automake libtool + @pip3 install meson @if not exist "$(pip show pre-commit)" (pip install pre-commit) npm install -g nodemon cd m1-panner && pre-commit install @@ -341,9 +395,63 @@ endif dev-player: ifeq ($(detected_OS),Darwin) - cmake m1-player -Bm1-player/build-dev -G "Xcode" + @echo "Configuring m1-player (dev)..." + cmake m1-player -Bm1-player/build-dev -G "Xcode" -DLIBVLC_BUILD_FROM_SOURCE=ON -DLIBVLC_STATIC=OFF || true + @if [ ! -f "m1-player/build-dev/vlc-install/lib/libvlc.dylib" ]; then \ + echo ""; \ + echo "VLC libraries not found. Building VLC from source..."; \ + echo ""; \ + cd m1-player && ./build_vlc.sh build-dev && \ + echo "" && \ + echo "VLC build complete! Reconfiguring CMake..." && \ + echo "" && \ + cd .. && cmake m1-player -Bm1-player/build-dev -G "Xcode" -DLIBVLC_BUILD_FROM_SOURCE=ON -DLIBVLC_STATIC=OFF; \ + fi else - cmake m1-player -Bm1-player/build-dev + @echo "Configuring m1-player (dev)..." + cmake m1-player -Bm1-player/build-dev -DLIBVLC_BUILD_FROM_SOURCE=ON -DLIBVLC_STATIC=OFF || true + @if [ ! -f "m1-player/build-dev/vlc-install/lib/libvlc.lib" ] && [ ! -f "m1-player/build-dev/vlc-install/lib/libvlc.dll.a" ]; then \ + echo ""; \ + echo "VLC libraries not found. Building VLC from source..."; \ + echo "This will take 20-40 minutes..."; \ + echo ""; \ + cd m1-player && ./build_vlc.sh build-dev && \ + echo "" && \ + echo "VLC build complete! Reconfiguring CMake..." && \ + echo "" && \ + cd .. && cmake m1-player -Bm1-player/build-dev -DLIBVLC_BUILD_FROM_SOURCE=ON -DLIBVLC_STATIC=OFF; \ + fi +endif + +# Cross-compile m1-player for x86_64 on Apple Silicon (via Rosetta 2) +# Requires x86_64 Homebrew installed at /usr/local with all VLC dependencies +dev-player-x86: +ifeq ($(detected_OS),Darwin) + @echo "Configuring m1-player for x86_64 (cross-compilation via Rosetta)..." + @if [ ! -d "/usr/local/bin" ]; then \ + echo "ERROR: x86_64 Homebrew not found at /usr/local"; \ + echo "Install it with:"; \ + echo " arch -x86_64 /bin/bash -c \"\$$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\""; \ + exit 1; \ + fi + arch -x86_64 cmake m1-player -Bm1-player/build-dev-x86 -G "Xcode" \ + -DCMAKE_OSX_ARCHITECTURES=x86_64 \ + -DLIBVLC_BUILD_FROM_SOURCE=ON -DLIBVLC_STATIC=OFF || true + @if [ ! -f "m1-player/build-dev-x86/vlc-install/lib/libvlc.dylib" ]; then \ + echo ""; \ + echo "VLC libraries not found. Building VLC for x86_64..."; \ + echo ""; \ + cd m1-player && arch -x86_64 ./build_vlc.sh build-dev-x86 && \ + echo "" && \ + echo "VLC build complete! Reconfiguring CMake..." && \ + echo "" && \ + cd .. && arch -x86_64 cmake m1-player -Bm1-player/build-dev-x86 -G "Xcode" \ + -DCMAKE_OSX_ARCHITECTURES=x86_64 \ + -DLIBVLC_BUILD_FROM_SOURCE=ON -DLIBVLC_STATIC=OFF; \ + fi +else + @echo "ERROR: dev-player-x86 is only available on macOS" + @exit 1 endif dev-orientationmanager: @@ -392,17 +500,313 @@ endif overlay-debug: cd m1-panner/Resources/overlay_debug && ./run_simulator.sh --title "Avid Video Engine" -# run configure first +# ============================================================================= +# Release Packaging +# ============================================================================= +# Full local build: configure → build → sign → notarize → installer package: update-versions build docs-build codesign notarize installer-pkg +# ============================================================================= +# Hybrid CI/CD Release (Recommended) +# ============================================================================= +# Downloads pre-built artifacts from CI/CD, then signs AAX locally and creates installer. +# This is the recommended workflow since AAX signing requires a physical USB iLok dongle. +# +# Usage: +# make package-from-ci VERSION=2.0.1 # By version tag +# make package-from-ci COMMIT=abc12345 # By commit SHA +# +# Prerequisites: +# - AWS CLI configured with access to mach1-build-artifacts bucket +# - USB iLok dongle connected for AAX signing +# - Apple Developer certificate in keychain for macOS +# +ARTIFACTS_BUCKET ?= mach1-build-artifacts +CI_ARTIFACTS_DIR ?= ci-artifacts + +package-from-ci: download-ci-artifacts sign-aax-local installer-pkg-from-ci + @echo "" + @echo "========================================" + @echo "Release Complete!" + @echo "========================================" + @echo "" + +download-ci-artifacts: + @echo "========================================" + @echo "Downloading CI Build Artifacts" + @echo "========================================" +ifeq ($(detected_OS),Darwin) + @if [ -z "$(VERSION)" ] && [ -z "$(COMMIT)" ]; then \ + echo "ERROR: Specify VERSION or COMMIT"; \ + echo " make package-from-ci VERSION=2.0.1"; \ + echo " make package-from-ci COMMIT=abc12345"; \ + exit 1; \ + fi + @mkdir -p $(CI_ARTIFACTS_DIR) + @if [ -n "$(VERSION)" ]; then \ + echo "Downloading version $(VERSION) artifacts..."; \ + aws s3 cp "s3://$(ARTIFACTS_BUCKET)/builds/$(VERSION)/macos-arm64-builds.tar.gz" \ + "$(CI_ARTIFACTS_DIR)/macos-arm64-builds.tar.gz" --region us-east-1 || \ + (echo "ERROR: Artifacts not found for version $(VERSION)" && exit 1); \ + elif [ -n "$(COMMIT)" ]; then \ + echo "Downloading commit $(COMMIT) artifacts..."; \ + aws s3 cp "s3://$(ARTIFACTS_BUCKET)/commits/$(COMMIT)/macos-arm64-builds.tar.gz" \ + "$(CI_ARTIFACTS_DIR)/macos-arm64-builds.tar.gz" --region us-east-1 || \ + (echo "ERROR: Artifacts not found for commit $(COMMIT)" && exit 1); \ + fi + @echo "Extracting artifacts..." + @cd $(CI_ARTIFACTS_DIR) && tar -xzf macos-arm64-builds.tar.gz + @echo "" + @echo "Installing artifacts to build directories..." + @mkdir -p m1-monitor/build m1-panner/build m1-player/build + @mkdir -p m1-orientationmanager/build services/m1-system-helper/build + @cp -r $(CI_ARTIFACTS_DIR)/macos-arm64/M1-Monitor/* m1-monitor/build/ 2>/dev/null || true + @cp -r $(CI_ARTIFACTS_DIR)/macos-arm64/M1-Panner/* m1-panner/build/ 2>/dev/null || true + @cp -r $(CI_ARTIFACTS_DIR)/macos-arm64/M1-Player/* m1-player/build/ 2>/dev/null || true + @cp -r $(CI_ARTIFACTS_DIR)/macos-arm64/m1-orientationmanager/* m1-orientationmanager/build/ 2>/dev/null || true + @cp -r $(CI_ARTIFACTS_DIR)/macos-arm64/m1-system-helper/* services/m1-system-helper/build/ 2>/dev/null || true + @echo "Artifacts downloaded and installed" +else ifeq ($(detected_OS),Windows) + @echo "Downloading Windows artifacts..." + @if not defined VERSION if not defined COMMIT ( \ + echo ERROR: Specify VERSION or COMMIT && \ + echo make package-from-ci VERSION=2.0.1 && \ + echo make package-from-ci COMMIT=abc12345 && \ + exit 1 \ + ) + @if not exist $(CI_ARTIFACTS_DIR) mkdir $(CI_ARTIFACTS_DIR) + @if defined VERSION ( \ + aws s3 cp "s3://$(ARTIFACTS_BUCKET)/builds/$(VERSION)/windows-builds.zip" \ + "$(CI_ARTIFACTS_DIR)\windows-builds.zip" --region us-east-1 \ + ) else ( \ + aws s3 cp "s3://$(ARTIFACTS_BUCKET)/commits/$(COMMIT)/windows-builds.zip" \ + "$(CI_ARTIFACTS_DIR)\windows-builds.zip" --region us-east-1 \ + ) + @7z x -y "$(CI_ARTIFACTS_DIR)\windows-builds.zip" -o"$(CI_ARTIFACTS_DIR)" + @echo Artifacts downloaded and extracted +endif + +sign-aax-local: + @echo "" + @echo "========================================" + @echo "Signing AAX Plugins (USB iLok Required)" + @echo "========================================" +ifeq ($(detected_OS),Darwin) + @echo "Ensure your USB iLok dongle is connected..." + @echo "" + @if [ -d "m1-monitor/build/M1-Monitor_artefacts/AAX" ] || [ -d "m1-monitor/build/AAX" ]; then \ + echo "Signing M1-Monitor AAX..."; \ + AAX_PATH=$$(find m1-monitor/build -name "M1-Monitor.aaxplugin" -type d | head -1); \ + if [ -n "$$AAX_PATH" ]; then \ + codesign --force --sign $(APPLE_CODESIGN_CODE) --timestamp "$$AAX_PATH"; \ + $(WRAPTOOL) sign --verbose --account $(PACE_ACCOUNT) --wcguid "$(MONITOR_FREE_GUID)" \ + --signid $(APPLE_CODESIGN_ID) --in "$$AAX_PATH" --out "$$AAX_PATH" --autoinstall on; \ + echo "M1-Monitor AAX signed"; \ + else \ + echo "M1-Monitor.aaxplugin not found"; \ + fi; \ + fi + @if [ -d "m1-panner/build/M1-Panner_artefacts/AAX" ] || [ -d "m1-panner/build/AAX" ]; then \ + echo "Signing M1-Panner AAX..."; \ + AAX_PATH=$$(find m1-panner/build -name "M1-Panner.aaxplugin" -type d | head -1); \ + if [ -n "$$AAX_PATH" ]; then \ + codesign --force --sign $(APPLE_CODESIGN_CODE) --timestamp "$$AAX_PATH"; \ + $(WRAPTOOL) sign --verbose --account $(PACE_ACCOUNT) --wcguid "$(PANNER_FREE_GUID)" \ + --signid $(APPLE_CODESIGN_ID) --in "$$AAX_PATH" --out "$$AAX_PATH" --autoinstall on; \ + echo "M1-Panner AAX signed"; \ + else \ + echo "M1-Panner.aaxplugin not found"; \ + fi; \ + fi + @echo "" + @echo "AAX signing complete!" +else ifeq ($(detected_OS),Windows) + @echo "Signing AAX plugins on Windows..." + @echo "Ensure iLok License Manager is running..." + @if exist "m1-monitor\build\M1-Monitor_artefacts\Release\AAX\M1-Monitor.aaxplugin" ( \ + set SIGNTOOL_PATH=$(WIN_SIGNTOOL_PATH) && \ + set ACS_DLIB=$(AZURE_DLIB_PATH) && \ + set ACS_JSON=$(AZURE_METADATA_PATH) && \ + set AZURE_TENANT_ID=$(AZURE_TENANT_ID) && \ + set AZURE_CLIENT_ID=$(AZURE_CLIENT_ID) && \ + set AZURE_SECRET_ID=$(AZURE_CLIENT_SECRET) && \ + $(WRAPTOOL) sign --signtool "$(CURDIR)/installer/win/aax-signtool.bat" --signid 1 --verbose \ + --installedbinaries --account $(PACE_ACCOUNT) --wcguid "$(MONITOR_FREE_GUID)" \ + --in m1-monitor\build\M1-Monitor_artefacts\Release\AAX\M1-Monitor.aaxplugin \ + --out m1-monitor\build\M1-Monitor_artefacts\Release\AAX\M1-Monitor.aaxplugin \ + ) + @if exist "m1-panner\build\M1-Panner_artefacts\Release\AAX\M1-Panner.aaxplugin" ( \ + set SIGNTOOL_PATH=$(WIN_SIGNTOOL_PATH) && \ + set ACS_DLIB=$(AZURE_DLIB_PATH) && \ + set ACS_JSON=$(AZURE_METADATA_PATH) && \ + set AZURE_TENANT_ID=$(AZURE_TENANT_ID) && \ + set AZURE_CLIENT_ID=$(AZURE_CLIENT_ID) && \ + set AZURE_SECRET_ID=$(AZURE_CLIENT_SECRET) && \ + $(WRAPTOOL) sign --signtool "$(CURDIR)/installer/win/aax-signtool.bat" --signid 1 --verbose \ + --installedbinaries --account $(PACE_ACCOUNT) --wcguid "$(PANNER_FREE_GUID)" \ + --in m1-panner\build\M1-Panner_artefacts\Release\AAX\M1-Panner.aaxplugin \ + --out m1-panner\build\M1-Panner_artefacts\Release\AAX\M1-Panner.aaxplugin \ + ) +endif + +installer-pkg-from-ci: sign-aax-local + @echo "" + @echo "========================================" + @echo "Creating Installer Package" + @echo "========================================" +ifeq ($(detected_OS),Darwin) + @echo "Building and signing installer..." + packagesbuild -v installer/osx/Mach1\ Spatial\ System\ Installer.pkgproj + codesign --force --sign $(APPLE_CODESIGN_CODE) --timestamp installer/osx/build/Mach1\ Spatial\ System\ Installer.pkg + mkdir -p installer/osx/build/signed + productsign --sign $(APPLE_CODESIGN_INSTALLER_ID) \ + "installer/osx/build/Mach1 Spatial System Installer.pkg" \ + "installer/osx/build/signed/Mach1 Spatial System Installer.pkg" + @echo "" + @echo "Notarizing installer..." + xcrun notarytool submit --wait --keychain-profile 'notarize-app' \ + --apple-id $(APPLE_USERNAME) --password $(ALTOOL_APPPASS) --team-id $(APPLE_TEAM_CODE) \ + "installer/osx/build/signed/Mach1 Spatial System Installer.pkg" + xcrun stapler staple installer/osx/build/signed/Mach1\ Spatial\ System\ Installer.pkg + @echo "" + @echo "Installer created at: installer/osx/build/signed/Mach1 Spatial System Installer.pkg" +else ifeq ($(detected_OS),Windows) + @echo "Building Windows installer..." + $(WIN_INNO_PATH) "${CURDIR}/installer/win/installer.iss" + @echo "Signing installer..." + powershell -ExecutionPolicy Bypass -File installer\win\sign-file.ps1 \ + -FilePath "installer\win\Output\Mach1 Spatial System Installer.exe" + @echo "Installer created at: installer\win\Output\Mach1 Spatial System Installer.exe" +endif + +# List available CI builds +list-ci-builds: + @echo "Available builds in S3:" + @echo "" + @echo "By Version:" + @aws s3 ls "s3://$(ARTIFACTS_BUCKET)/builds/" --region us-east-1 2>/dev/null || echo " (none or access denied)" + @echo "" + @echo "By Commit (recent):" + @aws s3 ls "s3://$(ARTIFACTS_BUCKET)/commits/" --region us-east-1 2>/dev/null | tail -10 || echo " (none or access denied)" + +# Clean CI artifacts +clean-ci-artifacts: + @echo "Cleaning CI artifacts..." + rm -rf $(CI_ARTIFACTS_DIR) + @echo "CI artifacts cleaned" + +# ============================================================================= +# CI/CD Testing (Local Simulation) +# ============================================================================= +# Simulates what CI/CD does, but runs locally. Useful for testing before push. +# +# Usage: +# make test-ci-build - Simulate full CI build +# make test-ci-build-player-only - Just build m1-player (fastest test) +# +test-ci-build: + @echo "========================================" + @echo "Simulating CI/CD Build Locally" + @echo "========================================" + @echo "" + @echo "This simulates what GitHub Actions does:" + @echo " 1. Clean build" + @echo " 2. Configure all projects" + @echo " 3. Build all projects" + @echo " 4. Sign non-AAX plugins" + @echo " 5. Package artifacts" + @echo "" + @echo "Press Ctrl+C to cancel, or wait 5 seconds..." + @sleep 5 + @echo "" + $(MAKE) clean + $(MAKE) configure + $(MAKE) build + $(MAKE) codesign-vst3 || true + $(MAKE) codesign-au || true + $(MAKE) codesign-apps || true + @echo "" + @echo "========================================" + @echo "CI Build Simulation Complete!" + @echo "========================================" + @echo "" + @echo "Next steps to test full release flow:" + @echo " 1. Connect USB iLok" + @echo " 2. Run: make sign-aax-local" + @echo " 3. Run: make installer-pkg" + +test-ci-build-player-only: + @echo "========================================" + @echo "Testing m1-player Build (CI Simulation)" + @echo "========================================" + @echo "" + @echo "This tests the VLC build + m1-player compilation" + @echo "which is the most complex part of CI/CD." + @echo "" +ifeq ($(detected_OS),Darwin) + rm -rf m1-player/build-test + cmake m1-player -Bm1-player/build-test -G "Xcode" \ + -DLIBVLC_BUILD_FROM_SOURCE=ON -DLIBVLC_STATIC=OFF || true + @if [ ! -f "m1-player/build-test/vlc-install/lib/libvlc.dylib" ]; then \ + echo "Building VLC from source..."; \ + cd m1-player && ./build_vlc.sh build-test && cd ..; \ + cmake m1-player -Bm1-player/build-test -G "Xcode" \ + -DLIBVLC_BUILD_FROM_SOURCE=ON -DLIBVLC_STATIC=OFF; \ + fi + cmake --build m1-player/build-test --config Release + @echo "" + @echo "m1-player build test complete!" + @echo " Output: m1-player/build-test/M1-Player_artefacts/" +else + @echo "This test is currently macOS-only" +endif + +# Validate workflow YAML syntax +test-ci-yaml: + @echo "Validating GitHub Actions workflow syntax..." + @if command -v actionlint >/dev/null 2>&1; then \ + actionlint .github/workflows/*.yml; \ + echo "Workflow YAML is valid"; \ + elif command -v act >/dev/null 2>&1; then \ + act -l > /dev/null 2>&1 && echo "Workflow YAML is valid (act)"; \ + else \ + echo "Install actionlint or act for YAML validation:"; \ + echo " brew install actionlint"; \ + echo " brew install act"; \ + fi + # clean and configure for release configure: clean update-versions cmake m1-monitor -Bm1-monitor/build -DBUILD_VST3=ON -DBUILD_AAX=ON -DBUILD_AU=ON -DBUILD_VST=ON -DVST2_PATH=$(VST2_PATH) -DJUCE_COPY_PLUGIN_AFTER_BUILD=OFF cmake m1-panner -Bm1-panner/build -DBUILD_VST3=ON -DBUILD_AAX=ON -DBUILD_AU=ON -DBUILD_VST=ON -DVST2_PATH=$(VST2_PATH) -DJUCE_COPY_PLUGIN_AFTER_BUILD=OFF ifeq ($(detected_OS),Darwin) - cmake m1-player -Bm1-player/build -G "Xcode" + @echo "Configuring m1-player (release)..." + cmake m1-player -Bm1-player/build -G "Xcode" -DLIBVLC_BUILD_FROM_SOURCE=ON -DLIBVLC_STATIC=OFF || true + @if [ ! -f "m1-player/build/vlc-install/lib/libvlc.dylib" ]; then \ + echo ""; \ + echo "VLC libraries not found. Building VLC from source..."; \ + echo "This will take 20-40 minutes..."; \ + echo ""; \ + cd m1-player && ./build_vlc.sh build && \ + echo "" && \ + echo "VLC build complete! Reconfiguring CMake..." && \ + echo "" && \ + cd .. && cmake m1-player -Bm1-player/build -G "Xcode" -DLIBVLC_BUILD_FROM_SOURCE=ON -DLIBVLC_STATIC=OFF; \ + fi else - cmake m1-player -Bm1-player/build + @echo "Configuring m1-player (release)..." + cmake m1-player -Bm1-player/build -DLIBVLC_BUILD_FROM_SOURCE=ON -DLIBVLC_STATIC=OFF || true + @if [ ! -f "m1-player/build/vlc-install/lib/libvlc.lib" ] && [ ! -f "m1-player/build/vlc-install/lib/libvlc.dll.a" ]; then \ + echo ""; \ + echo "VLC libraries not found. Building VLC from source..."; \ + echo "This will take 20-40 minutes..."; \ + echo ""; \ + cd m1-player && ./build_vlc.sh build && \ + echo "" && \ + echo "VLC build complete! Reconfiguring CMake..." && \ + echo "" && \ + cd .. && cmake m1-player -Bm1-player/build -DLIBVLC_BUILD_FROM_SOURCE=ON -DLIBVLC_STATIC=OFF; \ + fi endif cmake m1-orientationmanager -Bm1-orientationmanager/build cmake services/m1-system-helper -Bservices/m1-system-helper/build @@ -422,6 +826,91 @@ build-panner: build-player: cmake --build m1-player/build --config "Release" +# Cross-compile release build for x86_64 on Apple Silicon +build-player-x86: +ifeq ($(detected_OS),Darwin) + arch -x86_64 cmake --build m1-player/build-x86 --config "Release" +else + @echo "ERROR: build-player-x86 is only available on macOS" + @exit 1 +endif + +# Configure m1-player for x86_64 release (cross-compilation on Apple Silicon) +configure-player-x86: +ifeq ($(detected_OS),Darwin) + @echo "Configuring m1-player for x86_64 release (cross-compilation via Rosetta)..." + @if [ ! -d "/usr/local/bin" ]; then \ + echo "ERROR: x86_64 Homebrew not found at /usr/local"; \ + echo "Install it with:"; \ + echo " arch -x86_64 /bin/bash -c \"\$$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\""; \ + exit 1; \ + fi + arch -x86_64 cmake m1-player -Bm1-player/build-x86 -G "Xcode" \ + -DCMAKE_OSX_ARCHITECTURES=x86_64 \ + -DLIBVLC_BUILD_FROM_SOURCE=ON -DLIBVLC_STATIC=OFF || true + @if [ ! -f "m1-player/build-x86/vlc-install/lib/libvlc.dylib" ]; then \ + echo ""; \ + echo "VLC libraries not found. Building VLC for x86_64..."; \ + echo "This will take 20-40 minutes..."; \ + echo ""; \ + cd m1-player && arch -x86_64 ./build_vlc.sh build-x86 && \ + echo "" && \ + echo "VLC build complete! Reconfiguring CMake..." && \ + echo "" && \ + cd .. && arch -x86_64 cmake m1-player -Bm1-player/build-x86 -G "Xcode" \ + -DCMAKE_OSX_ARCHITECTURES=x86_64 \ + -DLIBVLC_BUILD_FROM_SOURCE=ON -DLIBVLC_STATIC=OFF; \ + fi +else + @echo "ERROR: configure-player-x86 is only available on macOS" + @exit 1 +endif + +# Build VLC from source (happens automatically as part of build-player) +# Use this target if you want to build VLC separately first +# +# ARCHITECTURE NOTE: +# VLC and m1-player are built for the HOST architecture only. +# Universal binaries are NOT supported due to VLC/Homebrew dependencies. +# For distribution: +# - Build on Apple Silicon Mac for ARM64 (.app for M1/M2/M3 Macs) +# - Build on Intel Mac for x86_64 (.app for older Intel Macs) +# The installer should be built and deployed separately for each architecture. +# +# CROSS-COMPILATION (macOS only): +# To build x86_64 on Apple Silicon, use the x86 targets: +# make dev-player-x86 (for development) +# make build-player-x86 (for release) +# Requires x86_64 Homebrew installed at /usr/local +build-vlc: + @echo "Building VLC from source..." +ifeq ($(detected_OS),Darwin) + @if [ -d "m1-player/build-dev" ]; then \ + echo "Using development build directory (build-dev)"; \ + cd m1-player && ./build_vlc.sh build-dev; \ + elif [ -d "m1-player/build" ]; then \ + echo "Using release build directory (build)"; \ + cd m1-player && ./build_vlc.sh build; \ + else \ + echo "Error: Neither m1-player/build nor m1-player/build-dev found."; \ + echo "Run 'make configure' or 'make dev-player' first."; \ + exit 1; \ + fi +else + @if [ -d "m1-player/build-dev" ]; then \ + echo "Using development build directory (build-dev)"; \ + cd m1-player && ./build_vlc.sh build-dev; \ + elif [ -d "m1-player/build" ]; then \ + echo "Using release build directory (build)"; \ + cd m1-player && ./build_vlc.sh build; \ + else \ + echo "Error: Neither m1-player/build nor m1-player/build-dev found."; \ + echo "Run 'make configure' or 'make dev-player' first."; \ + exit 1; \ + fi +endif + @echo "VLC setup complete" + build-orientationmanager: cmake --build m1-orientationmanager/build --config "Release" @@ -590,6 +1079,10 @@ endif codesign-apps: ifeq ($(detected_OS),Darwin) @echo "Code signing applications..." + # Sign bundled libraries and plugins inside M1-Player.app first (required for notarization) + # This signs all dylibs in Contents (Frameworks and PlugIns) with the same identity + find m1-player/build/M1-Player_artefacts/Release/M1-Player.app/Contents -type f -name "*.dylib" -exec codesign --force --sign $(APPLE_CODESIGN_CODE) --timestamp -o runtime {} \; + codesign -v --force -o runtime --entitlements m1-player/Resources/M1-Player.entitlements --sign $(APPLE_CODESIGN_CODE) --timestamp m1-player/build/M1-Player_artefacts/Release/M1-Player.app codesign -v --force -o runtime --entitlements m1-orientationmanager/Resources/entitlements.mac.plist --sign $(APPLE_CODESIGN_CODE) --timestamp m1-orientationmanager/build/m1-orientationmanager_artefacts/m1-orientationmanager codesign -v --force -o runtime --entitlements services/m1-system-helper/entitlements.mac.plist --sign $(APPLE_CODESIGN_CODE) --timestamp services/m1-system-helper/build/m1-system-helper_artefacts/m1-system-helper diff --git a/installer/osx/Mach1 Spatial System Installer.pkgproj b/installer/osx/Mach1 Spatial System Installer.pkgproj index 2965b7a..64f7366 100644 --- a/installer/osx/Mach1 Spatial System Installer.pkgproj +++ b/installer/osx/Mach1 Spatial System Installer.pkgproj @@ -1038,7 +1038,7 @@ USE_HFS+_COMPRESSION VERSION - 2.0.20250902 + 2.0.20251211 TYPE 0 @@ -1763,7 +1763,7 @@ USE_HFS+_COMPRESSION VERSION - 2.0.20251010 + 2.0.20251211 TYPE 0 @@ -4264,7 +4264,7 @@ USE_HFS+_COMPRESSION VERSION - 2.0.20250902 + 2.0.20251205 TYPE 0 @@ -5387,7 +5387,7 @@ USE_HFS+_COMPRESSION VERSION - 2.0.20251011 + 2.0.20251211 TYPE 0 @@ -6493,7 +6493,7 @@ USE_HFS+_COMPRESSION VERSION - 2.0.20251011 + 2.0.20251211 TYPE 0 @@ -7599,7 +7599,7 @@ USE_HFS+_COMPRESSION VERSION - 2.0.20251011 + 2.0.20251211 TYPE 0 @@ -8705,7 +8705,7 @@ USE_HFS+_COMPRESSION VERSION - 2.0.20251011 + 2.0.20251211 TYPE 0 diff --git a/m1-monitor b/m1-monitor index 1d698c5..fd86bc1 160000 --- a/m1-monitor +++ b/m1-monitor @@ -1 +1 @@ -Subproject commit 1d698c582ede2b27c55d06f910865b516c553593 +Subproject commit fd86bc16e3f5ce997a9512732b482963e91affea diff --git a/m1-orientationmanager b/m1-orientationmanager index 99293fa..d04e924 160000 --- a/m1-orientationmanager +++ b/m1-orientationmanager @@ -1 +1 @@ -Subproject commit 99293fa41bee6c1eaef2e9f3bcdaffb8eabc8ed2 +Subproject commit d04e924fbd6bb6b02fb23cf5ddda764be75aff58 diff --git a/m1-player b/m1-player index 71d5cea..1187aaf 160000 --- a/m1-player +++ b/m1-player @@ -1 +1 @@ -Subproject commit 71d5ceabaffeade165ca5219fd4e046e5275b3db +Subproject commit 1187aaf850ce0a74543af0959086a5e43e048904 diff --git a/services/m1-system-helper/VERSION b/services/m1-system-helper/VERSION index 25f76ba..251113e 100644 --- a/services/m1-system-helper/VERSION +++ b/services/m1-system-helper/VERSION @@ -1 +1 @@ -2.0.20251110 +2.0.20251215