تخطَّ إلى المحتوى

Build a macOS desktop app (Tauri)

هذا المحتوى غير متوفر بلغتك بعد.

Packaging the tool as a macOS desktop app uses Tauri (Rust + the system WebView) as the shell and bundles serve.py as a PyInstaller sidecar. The desktop app bundles only the Playground tool (/ + /api/*) — not the landing page or docs — so it opens straight into the tool. Everything lives in desktop/.

  1. Tauri launches and spawns the serve sidecar with --port 0 --data-dir <appData>.
  2. The sidecar seeds ~/Library/Application Support/vn.fighttech.appstorepreview/ from the bundled tool on first run — so writes (.env, listing*.json, assets/new, assets/current) go to a writable dir, not the read-only .app.
  3. serve.py binds a free port and prints READY http://127.0.0.1:<port>/. With no landing page bundled, / serves the tool directly.
  4. Tauri reads that line and opens the window on the URL — same origin for UI and /api/*, so Sync / Test / Try template work exactly like run.sh.

Only the Playground tool is bundled, so Node is not needed for the desktop build. The runtime is the Python sidecar + the WebView.

Terminal window
cd desktop
bash scripts/setup-prereqs.sh # one-time: Rust, PyInstaller, Python deps, node
bash scripts/build.sh # icons → sidecar (PyInstaller) → tauri build
# → src-tauri/target/release/bundle/dmg/*.dmg
ScriptDoes
setup-prereqs.shInstalls Rust (rustup), PyInstaller + Python store deps, node deps
build-sidecar.shEmbeds the Playground tool, PyInstaller → src-tauri/binaries/serve-<triple>
build.shIcons + sidecar + tauri build.app / .dmg

Unsigned builds trigger a Gatekeeper warning on other Macs (“AppPreview is damaged”).

Section titled “Auto-setup from an App Store Connect API key (recommended)”

Put your ASC API key in desktop/.signing/signing.env (ASC_KEY_ID, ASC_ISSUER_ID, ASC_KEY_P8) and run:

Terminal window
cd desktop && python3 scripts/setup-signing.py

It creates the Developer ID Application certificate via the App Store Connect API, imports it into your keychain, exports DeveloperID.p12, and writes the APPLE_SIGNING_IDENTITY / APPLE_TEAM_ID / APPLE_CERTIFICATE_* / APPLE_API_* values. Then bash scripts/build.sh produces a signed + notarized .dmg that opens with no warning. (Needs Apple Developer Program; the API key must have Admin / Account Holder access.)

Or sign + notarize by hand: copy signing.env.examplesigning.env and drop your files in that folder:

desktop/.signing/
signing.env # filled-in config (gitignored)
DeveloperID.p12 # "Developer ID Application" cert + key (export from Keychain)
AuthKey_XXXXXXXXXX.p8 # App Store Connect API key (for notarization)

Key values:

  • APPLE_SIGNING_IDENTITY — e.g. Developer ID Application: Tran Trung Hieu (TEAMID) (find with security find-identity -v -p codesigning)
  • APPLE_CERTIFICATE_PASSWORD — the .p12 export password
  • APPLE_TEAM_ID — your 10-char Team ID
  • APPLE_API_ISSUER / APPLE_API_KEY / APPLE_API_KEY_PATH — the notary key

bash scripts/build.sh then signs the sidecar (hardened runtime via entitlements.plist) and tauri build signs + notarizes the .app/.dmg. The .signing/ folder is gitignored — copy it to another Mac to build there. In CI, provide the same values as repo secrets (see .github/workflows/release-dmg.yml).

The app updates itself via the Tauri updater — no manual reinstall:

  • File ▸ Check for Updates… (and a silent check on launch) queries the latest GitHub release manifest, and if newer, prompts to download + install + relaunch.
  • Updates are cryptographically verified with a signing key (separate from Apple signing). The keypair lives in desktop/.signing/updater.key (+ .pub); the public key is in tauri.conf.json → plugins.updater.pubkey.

Set up once:

Terminal window
npx @tauri-apps/cli signer generate -w desktop/.signing/updater.key
# put the printed public key into tauri.conf.json → plugins.updater.pubkey

On release (release-dmg.yml, tag desktop-v*), tauri build produces a signed AppPreview.app.tar.gz + .sig; the workflow generates latest.json and uploads all three to the GitHub Release. The updater endpoint (releases/latest/download/latest.json) serves the manifest, so installed apps see the new version. CI signs with secrets TAURI_SIGNING_PRIVATE_KEY (+ password).

To ship an update: bump version in tauri.conf.json, push a new desktop-v* tag.

  • Heavy deps (cryptography, google-api-python-client) are pulled in with PyInstaller --collect-all flags. After building, open the app and run Test keys to confirm the bundled Python can reach both store APIs.
  • “AppPreview is damaged and can’t be opened” — this is Gatekeeper blocking an un-notarized download (quarantine), not a real corruption. Fix: xattr -dr com.apple.quarantine "/Applications/AppPreview.app" then open. Right-click → Open does not clear “damaged”. The permanent fix is to sign (Developer ID) + notarize — see Code signing & notarization.
  • The window only appears after the sidecar prints READY; a loading splash shows meanwhile.

See desktop/README.md for the full layout.