Milady Electrobun Build

Use when building an Electrobun app for distribution, setting up code signing, understanding platform-specific build requirements, or diagnosing build failures. Covers dev/canary/stable environments, all three platforms, toolchain prerequisites, artifact output, and CI/CD setup.

install
source · Clone the upstream repo
git clone https://github.com/milady-ai/milady
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/milady-ai/milady "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.claude/plugins/electrobun-dev/skills/electrobun-build" ~/.claude/skills/milady-ai-milady-electrobun-build && rm -rf "$T"
manifest: .claude/plugins/electrobun-dev/skills/electrobun-build/SKILL.md
source content

Electrobun Build

Builds a distributable app bundle and installer for the current platform using

electrobun build
.

Build Environments

EnvironmentCommandCodesignUpdatesPatch gen
dev
electrobun dev
NoDisabledNo
canary
electrobun build --env=canary
Yes (if configured)EnabledYes
stable
electrobun build --env=stable
Yes (if configured)EnabledYes

ELECTROBUN_BUILD_ENV
is set automatically by the CLI and passed to
postBuild
scripts — you do not set it manually.


Platform Prerequisites

macOS (Intel + Apple Silicon)

xcode-select --install   # Xcode Command Line Tools
brew install cmake

Produces:

.dmg
installer +
.app.tar.zst
update tarball Toolchain:
clang++
+
make
+
install_name_tool
(all from Xcode CLT) Architectures:
arm64
(Apple Silicon),
x64
(Intel) — build runs on matching host

Windows

  • Visual Studio 2022 with component
    Microsoft.VisualStudio.Component.VC.Tools.x86.x64
  • cmake (available via VS installer or standalone)

The CLI uses

vswhere.exe
to find VS and
vcvarsall.bat
to configure the environment automatically.

Produces:

.exe
self-extracting installer (in a
.zip
) +
.tar.zst
update tarball Toolchain:
cl.exe
+
link.exe
via MSVC Architecture:
x64
only

Linux

sudo apt-get install -y \
  build-essential \
  cmake \
  pkg-config \
  libgtk-3-dev \
  libwebkit2gtk-4.1-dev \
  libayatana-appindicator3-dev \
  librsvg2-dev \
  fuse \
  libfuse2

libfuse2
is required for AppImage creation.

Produces:

.AppImage
+
.tar.zst
update tarball Toolchain:
g++
+
make
+
pkg-config
Architectures:
x64
,
arm64


electrobun.config.ts — Build Options

General (
build.*
)

build: {
  bun: {
    entrypoint: "src/bun/index.ts",  // default
    // + any Bun.build() options (splitting, minify, target, etc.)
  },

  views: {
    // Each key becomes the view's URL scheme: mainview://index.html
    mainview: {
      entrypoint: "src/mainview/index.ts",
      // + any Bun.build() options per view
    },
  },

  copy: {
    // Record<sourcePath, destPathInBuildOutput>
    "src/assets/icon.png": "resources/icon.png",
  },

  buildFolder:    "build",      // default: "build"
  artifactFolder: "artifacts",  // default: "artifacts"
  targets:        "current",    // default: build for host platform

  useAsar:    false,            // default: false — pack assets into ASAR archive
  asarUnpack: ["*.node", "*.dll", "*.dylib", "*.so"],  // always unpacked from ASAR

  cefVersion:  undefined,  // override bundled CEF version
  bunVersion:  undefined,  // override bundled Bun runtime version
  wgpuVersion: undefined,  // override latest electrobun-dawn release

  locales: "*",  // ICU locales to include ("*" = all); Linux/Windows only
}

macOS (
build.mac
)

build: {
  mac: {
    bundleCEF:       false,      // bundle CEF instead of WKWebView (~120MB)
    bundleWGPU:      false,      // bundle Dawn for native GPU rendering
    defaultRenderer: "native",   // "native" | "cef"
    codesign:        false,      // enable Apple code signing
    notarize:        false,      // enable notarization (requires codesign: true)
    icons:           undefined,  // path to .iconset folder or .icns file
    chromiumFlags:   {},         // Record<string, string|true> — CEF-only flags
    entitlements:    undefined,  // path to custom entitlements.plist
  }
}

