Skip to content

fix(ci-macos): cp -R (preserve symlinks) in Package artifacts step #167

fix(ci-macos): cp -R (preserve symlinks) in Package artifacts step

fix(ci-macos): cp -R (preserve symlinks) in Package artifacts step #167

Workflow file for this run

name: Build + Validate
on:
push:
branches: ['**']
tags: ['v*']
pull_request:
branches: [main]
env:
BUILD_TYPE: Release
jobs:
build-macos:
runs-on: macos-14
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup CMake
uses: lukka/get-cmake@latest
- name: Build universal OpenSSL
run: |
OPENSSL_VER=3.4.1
curl -sL "https://github.com/openssl/openssl/releases/download/openssl-${OPENSSL_VER}/openssl-${OPENSSL_VER}.tar.gz" | tar xz
cd "openssl-${OPENSSL_VER}"
# arm64
./Configure darwin64-arm64-cc --prefix="$GITHUB_WORKSPACE/openssl-arm64" no-shared no-tests
make -j$(sysctl -n hw.ncpu) && make install_sw && make clean
# x86_64
./Configure darwin64-x86_64-cc --prefix="$GITHUB_WORKSPACE/openssl-x86_64" no-shared no-tests
make -j$(sysctl -n hw.ncpu) && make install_sw
# lipo into universal
mkdir -p "$GITHUB_WORKSPACE/openssl-universal/lib"
cp -r "$GITHUB_WORKSPACE/openssl-arm64/include" "$GITHUB_WORKSPACE/openssl-universal/"
lipo -create "$GITHUB_WORKSPACE/openssl-arm64/lib/libcrypto.a" \
"$GITHUB_WORKSPACE/openssl-x86_64/lib/libcrypto.a" \
-output "$GITHUB_WORKSPACE/openssl-universal/lib/libcrypto.a"
lipo -create "$GITHUB_WORKSPACE/openssl-arm64/lib/libssl.a" \
"$GITHUB_WORKSPACE/openssl-x86_64/lib/libssl.a" \
-output "$GITHUB_WORKSPACE/openssl-universal/lib/libssl.a"
lipo -info "$GITHUB_WORKSPACE/openssl-universal/lib/libcrypto.a"
- name: Verify LGPL discipline (vendored trees in libs/ffmpeg/)
# Iterate every populated libs/ffmpeg/<platform>/ tree (both arches
# of macOS are now committed) and assert no libx264 strings + clean
# otool/ldd linkage. Exits 1 on any contamination.
# The "Vendor LGPL ffmpeg" rebuild-from-source step that used to run
# here was removed 2026-05-19 — libs/ffmpeg/macos-arm64/ and
# libs/ffmpeg/macos-x86_64/ are both bundled in the repo, so a
# 15-25 min CI rebuild on every push became a no-op git checkout.
# Run scripts/build_ffmpeg_lgpl.sh manually when bumping ffmpeg or
# openh264 version and commit the refreshed trees.
run: bash scripts/verify_ffmpeg_lgpl.sh
- name: Configure CMake (universal — arm64 + x86_64)
# Universal mac build: cmake/ffmpeg.cmake detects the multi-arch
# CMAKE_OSX_ARCHITECTURES and lipo's libs/ffmpeg/macos-{arm64,x86_64}/
# dylibs into a universal tree at ${CMAKE_BINARY_DIR}/ffmpeg-universal/
# at configure time. JUCE/CMake compiles each .o twice + the linker
# produces fat artifacts. Output runs natively on Apple Silicon AND
# Intel Macs.
# JAMWIDE_BUILD_TESTS + JAMWIDE_VIDEO_SPIKE pulled the test executables
# (incl. test_flac_codec which has a pre-existing WDL_VORBIS_INTERFACE_ONLY
# guard gap in wdl/vorbisencdec.h that caused the compile to fail).
run: |
cmake -B build \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \
-DJAMWIDE_BUILD_JUCE=ON \
-DJAMWIDE_BUILD_CLAP=OFF \
-DJAMWIDE_DEV_BUILD=OFF \
-DOPENSSL_ROOT_DIR="$GITHUB_WORKSPACE/openssl-universal"
- name: Build
run: cmake --build build --config Release -j $(sysctl -n hw.ncpu)
- name: Verify artifacts
run: |
echo "=== Build artifacts ==="
ls -la "build/JamWideJuce_artefacts/Release/VST3/JamWide.vst3/" || echo "VST3 not found"
ls -la "build/JamWideJuce_artefacts/Release/AU/JamWide.component/" || echo "AU not found"
ls -la "build/JamWideJuce_artefacts/Release/CLAP/JamWide.clap/" || echo "CLAP not found"
ls -la "build/JamWideJuce_artefacts/Release/Standalone/JamWide.app/" || echo "Standalone not found"
- name: Download pluginval
run: |
curl -L https://github.com/Tracktion/pluginval/releases/latest/download/pluginval_macOS.zip -o pluginval.zip
unzip pluginval.zip
- name: Force AU re-scan
run: killall -9 AudioComponentRegistrar 2>/dev/null || true
- name: Validate VST3
run: |
./pluginval.app/Contents/MacOS/pluginval \
--validate-in-process \
--strictness-level 5 \
--timeout-ms 120000 \
--validate "build/JamWideJuce_artefacts/Release/VST3/JamWide.vst3"
- name: Validate AU
continue-on-error: true
run: |
./pluginval.app/Contents/MacOS/pluginval \
--validate-in-process \
--strictness-level 5 \
--timeout-ms 120000 \
--validate "build/JamWideJuce_artefacts/Release/AU/JamWide.component"
- name: Validate Standalone
continue-on-error: true
run: |
./pluginval.app/Contents/MacOS/pluginval \
--validate-in-process \
--strictness-level 5 \
--timeout-ms 120000 \
--validate "build/JamWideJuce_artefacts/Release/Standalone/JamWide.app"
- name: Import signing certificate
if: startsWith(github.ref, 'refs/tags/v')
env:
CERTIFICATE_P12: ${{ secrets.DEVELOPER_ID_P12 }}
CERTIFICATE_PASSWORD: ${{ secrets.DEVELOPER_ID_P12_PASSWORD }}
run: |
KEYCHAIN_PATH=$RUNNER_TEMP/signing.keychain-db
KEYCHAIN_PASSWORD=$(openssl rand -hex 16)
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
echo "$CERTIFICATE_P12" | base64 --decode > "$RUNNER_TEMP/cert.p12"
security import "$RUNNER_TEMP/cert.p12" -k "$KEYCHAIN_PATH" -P "$CERTIFICATE_PASSWORD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security list-keychains -d user -s "$KEYCHAIN_PATH" $(security list-keychains -d user | tr -d '"')
rm "$RUNNER_TEMP/cert.p12"
- name: Sign and notarize bundles
if: startsWith(github.ref, 'refs/tags/v')
env:
NOTARIZE_KEY_ID: ${{ secrets.NOTARIZE_KEY_ID }}
NOTARIZE_ISSUER_ID: ${{ secrets.NOTARIZE_ISSUER_ID }}
NOTARIZE_KEY_PATH: ${{ runner.temp }}/AuthKey.p8
NOTARIZE_API_KEY_B64: ${{ secrets.NOTARIZE_API_KEY_P8 }}
run: |
echo "$NOTARIZE_API_KEY_B64" | base64 --decode > "$NOTARIZE_KEY_PATH"
./scripts/notarize.sh build/JamWideJuce_artefacts/Release
rm -f "$NOTARIZE_KEY_PATH"
- name: Package artifacts
# IMPORTANT — use `cp -R` (capital R), NOT `cp -r`. On macOS 14+
# BSD cp treats lowercase `-r` as `-RL`, which dereferences
# symlinks. The vendored ffmpeg dylibs are bundled with version-
# alias symlinks (libavcodec.61.dylib -> libavcodec.61.19.101.dylib)
# that libavformat depends on, and codesign+notarize seal the
# bundle expecting <symlink> entries in _CodeSignature/CodeResources.
# `cp -r` flattens symlinks into duplicate files, breaking the seal
# AFTER notarization+stapling. Result: spctl rejects with "a sealed
# resource is missing or invalid" and macOS Gatekeeper shows
# the bundle as "damaged" — exactly the v1.1-beta.20.10 launch
# failure on Apple Silicon. `cp -R` preserves symlinks (verified
# empirically against the macos-14 runner).
run: |
mkdir -p staging
cp -R "build/JamWideJuce_artefacts/Release/VST3/JamWide.vst3" staging/
cp -R "build/JamWideJuce_artefacts/Release/AU/JamWide.component" staging/
cp -R "build/JamWideJuce_artefacts/Release/CLAP/JamWide.clap" staging/
cp -R "build/JamWideJuce_artefacts/Release/Standalone/JamWide.app" staging/
cd staging && tar czf ../JamWide-macOS.tar.gz .
- name: Upload macOS artifacts
uses: actions/upload-artifact@v4
with:
name: JamWide-macOS
path: JamWide-macOS.tar.gz
retention-days: 30
build-windows:
# Enabled 2026-05-18 (Phase 22 UAT exit ramp): the MSYS2 ffmpeg-vendoring
# script + windows-x86_64 leg has been written since Phase 14.3 but never
# exercised on a real CI run. This is the first attempt; Phase 23's full
# signtool + dumpbin /dependents gate is still owed but we ship an
# unsigned Windows-x86_64 beta artifact for early testers in the meantime.
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup CMake
uses: lukka/get-cmake@latest
- name: Verify LGPL discipline (vendored trees in libs/ffmpeg/)
# Iterates every populated libs/ffmpeg/<platform>/ tree (Windows
# vendored as of this commit) and asserts no libx264 strings + clean
# PE-import dependency check. Same gate that the mac CI runs against
# the bundled trees.
# The "Vendor LGPL ffmpeg" rebuild-from-source step that used to run
# here was removed once libs/ffmpeg/windows-x86_64/ landed bundled
# in the repo — saves 15-25 min/CI run and eliminates the gendef/
# dlltool/lib generation toolchain-rot surface that took multiple
# iterations to get green. Run scripts/build_ffmpeg_lgpl.sh manually
# under MSYS2 when bumping ffmpeg or openh264 version, then commit
# the refreshed tree.
# Runs under Git Bash (shipped on windows-latest by default — has
# bash + binutils strings). Dropped the previous setup-msys2 step
# in beta.20.9 after pacman -Syuu kept timing out against
# mirror.accum.se, breaking the tag CI for a transient mirror
# issue unrelated to our actual build.
shell: bash
run: bash scripts/verify_ffmpeg_lgpl.sh
- name: Configure CMake
run: |
cmake -B build `
-G "Visual Studio 17 2022" `
-A x64 `
-DJAMWIDE_BUILD_JUCE=ON `
-DJAMWIDE_BUILD_CLAP=OFF `
-DJAMWIDE_DEV_BUILD=OFF
- name: Build
run: cmake --build build --config Release
- name: Verify artifacts
shell: pwsh
run: |
Write-Host "=== Build artifacts ==="
Get-ChildItem -Path "build/JamWideJuce_artefacts" -Recurse -Include "*.vst3","*.clap" -Directory | Select-Object FullName
- name: Download pluginval
shell: pwsh
run: |
Invoke-WebRequest -Uri "https://github.com/Tracktion/pluginval/releases/latest/download/pluginval_Windows.zip" -OutFile pluginval.zip
Expand-Archive -Path pluginval.zip -DestinationPath pluginval
- name: Validate VST3
run: |
pluginval\pluginval.exe --validate-in-process --strictness-level 5 --timeout-ms 120000 --validate "build\JamWideJuce_artefacts\Release\VST3\JamWide.vst3"
- name: Stage artifacts
shell: pwsh
run: |
New-Item -ItemType Directory -Path staging -Force
# VST3: single-file bundle (extract DLL from package directory)
Copy-Item "build/JamWideJuce_artefacts/Release/VST3/JamWide.vst3/Contents/x86_64-win/JamWide.vst3" "staging/JamWide.vst3"
# CLAP: single-file bundle
Copy-Item "build/JamWideJuce_artefacts/Release/CLAP/JamWide.clap" "staging/JamWide.clap"
# Standalone
Copy-Item "build/JamWideJuce_artefacts/Release/Standalone/JamWide.exe" "staging/JamWide.exe"
# Vendored ffmpeg DLLs (LGPL) — required runtime deps for video
# encode/decode. Phase 23-02 owns proper Windows install layout;
# for the beta we just colocate the DLLs next to the standalone +
# plugins so Windows' DLL search resolves them. Per memory
# `project_release_packaging`: Windows uses zip (not tar.gz).
# The DLLs live in libs/ffmpeg/windows-x86_64/lib/ (the same dir
# as the MSVC .lib import libraries — MinGW autotools dropped
# them all under lib/ regardless of .dll vs .lib). The previous
# path "libs/ffmpeg/windows-x86_64/bin/" did not exist on the
# runner and the Test-Path soft-check silently skipped the copy,
# shipping v1.1-beta.20.8 Windows zip without any ffmpeg DLLs
# — that beta launch-crashed on Windows for the same reason
# the macOS .app did (missing runtime ffmpeg, just a different
# OS' dynamic loader complaining).
$ffmpegDlls = Get-ChildItem "libs/ffmpeg/windows-x86_64/lib/*.dll" -ErrorAction SilentlyContinue
if ($ffmpegDlls) {
Copy-Item $ffmpegDlls "staging/"
Write-Host "Staged $($ffmpegDlls.Count) ffmpeg DLL(s)"
} else {
Write-Error "No ffmpeg DLLs found under libs/ffmpeg/windows-x86_64/lib/ — packaging would ship a broken Windows zip"
exit 1
}
- name: Package Windows zip
shell: pwsh
run: |
# Per memory `project_release_packaging`: Windows uses zip (not
# tar.gz). PowerShell's Compress-Archive is native and preserves
# the flat layout the staging step produced.
Compress-Archive -Path "staging/*" -DestinationPath "JamWide-Windows.zip" -Force
- name: Upload Windows artifacts
uses: actions/upload-artifact@v4
with:
name: JamWide-Windows
path: JamWide-Windows.zip
retention-days: 30
build-linux:
# Disabled for v1.3 betas — Phase 19's camera-capture code uses
# juce::CameraDevice unconditionally, but juce_video has no
# juce_CameraDevice_linux.h (Linux capture deferred per memory
# 'project_jamtaba_video_port': Linux is receive-only v1, V4L2
# wrapper is post-v1 Item K). Re-enable once a Linux capture path
# (or conditional compile around the camera code) lands. Same one-line
# flip as build-windows.
if: ${{ false }}
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
build-essential cmake pkg-config \
nasm \
libasound2-dev \
libjack-jackd2-dev \
libfreetype-dev \
libx11-dev \
libxrandr-dev \
libxinerama-dev \
libxcursor-dev \
libxcomposite-dev \
libgl1-mesa-dev \
libcurl4-openssl-dev \
libwebkit2gtk-4.1-dev \
libssl-dev
- name: Vendor LGPL ffmpeg
# Phase 14.3-01: produce libs/ffmpeg/linux-x86_64/ on the CI runner via
# scripts/build_ffmpeg_lgpl.sh. The script's OS+ARCH dispatch picks the
# Linux-x86_64 leg (Cisco openh264 v2.1.1 prebuilt + ffmpeg LGPL from
# source). All inputs are static; no untrusted github event data flows
# into shell.
run: bash scripts/build_ffmpeg_lgpl.sh
- name: Verify LGPL discipline
run: bash scripts/verify_ffmpeg_lgpl.sh
- name: Configure CMake
run: |
cmake -B build \
-DCMAKE_BUILD_TYPE=Release \
-DJAMWIDE_BUILD_JUCE=ON \
-DJAMWIDE_BUILD_CLAP=OFF \
-DJAMWIDE_DEV_BUILD=OFF
- name: Build
run: cmake --build build --config Release -j $(nproc)
- name: Verify artifacts
run: |
echo "=== Build artifacts ==="
find build/JamWideJuce_artefacts -name "*.vst3" -o -name "*.clap" -o -name "*.so" | head -20
- name: Download pluginval
run: |
curl -L https://github.com/Tracktion/pluginval/releases/latest/download/pluginval_Linux.zip -o pluginval.zip
unzip pluginval.zip
chmod +x pluginval
- name: Validate VST3
continue-on-error: true
run: |
./pluginval \
--validate-in-process \
--strictness-level 5 \
--timeout-ms 120000 \
--validate "build/JamWideJuce_artefacts/Release/VST3/JamWide.vst3"
- name: Package artifacts
run: |
mkdir -p staging
cp -r "build/JamWideJuce_artefacts/Release/VST3/JamWide.vst3" staging/
cp "build/JamWideJuce_artefacts/Release/CLAP/JamWide.clap" staging/
cp "build/JamWideJuce_artefacts/Release/Standalone/JamWide" staging/
cd staging && tar czf ../JamWide-Linux.tar.gz .
- name: Upload Linux artifacts
uses: actions/upload-artifact@v4
with:
name: JamWide-Linux
path: JamWide-Linux.tar.gz
retention-days: 30
release:
if: startsWith(github.ref, 'refs/tags/v')
# macOS universal + Windows x64 both shipping as of v1.1-beta.20.8
# (50b7678 — bundled libs/ffmpeg/windows-x86_64/ with MSVC .lib import
# libraries generated via gendef + dlltool). Linux still owes the
# JUCE_LINUX camera-code conditional; queued for v1.1-beta.20.9.
needs: [build-macos, build-windows]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Prepare release artifacts
run: |
# macOS universal uses tar.gz (preserves Unix permissions per memory
# `project_release_packaging`); Windows uses zip. Linux still
# deferred (camera-code conditional queued).
cp artifacts/JamWide-macOS/JamWide-macOS.tar.gz .
cp artifacts/JamWide-Windows/JamWide-Windows.zip .
- name: Create or update release
env:
GH_TOKEN: ${{ github.token }}
TAG_NAME: ${{ github.ref_name }}
run: |
PRERELEASE_FLAG=""
if echo "$TAG_NAME" | grep -qE "beta|alpha|rc"; then
PRERELEASE_FLAG="--prerelease"
fi
if gh release view "$TAG_NAME" --repo "$GITHUB_REPOSITORY" > /dev/null 2>&1; then
echo "Release $TAG_NAME already exists — uploading assets"
gh release upload "$TAG_NAME" \
--repo "$GITHUB_REPOSITORY" \
--clobber \
JamWide-macOS.tar.gz \
JamWide-Windows.zip
else
gh release create "$TAG_NAME" \
--repo "$GITHUB_REPOSITORY" \
--title "$TAG_NAME" \
--generate-notes \
$PRERELEASE_FLAG \
JamWide-macOS.tar.gz \
JamWide-Windows.zip
fi