Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ DATABASE_PATH=./data/acestep.db
# Run ./models.sh to download the default Q8_0 essential set (~8 GB).
MODELS_DIR=./models

# Electron desktop app: MODELS_DIR is resolved in this priority order:
# 1. MODELS_DIR env var set before launching (e.g. export MODELS_DIR=/my/models)
# 2. Path chosen via the "Browse for existing models…" dialog (saved in prefs.json)
# 3. Default: <userData>/models (macOS: ~/Library/Application Support/ACE-Step UI/models)
# (Linux: ~/.config/ACE-Step UI/models)
# Setting MODELS_DIR in the environment lets you reuse a shared models folder
# across multiple tools without copying or re-downloading the ~8 GB model set.

# ── acestep-cpp: two modes, pick ONE ─────────────────────────────────────────
#
# Mode 1 (recommended, zero-config): binaries are auto-discovered in ./bin/
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
cache: "npm"

- name: Install dependencies
run: npm ci
run: npm install

- name: TypeScript check
run: ./node_modules/.bin/tsc --noEmit
Expand Down
346 changes: 346 additions & 0 deletions .github/workflows/electron-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,346 @@
name: Electron Release

# Triggered by a version tag push (v*) or a manual run.
on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
tag:
description: "Release tag (e.g. v1.0.0)"
required: true
default: "v0.0.1-electron"
binary_version:
description: "acestep.cpp release tag to bundle (e.g. v0.0.1)"
required: false
default: "v0.0.1"

concurrency:
group: electron-release-${{ github.ref }}
cancel-in-progress: true

env:
NODE_VERSION: "20"
# acestep.cpp binary release to bundle; override via workflow_dispatch input.
BINARY_VERSION: ${{ github.event.inputs.binary_version || 'v0.0.1' }}

# ──────────────────────────────────────────────────────────────────────────────
# Shared setup steps are defined as a reusable composite action inline via
# `run` steps repeated in each job. Each job is self-contained so the CI
# log is easy to read and debug per platform.
#
# Archive layout (flat tarball — all files at ./):
# bin/<binary> ace-qwen3, dit-vae, neural-codec, …
# bin/lib<name>.so Linux shared libraries (unversioned names)
# bin/lib<name>.dylib macOS dylibs (versioned + symlink chain)
# ──────────────────────────────────────────────────────────────────────────────

jobs:

# ────────────────────────────────────────────────────────────────────────────
# macOS — Apple Silicon → ACE-Step UI-*.dmg
# ────────────────────────────────────────────────────────────────────────────
build-mac:
name: Build — macOS arm64
runs-on: macos-14
permissions:
contents: read

steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: "npm"

- name: Install root dependencies
run: npm ci

- name: Install server dependencies
run: npm ci
working-directory: server

- name: Build frontend
run: npm run build

- name: Build server
run: npm run build
working-directory: server

- name: Download & extract acestep.cpp binaries
shell: bash
run: |
ARCHIVE="acestep-macos-arm64-metal.tar.gz"
URL="https://github.com/audiohacking/acestep.cpp/releases/download/${BINARY_VERSION}/${ARCHIVE}"
echo "Downloading ${ARCHIVE} from ${URL} …"
curl -fsSL --retry 3 "${URL}" -o "${ARCHIVE}"
mkdir -p bin
tar -xzf "${ARCHIVE}" -C bin/
echo "bin/ contents:"
ls -lh bin/

- name: Verify macOS dylibs
shell: bash
run: |
# macOS dylibs ship as versioned files + two-level symlink chain:
# lib<name>.0.9.7.dylib → lib<name>.0.dylib → lib<name>.dylib
GGML_VER="0.9.7"
warn=0
for base in libggml libggml-base libggml-metal libggml-cpu libggml-blas; do
for name in "${base}.${GGML_VER}.dylib" "${base}.0.dylib" "${base}.dylib"; do
if [ -e "bin/${name}" ]; then echo "✅ bin/${name}"
else echo "⚠️ bin/${name} — missing"; warn=1; fi
done
done
for bin in ace-qwen3 dit-vae neural-codec; do
if [ -f "bin/${bin}" ] && [ -x "bin/${bin}" ]; then echo "✅ bin/${bin}"
else echo "⚠️ bin/${bin} — not found or not executable"; warn=1; fi
done
[ "$warn" = "0" ] || echo "⚠️ Some files missing — verify BINARY_VERSION=${BINARY_VERSION}"

- name: Rebuild native modules for Electron
run: npx @electron/rebuild --module-dir server --only better-sqlite3

- name: Build Electron app bundle (unpacked)
# Build the unpacked .app directory so we can sign it before packaging.
# electron-builder --dir skips DMG creation; we create the DMG ourselves
# after signing so the delivered image contains a properly signed bundle.
run: npm run electron:build:mac -- --dir
env:
CSC_IDENTITY_AUTO_DISCOVERY: false # we sign manually below

- name: Code sign the app bundle
shell: bash
run: |
APP="$(find release/mac-arm64 -maxdepth 1 -name '*.app' | head -1)"
if [ -z "$APP" ]; then
echo "❌ No .app bundle found in release/mac-arm64/"
ls -lh release/mac-arm64/ || true
exit 1
fi
echo "App bundle: $APP"
chmod +x build/macos/codesign.sh
./build/macos/codesign.sh "$APP"
env:
MACOS_SIGNING_IDENTITY: ${{ secrets.MACOS_SIGNING_IDENTITY || '-' }}

- name: Create DMG
shell: bash
run: |
APP="$(find release/mac-arm64 -maxdepth 1 -name '*.app' | head -1)"
APP_NAME="$(basename "$APP" .app)"
VERSION="$(node -p "require('./package.json').version")"
DMG_NAME="${APP_NAME}-${VERSION}-arm64.dmg"

echo "Creating DMG: ${DMG_NAME}"
mkdir -p dist_dmg
cp -R "$APP" dist_dmg/
# Add Applications symlink for drag-and-drop install
ln -sf /Applications dist_dmg/Applications

hdiutil create \
-volname "${APP_NAME}" \
-srcfolder dist_dmg \
-ov \
-format UDZO \
"release/${DMG_NAME}"

echo "✅ DMG: release/${DMG_NAME}"
ls -lh "release/${DMG_NAME}"

- name: Upload macOS artifact
uses: actions/upload-artifact@v4
with:
name: electron-macos-arm64
path: release/**/*.dmg
if-no-files-found: warn
retention-days: 7

# ────────────────────────────────────────────────────────────────────────────
# Linux — x86_64 → ACE-Step UI-*.AppImage + ACE-Step UI-*.snap
#
# The Linux ELFs have a hardcoded RUNPATH pointing to the CI build tree.
# At runtime electron/main.js prepends BIN_DIR to LD_LIBRARY_PATH so the
# bundled shared libraries are found regardless.
#
# The archive ships unversioned .so names (libggml.so) but ELFs link against
# versioned sonames (libggml.so.0). We create the missing symlinks before
# packaging so electron-builder includes them in extraResources.
# ────────────────────────────────────────────────────────────────────────────
build-linux:
name: Build — Linux x64
runs-on: ubuntu-22.04
permissions:
contents: read

steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: "npm"

- name: Install root dependencies
run: npm ci

- name: Install server dependencies
run: npm ci
working-directory: server

- name: Build frontend
run: npm run build

- name: Build server
run: npm run build
working-directory: server

- name: Download & extract acestep.cpp binaries
shell: bash
run: |
ARCHIVE="acestep-linux-x64.tar.gz"
URL="https://github.com/audiohacking/acestep.cpp/releases/download/${BINARY_VERSION}/${ARCHIVE}"
echo "Downloading ${ARCHIVE} from ${URL} …"
curl -fsSL --retry 3 "${URL}" -o "${ARCHIVE}"
mkdir -p bin
tar -xzf "${ARCHIVE}" -C bin/
echo "bin/ contents:"
ls -lh bin/