Windows (
build.win
)

build: {
  win: {
    bundleCEF:     false,     // CEF is always needed on Windows (no OS webview)
    bundleWGPU:    false,     // bundle Dawn
    icons:         undefined, // path to .ico file
    chromiumFlags: {},        // CEF Chromium flags
  }
}

Linux (
build.linux
)

build: {
  linux: {
    bundleCEF:     false,     // bundle CEF instead of GTKWebKit
    bundleWGPU:    false,     // bundle Dawn
    icons:         undefined, // icon file path
    chromiumFlags: {},        // CEF Chromium flags
  }
}

Linux multi-view note: GTKWebKit is the default system webview. For apps with multiple views or needing consistent rendering,

bundleCEF: true
is strongly recommended.


Code Signing (macOS)

Code signing runs automatically when

build.mac.codesign: true
and
ELECTROBUN_DEVELOPER_ID
is set. It only runs when
buildEnvironment !== "dev"
, host OS is macOS, and target OS is macOS.

Signing Order

  1. CEF framework internals (if
    bundleCEF: true
    )
  2. CEF helper applications
  3. All
    .dylib
    files and executables inside
    MacOS/
    recursively
  4. The
    launcher
    executable
  5. The entire
    .app
    bundle
  6. The
    .dmg
    installer

Required Environment Variables

export ELECTROBUN_DEVELOPER_ID="Developer ID Application: Your Name (TEAMID)"

# Notarization (requires codesign: true)
export ELECTROBUN_APPLEID="you@example.com"
export ELECTROBUN_APPLEIDPASS="xxxx-xxxx-xxxx-xxxx"  # app-specific password
export ELECTROBUN_TEAMID="ABCDE12345"

Notarization Sequence

  1. .app
    bundle zipped
  2. xcrun notarytool submit --wait
    — blocks until Apple responds
  3. On success:
    xcrun stapler staple
    attaches ticket to bundle
  4. .dmg
    created, then also code-signed and notarized

Artifact Output

All artifacts land in

artifacts/
(or
build.artifactFolder
).

Naming Convention:
{channel}-{os}-{arch}-{filename}

PlatformInstallerUpdate TarballManifest
macOS stable arm64
MyApp.dmg
stable-macos-arm64-MyApp.app.tar.zst
stable-macos-arm64-update.json
macOS canary arm64
MyApp-canary.dmg
canary-macos-arm64-MyApp-canary.app.tar.zst
canary-macos-arm64-update.json
macOS stable x64
MyApp.dmg
stable-macos-x64-MyApp.app.tar.zst
stable-macos-x64-update.json
Windows stable x64
MyApp-Setup.zip
stable-win-x64-MyApp.tar.zst
stable-win-x64-update.json
Windows canary x64
MyApp-Setup-canary.zip
canary-win-x64-MyApp-canary.tar.zst
canary-win-x64-update.json
Linux stable x64
MyApp-Setup.AppImage
stable-linux-x64-MyApp.tar.zst
stable-linux-x64-update.json
Linux canary arm64
MyApp-Setup-canary.AppImage
canary-linux-arm64-MyApp-canary.tar.zst
canary-linux-arm64-update.json

Patch files:

{channel}-{os}-{arch}-{fromHash}.patch
(generated if previous tarball available)

update.json
Contents

{
  "version":  "1.0.0",
  "hash":     "<sha256-of-uncompressed-tarball>",
  "platform": "macos",
  "arch":     "arm64"
}

version.json
Inside the App Bundle

