This directory contains assets and scripts for building CTFN Studio as a standalone macOS application using PyInstaller.
build/macos/
├── CTFNStudio.icns # App icon (generated from frontend/public/ctfn-icon.png)
├── codesign.sh # Code signing script
├── generate_icon.sh # Script to generate .icns from PNG
└── hooks/
└── hook-heartlib.py # PyInstaller hook for heartlib
- macOS 10.13 or later
- Python 3.11
- Node.js 18+
- Homebrew (for icon generation tools)
Note: The app uses pywebview for native window rendering. This is automatically included in the build.
For the easiest local build experience, use the provided build script:
./local_build.shThis script replicates the GitHub Actions build process locally.
Before building, you can validate that all required files are present:
./validate_build_assets.shThis checks for:
- Required scripts (generate_icon.sh, codesign.sh)
- Required directories (backend/models, frontend/dist, etc.)
- Dependency configuration (transformers version constraints, triton, etc.)
- PyInstaller spec file configuration
- Install dependencies:
brew install librsvg imagemagick
pip install -r requirements_macos.txt- Build frontend:
cd frontend
npm install
npm run build
cd ..- Generate icon:
./build/macos/generate_icon.sh- Build with PyInstaller:
pyinstaller HeartMuLa.spec --clean --noconfirm- Set up executable:
cp dist/CTFNStudio.app/Contents/MacOS/CTFNStudio_bin dist/CTFNStudio.app/Contents/MacOS/CTFNStudio
chmod +x dist/CTFNStudio.app/Contents/MacOS/CTFNStudio- Code sign:
./build/macos/codesign.sh dist/CTFNStudio.app- Create DMG:
mkdir -p dmg_temp
cp -R dist/CTFNStudio.app dmg_temp/
cp CTFNStudio.command dmg_temp/
ln -s /Applications dmg_temp/Applications
cp .github/DMG_README.txt dmg_temp/README.txt
hdiutil create -volname "CTFN Studio" -srcfolder dmg_temp -ov -format UDZO CTFNStudio-macOS.dmgThe build process is automated via GitHub Actions. See .github/workflows/build-macos-release.yml.
To trigger a build:
- Create a new release on GitHub, or
- Manually trigger the workflow from the Actions tab
The macOS build is optimized for Apple Metal GPUs:
- PyTorch with MPS (Metal Performance Shaders) backend
- Automatic fallback for unsupported operations
- Environment variable
PYTORCH_ENABLE_MPS_FALLBACK=1is set by default
Native ARM64 support with Metal acceleration provides best performance.
Compatible with Intel Macs that have Metal-capable GPUs (2012 or later).
IMPORTANT: The macOS app stores ALL data in the user's Library directory, NOT in the app bundle:
~/Library/Application Support/HeartMuLa/
├── models/ # AI models (~5GB, auto-downloaded)
├── generated_audio/ # Generated music files
├── ref_audio/ # Uploaded reference audio
└── jobs.db # Song history database
~/Library/Logs/HeartMuLa/
└── (application logs)
The launcher (launcher.py) sets environment variables to ensure all data writes go to the user directory:
HEARTMULA_MODEL_DIR→ models directoryHEARTMULA_GENERATED_AUDIO_DIR→ generated audio directoryHEARTMULA_REF_AUDIO_DIR→ reference audio directoryHEARTMULA_DB_PATH→ database path
This approach ensures:
- The app bundle remains read-only (as required by code signing)
- User data persists across app updates
- Users can easily find and manage their generated music
- Standard macOS app behavior (data in Library folder)
The build includes ad-hoc code signing by default to prevent "damaged app" warnings.
For production releases, set the MACOS_SIGNING_IDENTITY secret in GitHub to your Apple Developer ID.
Ensure frontend/public/ctfn-icon.png exists. Install ImageMagick or use sips:
brew install imagemagickEnsure all dependencies are installed:
pip install -r requirements_macos.txt
pip install pyinstallerCommon issues:
- Missing backend/models directory: Fixed in latest version (directory created with .gitkeep)
- Transformers version conflict: requirements_macos.txt now constrains to
<4.57.0to avoid yanked version - Triton platform compatibility: Triton is not compatible with Apple Silicon. The dependency includes a platform constraint to skip installation on ARM64 Macs.
Right-click the app and select "Open" to bypass Gatekeeper.
For permanent fix, code sign with a valid Developer ID certificate.