- name: Create versioned soname symlinks
shell: bash
run: |
# ELFs link against libggml.so.0 / libggml-base.so.0 (sonames) but
# the archive ships the unversioned names. Create the missing links.
cd bin
for pair in "libggml.so:libggml.so.0" "libggml-base.so:libggml-base.so.0"; do
real="${pair%%:*}"
soname="${pair##*:}"
if [ -f "$real" ] && [ ! -e "$soname" ]; then
ln -sv "$real" "$soname"
fi
done
echo "Symlinks:"
ls -la | grep " -> " || echo "(none)"

- name: Verify Linux binaries & libraries
shell: bash
run: |
warn=0
for bin in ace-qwen3 dit-vae neural-codec; do
if [ -f "bin/${bin}" ] && [ -x "bin/${bin}" ]; then echo "✅ bin/${bin}"
else echo "⚠️ bin/${bin} — not found or not executable"; warn=1; fi
done
for lib in libggml.so libggml-base.so libggml.so.0 libggml-base.so.0; do
if [ -e "bin/${lib}" ]; then echo "✅ bin/${lib}"
else echo "⚠️ bin/${lib} — missing"; warn=1; fi
done
[ "$warn" = "0" ] || echo "⚠️ Some files missing — verify BINARY_VERSION=${BINARY_VERSION}"

- name: Rebuild native modules for Electron
run: npx @electron/rebuild --module-dir server --only better-sqlite3

- name: Install snapcraft
run: sudo snap install snapcraft --classic

- name: Build Electron package (Linux)
run: npm run electron:build:linux
env:
SNAPCRAFT_STORE_CREDENTIALS: "" # offline / no store upload

- name: Upload Linux artifacts
uses: actions/upload-artifact@v4
with:
name: electron-linux-x64
path: |
release/**/*.AppImage
release/**/*.snap
if-no-files-found: warn
retention-days: 7

# ────────────────────────────────────────────────────────────────────────────
# Publish a GitHub Release with all platform artifacts attached.
# ────────────────────────────────────────────────────────────────────────────
publish:
name: Publish GitHub Release
needs: [build-mac, build-linux]
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: actions/download-artifact@v4
with:
pattern: electron-*
path: release-artifacts
merge-multiple: true

- name: List release artifacts
run: find release-artifacts -type f | sort

- name: Resolve release tag
id: tag
shell: bash
run: |
TAG="${GITHUB_REF_NAME:-}"
[[ "$TAG" == v* ]] || TAG="${{ github.event.inputs.tag || 'v0.0.1-electron' }}"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"

- name: Publish GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.tag.outputs.tag }}
name: "ACE-Step UI ${{ steps.tag.outputs.tag }} — Desktop App"
draft: false
prerelease: ${{ contains(steps.tag.outputs.tag, '-') }}
body: |
## ACE-Step UI ${{ steps.tag.outputs.tag }} — Electron Desktop App

Native desktop application with embedded server and precompiled
[acestep.cpp](https://github.com/audiohacking/acestep.cpp) binaries bundled — no compiler or build step required.

### Downloads

| File | Platform |
|------|----------|
| `*.dmg` | macOS — Apple Silicon (arm64) |
| `*.AppImage` | Linux — x86_64 (portable, any distro) |
| `*.snap` | Linux — x86_64 (Snap Store / snapd) |

### First run

On first launch the app will offer to download the default Q8_0 model set
(~8 GB total) from HuggingFace automatically. You can also skip and
copy models manually to:
- **macOS** `~/Library/Application Support/ACE-Step UI/models/`
- **Linux** `~/.config/ACE-Step UI/models/`

Generated audio is saved to `~/Music/ACEStep/`.

### macOS notes

The DMG is ad-hoc signed (no Apple notarisation required for dev builds).
If macOS still shows a "damaged or incomplete" warning, run once:
```
xattr -cr "/Applications/ACE-Step UI.app"
```

### Linux notes

**AppImage** — make executable then run:
```
chmod +x ACE-Step*.AppImage && ./ACE-Step*.AppImage
```

**Snap** — install locally (classic confinement required):
```
sudo snap install *.snap --dangerous --classic
```
files: release-artifacts/**/*
fail_on_unmatched_files: false
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,8 @@ wavacity-editor/

# Script logs
logs/

# Electron build output
release/
dist-electron/

Loading
Loading