This document explains the automated build pipeline added for packaging the project as a desktop-style application.
- Automation script: scripts/package-desktop.sh
- Runtime launcher script: scripts/run-compiled-app.sh
- Electron desktop shell: desktop/main.js
- Desktop package config: desktop/package.json
- PyInstaller backend entrypoint: backend/packaging/serve_api_entry.py
- Next.js standalone output enabled in web-ui/next.config.ts
When you run the script, it performs these stages in order:
- Clean output folder
- Creates fresh build workspace under
engine/dist/desktop-build. - Can be skipped with
SKIP_CLEAN=1.
- Build backend executable (PyInstaller)
- Creates isolated Python build venv at
engine/backend/.venv-pack. - Installs backend requirements from
backend/requirement.txtpluspyinstaller. - Builds a distributable backend app (
onedir) namedgms-backend. - Uses
backend/packaging/serve_api_entry.py, which starts FastAPI directly with Uvicorn.
- Build frontend (Next.js)
- Runs
npm ciandnpm run buildinengine/web-ui. - Relies on
output: "standalone"in Next config. - Copies packaged frontend runtime from
.next/standaloneand.next/staticinto build output.
- Sync artifacts into Nexttron resources (if available)
- If
engine/desktop(orNEXTTRON_DIR) exists, backend/frontend artifacts are copied into:desktop/resources/backenddesktop/resources/frontend
- If
package.jsonexists in that folder and has adistscript, script runsnpm run dist.
- Print summary
- Displays where backend/frontend artifacts were created.
From repository root:
bash engine/scripts/package-desktop.shOr executable form:
./engine/scripts/package-desktop.shAfter build completes, start the compiled runtime with one command:
bash engine/scripts/run-compiled-app.shWhat this launcher does automatically:
- Verifies compiled backend/frontend artifacts exist.
- Picks free localhost ports automatically (unless
BACKEND_PORT/FRONTEND_PORTare set). - Starts backend and frontend in background processes.
- Waits for backend health and frontend readiness before opening browser.
- Writes logs to
engine/dist/runtime-logs/backend.logandengine/dist/runtime-logs/frontend.log. - Cleans up child processes on exit (
Ctrl+C, termination, or shell exit), which frees the ports.
Optional launcher env vars:
BACKEND_HOST(default:127.0.0.1)BACKEND_PORT(optional fixed backend port)FRONTEND_PORT(optional fixed frontend port)OPEN_BROWSER(1default, set0to skip auto-open)
Example:
OPEN_BROWSER=0 FRONTEND_PORT=3100 bash engine/scripts/run-compiled-app.shThis is the true desktop app flow (single window, hidden backend/frontend processes, clean shutdown).
- Build and sync artifacts to
engine/desktop/resources:
bash engine/scripts/package-desktop.sh- Install desktop shell dependencies:
cd engine/desktop
npm ci- Launch desktop window app:
npm run startDevelopment mode behavior (npm run start from engine/desktop):
- Backend runs from Python source (
python3 -m backend ...) instead of compiled executable. - You do not need to rebuild PyInstaller output for backend code-only changes.
- Frontend still runs from
desktop/resources/frontend/standalone, so rerun packaging after frontend build changes. - Interpreter selection in dev mode prefers project-local Python (for example
engine/backend/env/bin/python) before system Python commands. - Override Python binary with
PYTHON_BINif needed.
What this Electron shell handles automatically:
- In development mode, starts backend from source.
- In packaged mode, starts backend executable from
desktop/resources/backend. - Chooses free localhost ports for backend and frontend.
- Starts frontend standalone server from
desktop/resources/frontend/standalone/server.js. - Keeps both processes hidden from the user (no dedicated backend/frontend terminal windows).
- Loads the app in an Electron
BrowserWindow. - Kills child processes during app quit to free ports.
Desktop shell source files:
Optional variables to customize behavior:
PYTHON_BIN(default:python3)NODE_BIN(default:node)NPM_BIN(default:npm)PYTHON_BIN(default:python3, used by Electron dev startup)SKIP_CLEAN(1to keep previous build outputs)BACKEND_HOST(default:127.0.0.1)BACKEND_PORT(default:8000)NEXTTRON_DIR(default:engine/desktop)
Example:
BACKEND_PORT=18000 NEXTTRON_DIR=/absolute/path/to/your-nexttron-app ./engine/scripts/package-desktop.shAfter success, artifacts are under:
- Backend:
engine/dist/desktop-build/backend/gms-backend - Frontend:
engine/dist/desktop-build/frontend
If Nexttron packaging ran, final installer/app outputs are produced by your Nexttron dist script in its own configured output path.
Your Nexttron/Electron main process should:
- Start
resources/backendexecutable as a child process. - Wait for backend health endpoint to be ready.
- Serve/load
resources/frontendstandalone frontend. - Stop backend process when app closes.
This script already guarantees artifacts are copied to desktop/resources/* in that shape.
Window App Experience (Icon + Hidden Background Processes)
To get a true desktop window app experience (single app icon, no visible terminal processes), use Electron/Nexttron as the shell.
Recommended behavior in Electron main process:
- Spawn backend executable from
resources/backendwith hidden process options. - Wait until backend
/healthis ready. - Spawn frontend standalone server from
resources/frontend/standalone/server.js. - Load the frontend URL into
BrowserWindow. - On app exit, kill both child processes so ports are released.
Icon support:
- macOS icon file:
icon.icns - Windows icon file:
icon.ico - Configure icon in BrowserWindow and electron-builder packaging config.
If you do not use Electron/Nexttron yet, run-compiled-app.sh is the best available process manager and cleanup launcher.
Current status in this repository:
- Electron shell is already created in
engine/desktop. - Runtime icon uses
engine/desktop/assets/icon.pngwhen present. - You can add installer icons later (
icon.icnsandicon.ico) and extenddesktop/package.jsonbuild config.
-
No Nexttron directory found:- Create your Nexttron project and set
NEXTTRON_DIR, or place it underengine/desktop.
- Create your Nexttron project and set
-
No dist script:- Add a
distscript to your Nexttronpackage.jsonfor installer generation.
- Add a
-
Backend build fails at PyInstaller step:
- Ensure Python toolchain works and all backend imports are resolvable from
engineroot.
- Ensure Python toolchain works and all backend imports are resolvable from
-
Frontend build fails:
- Run
npm ci && npm run buildinengine/web-uimanually to inspect errors.
- Run