Skip to content

fix(ci): 放宽 macOS 通用包运行时白名单 #174

fix(ci): 放宽 macOS 通用包运行时白名单

fix(ci): 放宽 macOS 通用包运行时白名单 #174

Workflow file for this run

name: Release
on:
push:
tags:
- '*.*.*'
permissions:
contents: write
jobs:
build_win:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: 20
- name: Setup PNPM
run: |
corepack enable
corepack prepare pnpm@9 --activate
- name: Restore Demucs models cache (Windows)
uses: actions/cache@v5
with:
path: |
vendor/demucs/models
key: demucs-models-${{ runner.os }}-${{ hashFiles('scripts/demucs-model-manifest.json', 'scripts/ensure-demucs-runtime.mjs', 'scripts/lib/*.mjs') }}
restore-keys: |
demucs-models-${{ runner.os }}-
- name: Restore Rekordbox runtime cache (Windows)
uses: actions/cache@v5
with:
path: |
vendor/rekordbox-desktop-runtime/win32-x64/python
key: rekordbox-runtime-${{ runner.os }}-${{ hashFiles('scripts/ensure-rekordbox-desktop-runtime.mjs', 'scripts/rekordbox-desktop-runtime-requirements.txt', 'scripts/lib/demucs-standalone-python.mjs', 'scripts/lib/demucs-runtime-support.mjs') }}
restore-keys: |
rekordbox-runtime-${{ runner.os }}-
- name: Setup Python (for node-gyp)
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Ensure setuptools available
run: |
python -m pip install --upgrade pip setuptools wheel
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
targets: x86_64-pc-windows-msvc
- name: Install dependencies
run: pnpm install --frozen-lockfile=false
- name: Ensure Demucs models (Windows)
run: node scripts/ensure-demucs-runtime.mjs --models-only --ci
- name: Ensure Rekordbox Desktop runtime (Windows)
run: pnpm run rekordbox:runtime:ensure
- name: Typecheck (vue-tsc)
run: npx vue-tsc --noEmit
- name: Prepare FFmpeg (Windows)
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path vendor/ffmpeg/win32-x64 | Out-Null
$release = Invoke-RestMethod -Uri 'https://api.github.com/repos/BtbN/FFmpeg-Builds/releases/latest' -Headers @{ 'User-Agent' = 'FRKB-Release' }
$asset =
$release.assets |
Where-Object { $_.name -match '^ffmpeg-N-.*-win64-gpl\.zip$' } |
Select-Object -First 1
if (-not $asset) {
$asset =
$release.assets |
Where-Object { $_.name -match '^ffmpeg-.*-win64-gpl\.zip$' } |
Select-Object -First 1
}
if (-not $asset) {
throw 'win64 gpl ffmpeg asset not found in latest BtbN release.'
}
Invoke-WebRequest -Uri $asset.browser_download_url -OutFile ffmpeg.zip
Expand-Archive -Path ffmpeg.zip -DestinationPath ffmpeg_unzip -Force
$ff = (Get-ChildItem -Recurse -Filter ffmpeg.exe ffmpeg_unzip | Select-Object -First 1).FullName
$ffprobe = (Get-ChildItem -Recurse -Filter ffprobe.exe ffmpeg_unzip | Select-Object -First 1).FullName
if (-not $ff) {
throw "ffmpeg.exe not found in downloaded archive."
}
if (-not $ffprobe) {
throw "ffprobe.exe not found in downloaded archive."
}
Copy-Item -Path $ff -Destination vendor/ffmpeg/win32-x64/ffmpeg.exe -Force
Copy-Item -Path $ffprobe -Destination vendor/ffmpeg/win32-x64/ffprobe.exe -Force
Remove-Item ffmpeg.zip -Force
Remove-Item ffmpeg_unzip -Recurse -Force
- name: Prepare Chromaprint (Windows)
shell: pwsh
run: |
$chromaprintVersion = '1.5.1'
$tag = "v$chromaprintVersion"
$url = "https://github.com/acoustid/chromaprint/releases/download/$tag/chromaprint-fpcalc-$chromaprintVersion-windows-x86_64.zip"
New-Item -ItemType Directory -Force -Path vendor/chromaprint/win32-x64 | Out-Null
Invoke-WebRequest -Uri $url -OutFile fpcalc.zip
Expand-Archive -Path fpcalc.zip -DestinationPath fpcalc_unzip -Force
$fpcalc = Get-ChildItem -Recurse -Filter fpcalc.exe fpcalc_unzip | Select-Object -First 1
if (-not $fpcalc) {
throw "fpcalc.exe not found in downloaded archive."
}
Copy-Item -Path $fpcalc.FullName -Destination vendor/chromaprint/win32-x64/fpcalc.exe -Force
Remove-Item fpcalc.zip -Force
Remove-Item fpcalc_unzip -Recurse -Force
- name: Build Rust N-API (rust_package)
working-directory: rust_package
run: npx --yes @napi-rs/cli@2.18.4 build --platform --release --target x86_64-pc-windows-msvc
- name: Build (vite)
env:
CLOUD_SYNC_BASE_URL_PROD: ${{ secrets.CLOUD_SYNC_BASE_URL_PROD }}
CLOUD_SYNC_API_SECRET_KEY: ${{ secrets.CLOUD_SYNC_API_SECRET_KEY }}
run: pnpm build
- name: Package (electron-builder, no publish)
run: npx electron-builder --win --x64 --publish never
- name: Verify packaged Demucs resources (Windows)
shell: pwsh
run: |
$resourcesRoot = "dist/win-unpacked/resources"
if (-not (Test-Path $resourcesRoot)) {
throw "Windows unpacked resources directory not found: $resourcesRoot"
}
$required = @(
"ffmpeg/win32-x64/ffmpeg.exe",
"ffmpeg/win32-x64/ffprobe.exe",
"demucs/bootstrap/mixtape_demucs_bootstrap.py",
"demucs/models/htdemucs.yaml",
"demucs/models/955717e8-8726e21a.th",
"rekordboxDesktopLibrary/bridge.py",
"rekordboxDesktopRuntime/win32-x64/python/Scripts/python.exe",
"rekordboxDesktopRuntime/win32-x64/python/Lib/site-packages/pyrekordbox/__init__.py"
)
$missing = @()
foreach ($relativePath in $required) {
$targetPath = Join-Path $resourcesRoot $relativePath
if (-not (Test-Path $targetPath)) {
$missing += $relativePath
}
}
$unexpectedRuntimeRoot = Join-Path $resourcesRoot "demucs/win32-x64"
if (Test-Path $unexpectedRuntimeRoot) {
$missing += "unexpected:demucs/win32-x64"
}
if ($missing.Count -gt 0) {
Write-Error "Missing packaged Demucs resources (Windows):"
$missing | ForEach-Object { Write-Error " - $_" }
exit 1
}
Write-Host "Packaged Demucs resources verified (Windows, runtime excluded)."
# 不再复制 rc.yml,严格遵循 GitHub provider 读取 latest.yml / latest-mac.yml
- name: Upload dist artifacts (Windows)
uses: actions/upload-artifact@v7
if: always()
with:
name: win-artifacts
path: |
dist/*.exe
dist/*.blockmap
dist/*.yml
build_rekordbox_runtime_mac_x64:
runs-on: macos-15-intel
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: 20
- name: Restore Rekordbox runtime cache (macOS x64)
uses: actions/cache@v5
with:
path: |
vendor/rekordbox-desktop-runtime/darwin-x64/python
key: rekordbox-runtime-${{ runner.os }}-${{ runner.arch }}-darwin-x64-${{ hashFiles('scripts/ensure-rekordbox-desktop-runtime.mjs', 'scripts/rekordbox-desktop-runtime-requirements.txt', 'scripts/lib/demucs-standalone-python.mjs', 'scripts/lib/demucs-runtime-support.mjs') }}
restore-keys: |
rekordbox-runtime-${{ runner.os }}-${{ runner.arch }}-darwin-x64-
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Ensure Rekordbox Desktop runtime (macOS x64)
run: node scripts/ensure-rekordbox-desktop-runtime.mjs
- name: Archive Rekordbox Desktop runtime (macOS x64)
run: |
set -euo pipefail
mkdir -p dist
tar -czf dist/rekordbox-runtime-darwin-x64.tar.gz \
-C vendor/rekordbox-desktop-runtime/darwin-x64 \
python
- name: Upload Rekordbox runtime artifact (macOS x64)
uses: actions/upload-artifact@v7
with:
name: rekordbox-runtime-darwin-x64
path: dist/rekordbox-runtime-darwin-x64.tar.gz
build_mac:
runs-on: macos-latest
needs: [build_rekordbox_runtime_mac_x64]
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: 20
- name: Setup PNPM
run: |
corepack enable
corepack prepare pnpm@9 --activate
- name: Restore Rekordbox runtime cache (macOS arm64)
uses: actions/cache@v5
with:
path: |
vendor/rekordbox-desktop-runtime/darwin-arm64/python
key: rekordbox-runtime-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('scripts/ensure-rekordbox-desktop-runtime.mjs', 'scripts/rekordbox-desktop-runtime-requirements.txt', 'scripts/lib/demucs-standalone-python.mjs', 'scripts/lib/demucs-runtime-support.mjs') }}
restore-keys: |
rekordbox-runtime-${{ runner.os }}-${{ runner.arch }}-
- name: Setup Python (for node-gyp)
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Ensure setuptools available
run: |
python -m pip install --upgrade pip setuptools wheel
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
targets: aarch64-apple-darwin, x86_64-apple-darwin
- name: Ensure Rust targets
run: rustup target add aarch64-apple-darwin x86_64-apple-darwin
- name: Install dependencies
run: pnpm install --frozen-lockfile=false
- name: Ensure Demucs models (macOS)
run: node scripts/ensure-demucs-runtime.mjs --models-only --ci
- name: Ensure Rekordbox Desktop runtime (macOS arm64)
run: pnpm run rekordbox:runtime:ensure
- name: Download Rekordbox runtime artifact (macOS x64)
uses: actions/download-artifact@v8
with:
name: rekordbox-runtime-darwin-x64
path: artifacts/rekordbox-runtime-darwin-x64
- name: Restore Rekordbox runtime artifact (macOS x64)
run: |
set -euo pipefail
mkdir -p vendor/rekordbox-desktop-runtime/darwin-x64
tar -xzf artifacts/rekordbox-runtime-darwin-x64/rekordbox-runtime-darwin-x64.tar.gz \
-C vendor/rekordbox-desktop-runtime/darwin-x64
- name: Typecheck (vue-tsc)
run: npx vue-tsc --noEmit
- name: Prepare FFmpeg (macOS both arch)
run: |
set -euxo pipefail
mkdir -p vendor/ffmpeg/darwin-arm64 vendor/ffmpeg/darwin-x64 vendor/ffmpeg/darwin
echo "=== Host uname ==="
uname -a
sw_vers
# 使用 Homebrew 安装 arm64 版本 (native on arm64 runner)
echo "Installing arm64 ffmpeg via Homebrew"
brew update
brew install ffmpeg
ARM64_FFMPEG_PATH="$(brew --prefix)/bin/ffmpeg"
ARM64_FFPROBE_PATH="$(brew --prefix)/bin/ffprobe"
file "$ARM64_FFMPEG_PATH"
file "$ARM64_FFPROBE_PATH"
install -m 755 "$ARM64_FFMPEG_PATH" vendor/ffmpeg/darwin-arm64/ffmpeg
install -m 755 "$ARM64_FFPROBE_PATH" vendor/ffmpeg/darwin-arm64/ffprobe
# 从多个源尝试下载 x64 版本
echo "Downloading x64 ffmpeg/ffprobe from ffbinaries..."
curl -L "https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v4.4.1/ffmpeg-4.4.1-osx-64.zip" -o ffmpeg-x64.zip
curl -L "https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v4.4.1/ffprobe-4.4.1-osx-64.zip" -o ffprobe-x64.zip
unzip -q ffmpeg-x64.zip -d ffmpeg-x64-temp
unzip -q ffprobe-x64.zip -d ffprobe-x64-temp
X64_FFMPEG_PATH="$(find ffmpeg-x64-temp -type f -name ffmpeg | head -n 1 || true)"
X64_FFPROBE_PATH="$(find ffprobe-x64-temp -type f -name ffprobe | head -n 1 || true)"
if [ -z "$X64_FFMPEG_PATH" ] || [ -z "$X64_FFPROBE_PATH" ]; then
echo "ERROR: x64 ffmpeg/ffprobe binary missing from downloaded archives"
exit 1
fi
file "$X64_FFMPEG_PATH"
file "$X64_FFPROBE_PATH"
if ! file "$X64_FFMPEG_PATH" | grep -q "x86_64"; then
echo "ERROR: x64 ffmpeg artifact is not x86_64"
exit 1
fi
if ! file "$X64_FFPROBE_PATH" | grep -q "x86_64"; then
echo "ERROR: x64 ffprobe artifact is not x86_64"
exit 1
fi
install -m 755 "$X64_FFMPEG_PATH" vendor/ffmpeg/darwin-x64/ffmpeg
install -m 755 "$X64_FFPROBE_PATH" vendor/ffmpeg/darwin-x64/ffprobe
rm -rf ffmpeg-x64-temp ffprobe-x64-temp ffmpeg-x64.zip ffprobe-x64.zip
if [ ! -f vendor/ffmpeg/darwin-x64/ffprobe ]; then
echo "ERROR: x64 ffprobe is missing"
exit 1
fi
# 输出原始二进制信息
echo "=== Raw arm64 binary ==="
file vendor/ffmpeg/darwin-arm64/ffmpeg
file vendor/ffmpeg/darwin-arm64/ffprobe
ls -lh vendor/ffmpeg/darwin-arm64/ffmpeg
ls -lh vendor/ffmpeg/darwin-arm64/ffprobe
shasum -a 256 vendor/ffmpeg/darwin-arm64/ffmpeg || true
shasum -a 256 vendor/ffmpeg/darwin-arm64/ffprobe || true
lipo -archs vendor/ffmpeg/darwin-arm64/ffmpeg || true
echo "=== Raw x64 binary ==="
file vendor/ffmpeg/darwin-x64/ffmpeg
file vendor/ffmpeg/darwin-x64/ffprobe
ls -lh vendor/ffmpeg/darwin-x64/ffmpeg
ls -lh vendor/ffmpeg/darwin-x64/ffprobe
shasum -a 256 vendor/ffmpeg/darwin-x64/ffmpeg || true
shasum -a 256 vendor/ffmpeg/darwin-x64/ffprobe || true
lipo -archs vendor/ffmpeg/darwin-x64/ffmpeg || true
# 生成通用二进制并放入固定目录
echo "Creating universal ffmpeg via lipo"
lipo -create -output vendor/ffmpeg/darwin/ffmpeg \
vendor/ffmpeg/darwin-arm64/ffmpeg \
vendor/ffmpeg/darwin-x64/ffmpeg
echo "Creating universal ffprobe via lipo"
lipo -create -output vendor/ffmpeg/darwin/ffprobe \
vendor/ffmpeg/darwin-arm64/ffprobe \
vendor/ffmpeg/darwin-x64/ffprobe
chmod +x vendor/ffmpeg/darwin/ffmpeg
chmod +x vendor/ffmpeg/darwin/ffprobe
echo "=== Universal binary ==="
file vendor/ffmpeg/darwin/ffmpeg
file vendor/ffmpeg/darwin/ffprobe
ls -lh vendor/ffmpeg/darwin/ffmpeg
ls -lh vendor/ffmpeg/darwin/ffprobe
shasum -a 256 vendor/ffmpeg/darwin/ffmpeg || true
shasum -a 256 vendor/ffmpeg/darwin/ffprobe || true
lipo -archs vendor/ffmpeg/darwin/ffmpeg || true
lipo -archs vendor/ffmpeg/darwin/ffprobe || true
# 清理单架构副本
rm -rf vendor/ffmpeg/darwin-arm64 vendor/ffmpeg/darwin-x64
echo "Listing vendor/ffmpeg directory"
find vendor/ffmpeg -maxdepth 2 -type f -exec ls -l {} \;
- name: Prepare Chromaprint (macOS universal)
run: |
set -euxo pipefail
CHROMAPRINT_VERSION="1.5.1"
TAG="v${CHROMAPRINT_VERSION}"
mkdir -p vendor/chromaprint/darwin
curl -L "https://github.com/acoustid/chromaprint/releases/download/${TAG}/chromaprint-fpcalc-${CHROMAPRINT_VERSION}-macos-universal.tar.gz" -o fpcalc-mac.tar.gz
tar -xzf fpcalc-mac.tar.gz
install -m 755 chromaprint-fpcalc-${CHROMAPRINT_VERSION}-macos-universal/fpcalc vendor/chromaprint/darwin/fpcalc
rm -rf fpcalc-mac.tar.gz chromaprint-fpcalc-${CHROMAPRINT_VERSION}-macos-universal
ls -l vendor/chromaprint/darwin
- name: Build Rust N-API for mac (both arch)
working-directory: rust_package
run: |
npx --yes @napi-rs/cli@2.18.4 build --platform --release --target x86_64-apple-darwin
npx --yes @napi-rs/cli@2.18.4 build --platform --release --target aarch64-apple-darwin
- name: Build (vite)
env:
CLOUD_SYNC_BASE_URL_PROD: ${{ secrets.CLOUD_SYNC_BASE_URL_PROD }}
CLOUD_SYNC_API_SECRET_KEY: ${{ secrets.CLOUD_SYNC_API_SECRET_KEY }}
run: pnpm build
- name: Package macOS (universal, no publish)
env:
DEBUG: electron-builder
run: npx electron-builder --mac --universal --publish never
- name: Verify packaged Demucs resources (macOS)
run: |
set -euo pipefail
APP_DIR="$(find dist/mac-universal -maxdepth 1 -name '*.app' -type d | head -n 1 || true)"
if [ -z "$APP_DIR" ]; then
echo "macOS app bundle not found under dist/mac-universal"
exit 1
fi
RESOURCES_DIR="${APP_DIR}/Contents/Resources"
if [ ! -d "$RESOURCES_DIR" ]; then
echo "macOS resources directory not found: ${RESOURCES_DIR}"
exit 1
fi
REQUIRED_PATHS=(
"ffmpeg/darwin/ffmpeg"
"ffmpeg/darwin/ffprobe"
"demucs/bootstrap/mixtape_demucs_bootstrap.py"
"demucs/models/htdemucs.yaml"
"demucs/models/955717e8-8726e21a.th"
"rekordboxDesktopLibrary/bridge.py"
"rekordboxDesktopRuntime/darwin-arm64/python/bin/python3"
"rekordboxDesktopRuntime/darwin-x64/python/bin/python3"
)
UNEXPECTED_PATHS=(
"demucs/darwin-arm64"
"demucs/darwin-x64"
"demucs/win32-x64"
)
MISSING=0
for relative_path in "${REQUIRED_PATHS[@]}"; do
target_path="${RESOURCES_DIR}/${relative_path}"
if [ ! -e "$target_path" ]; then
echo "Missing packaged Demucs resource (macOS): ${relative_path}"
MISSING=1
fi
done
for relative_path in "${UNEXPECTED_PATHS[@]}"; do
target_path="${RESOURCES_DIR}/${relative_path}"
if [ -e "$target_path" ]; then
echo "Unexpected packaged Demucs runtime (macOS): ${relative_path}"
MISSING=1
fi
done
if [ "$MISSING" -ne 0 ]; then
exit 1
fi
echo "Packaged Demucs resources verified (macOS, runtime excluded)."
# 不再复制 rc.yml / rc-mac.yml,保持标准文件名
- name: Upload dist artifacts (macOS)
uses: actions/upload-artifact@v7
if: always()
with:
name: mac-artifacts
path: |
dist/*.dmg
dist/*.yml
dist/FRKB-*-mac-universal.zip
dist/FRKB-*-mac-universal.zip.blockmap
dist/*.blockmap
dist/*.zip
release:
needs: [build_win, build_mac]
runs-on: ubuntu-latest
steps:
- name: Download Windows artifacts
uses: actions/download-artifact@v8
with:
name: win-artifacts
path: artifacts/win
- name: Download macOS artifacts
uses: actions/download-artifact@v8
with:
name: mac-artifacts
path: artifacts/mac
- name: Show downloaded files
run: ls -R artifacts | cat
- name: Create/Update GitHub Release and upload assets
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
TAG_NAME: ${{ github.ref_name }}
PRERELEASE: ${{ contains(github.ref_name, '-') }}
run: |
set -euo pipefail
shopt -s nullglob
assets=(
artifacts/win/*.exe
artifacts/win/latest.yml
artifacts/win/*.blockmap
artifacts/mac/*.dmg
artifacts/mac/latest-mac.yml
artifacts/mac/*.zip
artifacts/mac/*.blockmap
)
if [ ${#assets[@]} -eq 0 ]; then
echo "No release assets found."
exit 1
fi
if gh release view "$TAG_NAME" >/dev/null 2>&1; then
gh release upload "$TAG_NAME" "${assets[@]}" --clobber
if [ "$PRERELEASE" = "true" ]; then
gh release edit "$TAG_NAME" --title "$TAG_NAME" --prerelease
else
gh release edit "$TAG_NAME" --title "$TAG_NAME" --latest
fi
else
if [ "$PRERELEASE" = "true" ]; then
gh release create "$TAG_NAME" "${assets[@]}" --title "$TAG_NAME" --prerelease --verify-tag
else
gh release create "$TAG_NAME" "${assets[@]}" --title "$TAG_NAME" --latest --verify-tag
fi
fi