-
-
Notifications
You must be signed in to change notification settings - Fork 140
231 lines (209 loc) · 9.67 KB
/
Copy pathbuild-macos.yml
File metadata and controls
231 lines (209 loc) · 9.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# 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