macOS:   MyApp.app/Contents/Resources/version.json
Windows: MyApp/Resources/version.json
Linux:   MyApp/Resources/version.json
{
  "version":    "1.0.0",
  "hash":       "<content-hash>",
  "channel":    "stable",
  "baseUrl":    "https://updates.example.com/",
  "name":       "MyApp",
  "identifier": "com.example.myapp"
}

Environment Variables Reference

Set by Electrobun CLI — passed to
postBuild
scripts

VariableExample
ELECTROBUN_BUILD_ENV
"dev"
/
"canary"
/
"stable"
ELECTROBUN_OS
"macos"
/
"win"
/
"linux"
ELECTROBUN_ARCH
"arm64"
/
"x64"
ELECTROBUN_BUILD_DIR
/abs/path/to/build/
ELECTROBUN_APP_NAME
"MyApp"
ELECTROBUN_APP_VERSION
"1.0.0"
ELECTROBUN_APP_IDENTIFIER
"com.example.myapp"
ELECTROBUN_ARTIFACT_DIR
/abs/path/to/artifacts/

Set by you — version and runtime overrides

VariablePurpose
ELECTROBUN_CEF_VERSION
Override CEF version without editing config
ELECTROBUN_BUN_VERSION
Override Bun runtime version
ELECTROBUN_CONSOLE=1
Force console output on Windows in production

ELECTROBUN_SKIP_CODESIGN
does not exist. Code signing is controlled by
config.build.mac.codesign
and
buildEnvironment
.


GitHub Actions CI/CD Matrix

Official runners used by Electrobun's own release workflow:

strategy:
  matrix:
    include:
      - runner: macos-14           # Apple Silicon
        arch: arm64
      - runner: macos-15-intel     # Intel
        arch: x64
      - runner: ubuntu-24.04       # Linux x64
        arch: x64
      - runner: ubuntu-24.04-arm   # Linux ARM64
        arch: arm64
      - runner: windows-2025       # Windows x64
        arch: x64

Required GitHub Secrets

SecretPurpose
MACOS_CERTIFICATE
Base64-encoded .p12 certificate
MACOS_CERTIFICATE_PWD
Certificate password
ELECTROBUN_DEVELOPER_ID
Apple Developer ID string
ELECTROBUN_APPLEID
Apple ID email
ELECTROBUN_APPLEIDPASS
App-specific password
ELECTROBUN_TEAMID
Apple Team ID
R2_ENDPOINT
Cloudflare R2 endpoint URL
R2_ACCESS_KEY_ID
R2 access key
R2_SECRET_ACCESS_KEY
R2 secret key
R2_BUCKET
R2 bucket name
NODE_AUTH_TOKEN
npm publish token

macOS Certificate Import Step

- name: Install Apple Certificate
  run: |
    echo $MACOS_CERTIFICATE | base64 --decode > certificate.p12
    security create-keychain -p "" build.keychain
    security import certificate.p12 -k build.keychain \
      -P $MACOS_CERTIFICATE_PWD -T /usr/bin/codesign
    security list-keychains -d user -s build.keychain
    security set-keychain-settings -t 3600 -u build.keychain
    security unlock-keychain -p "" build.keychain

Common Build Failures

ErrorCauseFix
View URL not foundConfig key doesn't match
src/
dir name
views: { mainview: ... }
→ URL must be
mainview://index.html
Blank windowScript tag points to
.ts
not compiled
.js
Use
index.js
in HTML
Codesign fails locallyCertificate not in keychainRun
security find-identity -v -p codesigning
Notarization failsHardened runtime without JIT entitlementAdd
com.apple.security.cs.allow-jit
to entitlements.plist
Linux AppImage won't runFUSE missing
apt-get install fuse libfuse2
Windows MSVC not foundVS component missingInstall
VC.Tools.x86.x64
component via VS Installer
CEF build failscmake not installedInstall cmake for the platform
libwebgpu_dawn.dylib
not found
bundleWGPU
not set
Set
build.mac.bundleWGPU: true
in config
WGPU GC crash after framesFFI objects not pinnedPush all GPU objects to
KEEPALIVE
array