CodeNomad/BUILD.md
Pascal André 4e0b62bc7f
build: ship the Linux deb from Electron and the Windows installer without a ZIP (#955)
## Summary

One download per need, with fewer duplicate or unused assets:

| | Before | After |
|---|---|---|
| **Linux Electron** | portable `tar.gz` | **`.deb`** |
| **Linux Tauri** | `.deb` + AppImage | **AppImage** (self-updating) |
| **Windows Tauri** | ZIP wrapping the installer + the same installer as
`.exe` | **`.exe`** only |
| **Updater signatures** | `latest.json` + separate `.sig` assets |
**`latest.json` only** |

### Linux: Electron deb, Tauri AppImage
- The Electron deb installs as root, so electron-builder's post-install
script gives Chromium's `chrome-sandbox` the `root:root` ownership and
`4755` mode that a user-extracted archive cannot. Electron starts **with
its sandbox** and without any manual step. This fixes #541 properly.
- The package is named `codenomad` and **conflicts with / replaces** the
former Tauri deb (`code-nomad`). Both ship
`/usr/share/applications/CodeNomad.desktop`, so without this, installing
over an existing Tauri deb would fail on the shared file.
- The deb declares Electron's full runtime closure. electron-builder's
default `Depends` omit `libgbm1` and ALSA, which previously arrived only
through Recommends. ALSA is declared as `libasound2t64 | libasound2`,
because `libasound2` is a multi-provider virtual package on Ubuntu
24.04.
- Debian versions: dev builds (`<stable>-dev-<date>-<sha>`) become
`<stable>+dev.<date>.<HHMMSS commit time>.<sha>`, so they install as
upgrades after their stable base and in order on the same day; other
prereleases keep electron-builder's `~`, which sorts before the release.
The workflow rewrites the control `Version` with `dpkg-deb` when it
differs; PR and stable builds rewrite a copy to a synthetic dev version
and check that fields, files, modes, owners and control members are
preserved.
- Tauri Linux builds only the AppImage (`tauri build --bundles
appimage`).
- No unused electron-updater metadata is generated (`publish: null`).

### Windows: installer only
- The Tauri NSIS installer is published directly as
`CodeNomad-Tauri-windows-x64-<version>.exe`. It is also the signed
updater payload, so signed builds upload it once.
- The ZIP that wrapped the same installer is no longer published.
- The WinGet automation defaults now point to the `.exe`.

### Updater signatures
- The updater reads each signature from `latest.json`, never from a
`.sig` asset. Staging still verifies every signature and its trusted
comment and records it in the receipt, and publication still requires
every payload's uploaded digest to match.
- Releases no longer carry `.sig` assets, and staging deletes any left
on a rebuilt tag by older builds.

Rebuilt tags also delete obsolete Electron `tar.gz`, Tauri deb and
Windows ZIP assets.

### Docs
- **README:** shows the deb and AppImage commands. The AppImage uses the
static type2 runtime, so it needs no `libfuse2`;
`--appimage-extract-and-run` covers environments without FUSE. The
Wayland/NVIDIA command now targets the AppImage.
- **BUILD.md, `TAURI_UPDATER.md` and the WinGet guide** describe the new
formats.

## Migration note

Installing the Electron deb over a Tauri deb switches the app from Tauri
to Electron:
- `code-nomad` and `codenomad-tauri` are removed, and the launcher
becomes `CodeNomad`;
- server configuration is shared, but desktop window state is not
migrated.

Tauri deb users never had in-app updates, so the release notes should
say this. The AppImage is their Tauri option.

## Validation

- **Electron deb packaging (CI):** checks package, conflicts, replaces
and version metadata, the contents, the post-install `chmod 4755` step,
the resources/asar smoke checks and `ldd`.
- **Electron deb install (CI):** installs on Ubuntu 24.04 **without
recommends**, so `ldd` proves the declared `Depends` are enough. It
installs over a stand-in `code-nomad` package, then checks that the old
package was replaced, the owner of the desktop entry,
`/usr/bin/CodeNomad`, `chrome-sandbox` `root:root 4755` and shared
libraries.
- **Version mapping:** the mapping, the ordering against stable, later
releases and same-day builds, and both repack branches were checked with
dpkg 1.22.6 on Ubuntu 24.04. The repack keeps the control fields and
root ownership.
- **Dependencies:** every declared dependency resolves on Ubuntu 24.04,
either as a real package or through its t64 provider.
- **Updater scripts:** `updater-artifacts.test.mjs` passes, including
signature-in-manifest publication with no staged `.sig`.
- **Workflow:** the YAML parses.
- **Independent review:** no blocker. The major finding (missing deb
dependencies) and the minor ones (dev version ordering, stale BUILD.md
and README command, unchecked exit codes, unused updater metadata) are
addressed.

## Follow-ups outside this PR

- **WinGet:** the `WINGET_WINDOWS_ASSET_NAME_TEMPLATE` repository
variable still pins the `.zip` name and overrides the new default.
Before the next stable release:
  - update it to `.exe`, or delete it;
- check that the automated `winget-pkgs` PR declares a direct `nullsoft`
installer without the old ZIP fields, and correct it if not.

Until then, a stable release's WinGet job waits for the ZIP and fails
without side effects.
- **Ubuntu desktop trial:** test the Electron deb on a real Ubuntu
desktop.
2026-10-14 16:45:49 +02:00

7.2 KiB
Raw Permalink Blame History

Building CodeNomad Binaries

This guide explains how to build distributable binaries for CodeNomad.

Prerequisites

  • Bun - Package manager and runtime
  • Node.js 24 LTS - Use the exact release pinned in .node-version for builds and tests
  • Electron Builder - Installed via devDependencies

Bundled server runtime

Both Tauri and Electron ship a separate Node.js executable for the CodeNomad server. scripts/prepare-node-runtime.cjs reads the same .node-version pin as CI, downloads the official target archive, and verifies its SHA-256 before packaging. Only the executable is bundled, not npm or Corepack. Updating this pin does not update Electron's internal Node/Chromium, WebView2, the system Node installation, or the independently managed OpenCode daemon.

For a runtime update, run the server and desktop tests with the pinned Node, then rebuild and smoke-test the packaged applications on Windows, macOS, and Linux. Check the upstream Node platform requirements before changing the pin; Node 24 requires macOS 13.5 or later. Keep both desktop bundle minimum versions aligned. Keep the version exact rather than resolving latest or lts/* at build time so release inputs remain reproducible.

Quick Start

All commands now run inside the workspace packages. From the repo root you can target the Electron app package directly:

npm run build --workspace @neuralnomads/codenomad-electron-app

Build for Current Platform (macOS default)

bun run build:binaries

This builds for macOS (Universal - Intel + Apple Silicon) by default.

Platform-Specific Builds

macOS

# Universal (Intel + Apple Silicon) - Recommended
bun run build:mac

# Intel only (x64)
bun run build:mac-x64

# Apple Silicon only (ARM64)
bun run build:mac-arm64

Output formats: .dmg, .zip

Windows

# x64 (64-bit Intel/AMD)
bun run build:win

# ARM64 (Windows on ARM)
bun run build:win-arm64

Output formats: .exe (NSIS installer), .zip

Linux

# Electron Debian package (x64)
npm run build:linux --workspace @neuralnomads/codenomad-electron-app

# Tauri AppImage (x64)
npm exec --workspace @codenomad/tauri-app -- tauri build --bundles appimage

Release formats: Electron .deb installer and self-updating Tauri .AppImage.

Build All Platforms

bun run build:all

⚠️ Note: Cross-platform builds may have limitations. Build on the target platform for best results.

Build Process

The build script performs these steps:

  1. Build @neuralnomads/codenomad → Produces the CLI dist/ bundle (also rebuilds the UI assets it serves)
  2. Compile TypeScript + bundle with Vite → Electron main, preload, and renderer output in dist/
  3. Package with electron-builder → Platform-specific binaries

Output

Build artifacts are generated in package-specific output directories:

packages/electron-app/release/
└── CodeNomad-Electron-linux-amd64-0.18.0.deb

packages/tauri-app/target/release/bundle/appimage/
└── CodeNomad_0.18.0_amd64.AppImage

File Naming Convention

CodeNomad-Electron-{os}-{arch}-{version}.{ext}
CodeNomad-Tauri-{os}-{arch}-{version}.{ext}
  • version: From package.json (e.g., 0.18.0)
  • os: macos, windows, linux
  • arch: x64, arm64, universal
  • ext: zip, exe, app.tar.gz, deb, AppImage

Electron names deb architectures the Debian way (amd64) and Tauri keeps its native bundle filenames. CI renames both to the convention above when preparing release assets.

Platform Requirements

macOS

  • Build on: macOS 13.5+ with the required Xcode tools (CI uses macOS 15)
  • Run on: macOS 13.5+ (Intel and Apple Silicon; required by bundled Node 24)
  • Code signing: Optional (recommended for distribution)

Windows

  • Build on: Windows 10+, macOS, or Linux
  • Run on: Windows 10+
  • Code signing: Optional (recommended for distribution)

Linux

  • Build on: Linux x64
  • Electron deb: built and installation-tested on Ubuntu 24.04; its post-install step gives chrome-sandbox the root ownership and 4755 mode Chromium's sandbox requires, and it replaces the former Tauri deb (code-nomad)
  • Tauri AppImage: built and tested on Ubuntu 24.04; older distributions are not yet guaranteed

Troubleshooting

Build fails on macOS

# Install Xcode Command Line Tools
xcode-select --install

Build fails on Linux

Install the Electron and Tauri build dependencies documented by their upstream projects. Release builds currently target Linux x64 and produce an Electron Debian package plus a Tauri AppImage.

"electron-builder not found"

# Install dependencies
bun install

Build is slow

  • Use platform-specific builds instead of build:all
  • Close other applications to free up resources
  • Use SSD for faster I/O

Development vs Production

Development:

bun run dev           # Hot reload, no packaging

Production:

bun run build:binaries # Full build + packaging

CI/CD Integration

Example GitHub Actions workflow:

name: Build Binaries

on:
  push:
    tags:
      - "v*"

jobs:
  build-mac:
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v3
      - uses: oven-sh/setup-bun@v1
      - run: bun install
      - run: bun run build:mac

  build-win:
    runs-on: windows-latest
    steps:
      - uses: actions/checkout@v3
      - uses: oven-sh/setup-bun@v1
      - run: bun install
      - run: bun run build:win

  build-linux:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: oven-sh/setup-bun@v1
      - run: bun install
      - run: bun run build:linux

Advanced Configuration

Edit package.json → build section to customize:

  • App icon
  • Code signing
  • Installer options
  • File associations
  • Auto-update settings

See electron-builder docs for details.

Brand Assets

  • images/CodeNomad-Icon.png — primary asset for in-app logo placements and the 1024×1024 master icon used to generate packaged app icons

To update the binaries:

  1. Run node scripts/generate-icons.js images/CodeNomad-Icon.png electron/resources to round the corners and emit fresh icon.icns, icon.ico, and icon.png files.
  2. (Optional) Pass --radius to tweak the corner curvature or --name to change the filename prefix.
  3. If you prefer manual control, export images/CodeNomad-Icon.png with your tool of choice and place the generated files in electron/resources/.

Clean Build

Remove previous builds:

rm -rf release/ dist/
bun run build:binaries

FAQ

Q: Can I build for Windows on macOS?
A: Yes, but native binaries (e.g., DMG) require the target OS.

Q: How large are the binaries?
A: Approximately 100-150 MB (includes Electron runtime).

Q: Do I need code signing?
A: Not required, but recommended for public distribution to avoid security warnings.

Q: How do I update the version?
A: Update version in package.json, then rebuild.

Support

For issues or questions: