Skip to content

Readme update

Readme update #61

Workflow file for this run

# Builds the standalone macOS .app for Apple Silicon and attaches the resulting
# zip to the GitHub release that triggered the run.
#
# Apple Silicon ONLY (M1/M2/M3/M4...). AudioMuse-AI's macOS build targets arm64
# exclusively — there is no Intel build. onnxruntime/PyAV/voyager wheels are not
# reliably universal2, so we build natively on an arm64 runner (macos-14).
#
# Reproducibility: the small, exact-version native build inputs are committed in
# git (macos/vendor/redis/arm64/redis-server and macos/vendor/pg-contrib/arm64/*),
# NOT regenerated at build time, so the bundle's embedded Redis and the Postgres
# unaccent/pg_trgm contrib extensions are byte-identical to the tested local build.
#
# The app is ad-hoc signed only (no Apple Developer account); end users clear
# quarantine with `xattr -dr ...` after download (see macos/README.md).
#
# The ~5 GB of models are NOT in git. This workflow assembles ./model from the
# GitHub releases the Dockerfile uses, INCLUDING the HuggingFace cache
# (huggingface_models.tar.gz) — macos/env.py points HF_HOME at model/huggingface
# for the CLAP RoBERTa tokenizer, so the bundle needs it. The HF cache is then
# trimmed to just that tokenizer (bert/bart and the roberta weights are unused by
# the app, ~1.4 GB) so the release zip stays under GitHub's 2 GB asset limit.
name: Build standalone macOS app
on:
push:
tags:
- 'v*.*.*' # Build on every version tag (same trigger as the Docker builds)
pull_request: # Build on PRs too, to catch macOS bundle breakage early.
types: # PR runs build+verify only; the release job is gated to tags.
- opened
- reopened
- synchronize
workflow_dispatch: # Allow manual runs for testing without a release
concurrency:
group: build-macos-${{ github.ref }}
cancel-in-progress: true
# Least-privilege default for GITHUB_TOKEN: read-only. Only the release job
# widens it (`contents: write`, to attach the zip to a release). The PR
# test-build link is posted by the separate `pr-test-link.yml` workflow.
permissions:
contents: read
env:
# Releases the models live on. Bump these in lockstep with the Dockerfile.
MODEL_RELEASE: v5.0.0-model
DCLAP_RELEASE: v1
jobs:
build:
# Apple Silicon only. macos-14 is arm64; do NOT add an Intel (macos-13) leg.
# Skip draft PRs and fork PRs — the same guard the Docker build workflows use.
if: >-
github.event_name != 'pull_request' ||
(github.event.pull_request.draft == false &&
github.event.pull_request.head.repo.full_name == github.repository)
runs-on: macos-14
permissions:
contents: read
env:
ARCH: arm64
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Sanity-check runner architecture (must be arm64)
run: |
set -euo pipefail
GOT="$(uname -m)"
if [ "$GOT" != "arm64" ]; then
echo "::error::This build is Apple-Silicon-only but uname -m reports $GOT"
exit 1
fi
echo "Building for $GOT"
- name: Verify committed native build inputs are present
run: |
set -euo pipefail
# These are committed in git (not regenerated here) so the bundle is
# reproducible. If a fresh checkout is missing them, fail loudly.
required=(
macos/vendor/redis/arm64/redis-server
macos/vendor/pg-contrib/arm64/lib/unaccent.dylib
macos/vendor/pg-contrib/arm64/lib/pg_trgm.dylib
macos/vendor/pg-contrib/arm64/extension/unaccent.control
macos/vendor/pg-contrib/arm64/extension/pg_trgm.control
macos/vendor/pg-contrib/arm64/tsearch_data/unaccent.rules
)
missing=0
for f in "${required[@]}"; do
if [ ! -s "$f" ]; then echo "::error::Missing committed vendor file: $f"; missing=1; fi
done
[ "$missing" -eq 0 ] || { echo "::error::macos/vendor/ must be committed (see macos/vendor/pg-contrib/README.md)."; exit 1; }
chmod +x macos/vendor/redis/arm64/redis-server
file macos/vendor/redis/arm64/redis-server
- name: Assemble ./model (mirrors the Dockerfile models stage)
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
mkdir -p model
echo "==> musicnn + CLAP text models (from ${MODEL_RELEASE})"
gh release download "$MODEL_RELEASE" -R "$GITHUB_REPOSITORY" -D model --clobber \
-p musicnn_embedding.onnx \
-p musicnn_prediction.onnx \
-p clap_text_model.onnx
echo "==> DCLAP audio model (from ${DCLAP_RELEASE} in the -DCLAP repo)"
gh release download "$DCLAP_RELEASE" -R NeptuneHub/AudioMuse-AI-DCLAP -D model --clobber \
-p model_epoch_36.onnx \
-p model_epoch_36.onnx.data
echo "==> HuggingFace cache (roberta/bert/bart) — HF_HOME points at model/huggingface"
tmp_hf="$(mktemp -d)"
gh release download "$MODEL_RELEASE" -R "$GITHUB_REPOSITORY" -D "$tmp_hf" --clobber \
-p huggingface_models.tar.gz
mkdir -p model/huggingface
tar -xzf "$tmp_hf/huggingface_models.tar.gz" -C model/huggingface
rm -rf "$tmp_hf"
echo "==> Trim HF cache to just the roberta-base tokenizer (~1.4 GB saved)"
# The macOS app's ONLY runtime HF dependency is the roberta-base *tokenizer*
# (tasks/clap_analyzer.py: AutoTokenizer.from_pretrained("roberta-base")).
# bert-base-uncased and bart-base are never loaded by the app, and a tokenizer
# does not need model weights. Dropping them keeps the release zip under the
# 2 GB GitHub release-asset limit without removing any model the app uses.
# Note: this prunes only the macOS bundle copy — the shared release tarball
# and the Docker build are unaffected.
hf="model/huggingface/hub"
rm -rf "$hf/models--bert-base-uncased" "$hf/models--facebook--bart-base"
rb="$hf/models--roberta-base"
if [ -d "$rb" ]; then
# Drop the ~476 MB weight blob; AutoTokenizer reads only the tiny
# tokenizer.json/vocab.json/merges.txt/config files (all < 2 MB).
find "$rb/blobs" -type f -size +10M -delete
find "$rb/snapshots" \( -name "model.safetensors" -o -name "pytorch_model.bin" \) -delete
du -sh "$rb"
fi
echo "==> lyrics bundles (whisper / silero / gte) — downloaded then extracted into ./model"
tmp="$(mktemp -d)"
gh release download "$MODEL_RELEASE" -R "$GITHUB_REPOSITORY" -D "$tmp" --clobber \
-p lyrics_model_whisper.tar.gz \
-p lyrics_model_silero_vad.tar.gz \
-p lyrics_model_gte_vnni.tar.gz
for t in lyrics_model_whisper lyrics_model_silero_vad lyrics_model_gte_vnni; do
tar -xzf "$tmp/$t.tar.gz" -C model
done
rm -rf "$tmp"
- name: Verify the assembled model/ is complete
run: |
set -euo pipefail
required=(
model/musicnn_embedding.onnx
model/musicnn_prediction.onnx
model/clap_text_model.onnx
model/model_epoch_36.onnx
model/model_epoch_36.onnx.data
model/huggingface/hub/models--roberta-base/snapshots
model/silero_vad.onnx
model/gte-multilingual-base-int8.onnx
model/whisper-small-onnx/encoder_model.onnx
model/whisper-small-onnx/decoder_model_merged.onnx
model/gte-multilingual-base/tokenizer.json
)
missing=0
for f in "${required[@]}"; do
if [ ! -e "$f" ]; then echo "::error::Missing or empty: $f"; missing=1; fi
done
# The roberta tokenizer files must survive the HF-cache prune above.
if [ -z "$(find model/huggingface/hub/models--roberta-base -name tokenizer.json -print -quit)" ]; then
echo "::error::roberta-base tokenizer.json missing after HF-cache prune"; missing=1
fi
[ "$missing" -eq 0 ] || { echo "::error::Refusing to build an incomplete bundle."; exit 1; }
du -sh model
- name: Install Python dependencies
run: |
set -euo pipefail
python3.12 -m venv .venv-macos
source .venv-macos/bin/activate
pip install --upgrade pip
pip install -r requirements/macos.txt
- name: Build the app
run: |
set -euo pipefail
source .venv-macos/bin/activate
bash macos/build.sh
- name: Upload zip as a workflow artifact
uses: actions/upload-artifact@v4
with:
name: AudioMuse-AI-${{ env.ARCH }}
path: dist/AudioMuse-AI-${{ env.ARCH }}.zip
if-no-files-found: error
# NB: the PR-description "test build links" block is no longer written
# here. The dedicated `pr-test-link.yml` workflow runs after this build
# completes, verifies the artifact exists, and posts the consolidated
# macOS + Linux + Docker link block to the PR.
# Attach the arm64 zip to the release. Runs only for tag pushes.
release:
needs: build
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Download built zip
uses: actions/download-artifact@v4
with:
path: artifacts
merge-multiple: true
- name: Attach zip to the release
uses: softprops/action-gh-release@v2
with:
files: artifacts/AudioMuse-AI-*.zip
fail_on_unmatched_files: true