Building for Mobile & Desktop
zmNinjaNg can be built as a native mobile app (Android, iOS) or a desktop app (macOS, Windows, Linux). The same source code is used across all platforms.
Prerequisites
Node.js ^20.19.0 or >=22.12.0 and npm
For Android: Android Studio, JDK 17+
For iOS: Xcode, macOS, Apple Developer account
Web Build
The simplest target. Produces static files you can host anywhere.
cd app
npm install
npm run build # Output: dist/
npm run preview # Preview locally
Deploy the dist/ folder to Netlify, Vercel, GitHub Pages, AWS S3, or
any static host.
Desktop Build (Electron)
Produces a native desktop app via Electron.
cd app
npm install
npm run electron:dev # Development with HMR
npm run electron:build # Production build -> desktop_release_builds/electron/
macOS Code Signing: Avoiding Keychain Prompts
When running npm run electron:build on macOS, you may be prompted for your
keychain password multiple times (once per binary that needs signing). To
avoid this:
Open Keychain Access
Under My Certificates, find your signing certificate and expand it to reveal the private key
Right-click the private key and select Get Info
Go to the Access Control tab
Select Allow all applications to access this item
Click Save Changes and enter your keychain password when prompted
This is a one-time change. You will not be prompted again for subsequent builds.
Mobile Builds
Versioning
Two numbers identify a build, set by
sync-version.js
(run by npm run android:sync / npm run ios:sync and by the release
script):
Marketing version, from
package.jsonversion(e.g.1.1.14). Written to AndroidversionNameand iOSMARKETING_VERSION(CFBundleShortVersionString). This is the version shown in the store listing and the app sidebar. Bump it on a prod release.Build number, the git commit count (
git rev-list --count HEAD). The stores require this to strictly increase per upload. iOSCURRENT_PROJECT_VERSION(CFBundleVersion) uses the commit count directly, sov1.1.14 (1533)in the app sidebar matchesCFBundleVersion 1533in App Store Connect. AndroidversionCodeadds a base offset of 100000 (100000 + commit count): builds before mid-2026 usedmajor*10000 + minor*100 + patch(v1.1.14->10114), and the raw commit count is below those legacy codes, so Google Play would reject it as a downgrade. The offset clears any legacy code (at most 99999 for versions below 10.0.0), so commit1533becomesversionCode 101533. The app sidebar shows the un-offset commit count.
The git commit count increases on every commit and stays monotonic as long as
release builds come from main without rewriting published history. The
Android versionCode is a signed 32-bit integer capped at 2,100,000,000 by
Google Play, which 100000 + commit count stays far below.
The generated versionCode / CURRENT_PROJECT_VERSION values in
app/android/app/build.gradle and the Xcode project are regenerated and
committed at release time by make_release.sh; they are not committed on
every change.
The build number also goes into the desktop artifact filenames, so a downloaded binary identifies its exact build:
zmNinjaNg-1.1.14-b1512-macos-aarch64.dmg
zmNinjaNg-1.1.14-b1512-windows-x64-setup.exe
zmNinjaNg-1.1.14-b1512-linux-amd64.AppImage
The release workflows set this in their rename steps; local builds and
build-all.yml use the electron-builder ${env.BUILD_NUMBER} macro, which
sync-version.js exports via $GITHUB_ENV in CI and
build-desktop-electron.sh exports locally.
Because the build number is the git commit count, the build workflows check
out with fetch-depth: 0. The default shallow clone makes
git rev-list --count HEAD return 1.
Automated Releases
zmNinjaNg uses GitHub Actions to build release binaries automatically. See make_release.sh for the release workflow.
To enable automated builds on your fork:
Go to Settings > Actions > General in your GitHub repository
Under Workflow permissions, select Read and write permissions
Click Save
Pushing a version tag triggers builds for Android, Linux (amd64 and arm64), macOS, and Windows. iOS is not built in CI, use the steps in iOS Build Guide to build and submit it locally from a Mac with Xcode.