These steps let you validate the macOS and Linux Electron builds locally before merging, without waiting for CI to produce a full installer. They specifically verify:
- The precompiled binary archive is correctly extracted into
bin/ - All required
libggmlshared libraries are present alongside the binaries - The symlink chain is intact (macOS
.0.dylib/.dylib; Linux.so.0) DYLD_LIBRARY_PATH(macOS) orLD_LIBRARY_PATH(Linux) is correctly set tobin/at runtime- The Electron app starts, shows the loading screen, and opens the UI
- First-run model dialog offers Download, Browse, and Skip options
- Browsing to an existing models folder persists the choice across restarts
MODELS_DIRenv var is respected and takes priority over saved preferences
| Tool | Minimum version | Check |
|---|---|---|
| Node.js | 20 | node -v |
| npm | 10 | npm -v |
| curl | any | curl --version |
| macOS (Apple Silicon) | 14 (Sonoma) | sw_vers |
| Linux (x86_64) | any glibc ≥ 2.31 | ldd --version |
Clone the branch and install dependencies:
# After merge this will be on the main branch; use the appropriate branch/tag:
git clone https://github.com/audiohacking/acestep-cpp-ui
cd acestep-cpp-ui
npm ci
npm ci --prefix serverBoth archives are flat tarballs (no subdirectory). Run the command for your platform:
mkdir -p bin
curl -fsSL --retry 3 \
https://github.com/audiohacking/acestep.cpp/releases/download/v0.0.2/acestep-macos-arm64-metal.tar.gz \
-o acestep-macos-arm64-metal.tar.gz
echo "Archive contents:"
tar -tzf acestep-macos-arm64-metal.tar.gz
echo ""
echo "Extracting …"
tar -xzf acestep-macos-arm64-metal.tar.gz -C bin/
echo ""
echo "bin/ after extraction:"
ls -lh bin/mkdir -p bin
curl -fsSL --retry 3 \
https://github.com/audiohacking/acestep.cpp/releases/download/v0.0.2/acestep-linux-x64.tar.gz \
-o acestep-linux-x64.tar.gz
echo "Archive contents:"
tar -tzf acestep-linux-x64.tar.gz
echo ""
echo "Extracting …"
tar -xzf acestep-linux-x64.tar.gz -C bin/
echo ""
echo "bin/ after extraction:"
ls -lh bin/Run:
GGML_VER="0.9.7"
ok=1
for bin in ace-lm ace-synth neural-codec; do
[ -f "bin/$bin" ] && [ -x "bin/$bin" ] \
&& echo "✅ bin/$bin" \
|| { echo "❌ bin/$bin — missing or not executable"; ok=0; }
done
for lib_base in libggml libggml-base libggml-metal libggml-cpu libggml-blas; do
for lib_name in "${lib_base}.${GGML_VER}.dylib" "${lib_base}.0.dylib" "${lib_base}.dylib"; do
[ -e "bin/${lib_name}" ] \
&& echo "✅ bin/${lib_name}" \
|| { echo "❌ bin/${lib_name} — missing"; ok=0; }
done
done
[ "$ok" = "1" ] && echo "" && echo "All checks passed." || echo "" && echo "FAILURES — see above."Expected: every line shows ✅. The archive ships the real .0.9.7.dylib files plus a two-level symlink chain (.0.dylib → .0.9.7.dylib and .dylib → .0.dylib). If any symlinks are missing, fixMacosDylibLinks() in electron/main.js will recreate them at app startup.
Run:
ok=1
for bin in ace-lm ace-synth neural-codec; do
[ -f "bin/$bin" ] && [ -x "bin/$bin" ] \
&& echo "✅ bin/$bin" \
|| { echo "❌ bin/$bin — missing or not executable"; ok=0; }
done
for lib in libggml.so libggml-base.so; do
[ -f "bin/$lib" ] \
&& echo "✅ bin/$lib" \
|| { echo "❌ bin/$lib — missing"; ok=0; }
done
[ "$ok" = "1" ] && echo "" && echo "All checks passed." || echo "" && echo "FAILURES — see above."Note:
libggml.so.0/libggml-base.so.0symlinks are created byfixSonameLinks()at first run; at this point only the unversioned names need to be present.
# Frontend (Vite → dist/)
npm run build
# Server (TypeScript → server/dist/)
npm run build --prefix serverBoth should complete without errors.
npx @electron/rebuild --module-dir server --only better-sqlite3This rebuilds better-sqlite3 against Electron's bundled Node.js ABI. Expect a success line like:
✔ Rebuild Complete
npm run electron:dev| # | Expected behaviour |
|---|---|
| 1 | A dark frameless loading window appears with a progress bar |
| 2 | Status text shows "Checking models…" before "Starting server…" (model check now runs first so the resolved path is passed to the server) |
| 3 | If no .gguf files exist in the models directory, a native dialog appears: "ACE-Step models not found" with three buttons: "Download now (~8 GB)", "Browse for existing models…", and "Skip — I'll add models manually" — click Skip for now |
| 4 | The main browser window opens at http://127.0.0.1:3001 and shows the ACE-Step UI |
| 5 | No crash / black screen / "library not found" errors in the terminal |
Models directory locations:
| Platform | Path |
|---|---|
| macOS | ~/Library/Application Support/ACE-Step UI/models/ |
| Linux | ~/.config/ACE-Step UI/models/ |
While the app is running, open a second terminal and confirm the environment variables are set by checking the server process:
# Find the ace-lm / ace-synth child process (if a generation is running) or
# inspect the Electron main process env via the logs directory:
cat ~/Library/Application\ Support/ACE-Step\ UI/logs/server.log | grep -i "DYLD"Alternatively, add a temporary console.log in electron/main.js after setupLibraryPaths():
console.log('DYLD_LIBRARY_PATH:', process.env.DYLD_LIBRARY_PATH);The value must contain the absolute path to bin/ (dev mode: <repo>/bin).
cat ~/.config/ACE-Step\ UI/logs/server.log | grep -i "LD_LIBRARY"Or add temporarily:
console.log('LD_LIBRARY_PATH:', process.env.LD_LIBRARY_PATH);After launching the app once, check that fixMacosDylibLinks() has created the symlinks if they were missing:
ls -la bin/*.dylib | grep " -> "Expected: 10 symlinks total (2 per library × 5 libraries):
libggml-base.0.dylib -> libggml-base.0.9.7.dylib
libggml-base.dylib -> libggml-base.0.dylib
libggml-blas.0.dylib -> libggml-blas.0.9.7.dylib
libggml-blas.dylib -> libggml-blas.0.dylib
libggml-cpu.0.dylib -> libggml-cpu.0.9.7.dylib
libggml-cpu.dylib -> libggml-cpu.0.dylib
libggml-metal.0.dylib -> libggml-metal.0.9.7.dylib
libggml-metal.dylib -> libggml-metal.0.dylib
libggml.0.dylib -> libggml.0.9.7.dylib
libggml.dylib -> libggml.0.dylib
After first launch:
ls -la bin/*.so* | grep " -> "Expected:
libggml.so.0 -> libggml.so
libggml-base.so.0 -> libggml-base.so
- With models present (or after downloading them), enter a short prompt such as
upbeat electronic music - Set Duration to
5sin the Advanced panel to keep the test fast - Click Generate
- Watch the Debug tab — you should see
ace-lmandace-synthoutput lines appear - The generated audio file should appear in the Songs list and be playable
If the binaries fail to load their shared libraries you will see an error like:
- macOS:
dyld: Library not loaded: @rpath/libggml.dyliborimage not found - Linux:
error while loading shared libraries: libggml.so.0: cannot open shared object file
Either error means the library path setup is not working — check Steps 5–7 above.
To fully validate the packaged release (as CI produces it):
npm run electron:build:macOpen release/*.dmg, mount it, and drag ACE-Step UI.app to /Applications. Launch it and repeat Steps 5–8. The packaged app uses process.resourcesPath/bin instead of the dev-mode bin/ directory — fixMacosDylibLinks() recreates any symlinks electron-builder may have dropped.
This step validates the new first-run UX for users who already have ACE-Step models locally.
-
Remove (or temporarily rename) the models directory so the app treats this as a first run:
# macOS mv ~/Library/Application\ Support/ACE-Step\ UI/models \ ~/Library/Application\ Support/ACE-Step\ UI/models.bak # Linux mv ~/.config/ACE-Step\ UI/models ~/.config/ACE-Step\ UI/models.bak
-
Clear any saved preference so the dialog triggers:
# macOS rm -f ~/Library/Application\ Support/ACE-Step\ UI/prefs.json # Linux rm -f ~/.config/ACE-Step\ UI/prefs.json
-
Run the app:
npm run electron:dev
-
The first-run dialog should appear with three buttons:
Download now (~8 GB)Browse for existing models…Skip — I'll add models manually
-
Click Browse for existing models… and navigate to the
.bakfolder (or any folder containing.gguffiles). -
Expected: the app starts normally and the models folder in the loading status shows the path you selected.
-
Quit and relaunch — the dialog should not appear again; the persisted path is used automatically.
-
Verify the saved preference:
# macOS cat ~/Library/Application\ Support/ACE-Step\ UI/prefs.json # Linux cat ~/.config/ACE-Step\ UI/prefs.json
Expected content:
{ "modelsDir": "/path/to/the/folder/you/selected" } -
Edge case — empty folder: repeat from step 3, click Browse, and select a folder that contains no
.gguffiles. A warning dialog should appear, the app should start without models (no crash), andprefs.jsonshould not be updated.
The env var takes priority over saved preferences.
# macOS / Linux
MODELS_DIR=/path/to/your/models npm run electron:devExpected:
- The first-run dialog does not appear (models found via env var)
- The server uses the specified directory for model lookups
- The saved
prefs.json(if any) is ignored in favour of the env var
To verify the server received the correct path, check the log:
# macOS
grep -i "MODELS_DIR\|models" ~/Library/Application\ Support/ACE-Step\ UI/logs/server.log | head -5
# Linux
grep -i "MODELS_DIR\|models" ~/.config/ACE-Step\ UI/logs/server.log | head -5Copy this into a PR comment to report results:
### Electron pre-merge test results
**Platform:** macOS arm64 / Linux x64 (circle one)
- [ ] Step 1: Binary archive downloaded and extracted without errors
- [ ] Step 2: All binaries executable; all libggml dylibs / .so files present
- [ ] Step 3: Frontend and server builds succeed
- [ ] Step 4: `@electron/rebuild` succeeds for `better-sqlite3`
- [ ] Step 5: App starts — loading window shows "Checking models…" first, then "Starting server…"; UI opens; no library errors
- [ ] Step 6: `DYLD_LIBRARY_PATH` / `LD_LIBRARY_PATH` points to `bin/`
- [ ] Step 7: All expected dylib / soname symlinks present after first launch
- [ ] Step 8: Generation smoke test completes and audio is playable
- [ ] Step 9 (macOS): Packaged `.dmg` installs and runs correctly
- [ ] Step 10a: Browse dialog — picking a valid folder persists path; invalid folder shows warning; preference survives relaunch
- [ ] Step 10b: `MODELS_DIR` env var overrides saved preference; dialog skipped when models found
**Notes / failures:**
<!-- describe anything unexpected -->