electrobun-distribution
10
总安装量
10
周安装量
#29113
全站排名
安装命令
npx skills add https://github.com/rajavijayach/electrobun-skills --skill electrobun-distribution
Agent 安装分布
opencode
10
gemini-cli
10
github-copilot
10
codex
10
amp
10
kimi-cli
10
Skill 文档
Electrobun Distribution
Complete guide to packaging, signing, and distributing Electrobun applications.
Production Build
Basic Build Configuration
electrobun.config.ts:
import { defineConfig } from "electrobun";
export default defineConfig({
app: {
name: "My Application",
version: "1.0.0",
identifier: "com.mycompany.myapp",
description: "My desktop application",
author: "My Company",
},
build: {
main: "src/bun/main.ts",
views: {
mainview: "src/views/mainview/index.ts",
settings: "src/views/settings/index.ts",
},
output: "dist",
},
icons: {
mac: "assets/icon.icns", // macOS: 1024x1024 .icns
win: "assets/icon.ico", // Windows: .ico with multiple sizes
linux: "assets/icon.png", // Linux: 512x512 .png
},
updates: {
provider: "generic",
url: "https://updates.myapp.com",
},
});
Build Commands
# Development build
bun run dev
# Production build
bun run build
# Build for specific platform
bun run build --platform=mac
bun run build --platform=win
bun run build --platform=linux
# Build for all platforms
bun run build --all
Code Signing
macOS Code Signing
Setup Certificates
# List available certificates
security find-identity -v -p codesigning
# Import Developer ID certificate
# Get certificate from Apple Developer Portal
# Double-click .p12 file or:
security import cert.p12 -k ~/Library/Keychains/login.keychain
Sign Application
electrobun.config.ts:
export default defineConfig({
// ...
mac: {
identity: "Developer ID Application: Your Name (TEAM_ID)",
entitlements: "entitlements.mac.plist",
entitlementsInherit: "entitlements.mac.plist",
hardenedRuntime: true,
gatekeeperAssess: false,
},
});
entitlements.mac.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- Allow JIT for Bun runtime -->
<key>com.apple.security.cs.allow-jit</key>
<true/>
<!-- Allow unsigned executable memory -->
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<!-- Disable library validation -->
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<!-- Network client access -->
<key>com.apple.security.network.client</key>
<true/>
<!-- Optional: Network server -->
<key>com.apple.security.network.server</key>
<true/>
<!-- Optional: Camera access -->
<key>com.apple.security.device.camera</key>
<true/>
<!-- Optional: Microphone access -->
<key>com.apple.security.device.audio-input</key>
<true/>
</dict>
</plist>
Apple Notarization
# Notarize the app
xcrun notarytool submit dist/MyApp.dmg \
--apple-id "your@email.com" \
--team-id "TEAM_ID" \
--password "app-specific-password" \
--wait
# Check notarization status
xcrun notarytool log <submission-id> \
--apple-id "your@email.com" \
--team-id "TEAM_ID" \
--password "app-specific-password"
# Staple notarization to DMG
xcrun stapler staple dist/MyApp.dmg
# Verify stapling
xcrun stapler validate dist/MyApp.dmg
Automated notarization:
// In electrobun.config.ts
export default defineConfig({
// ...
mac: {
identity: "Developer ID Application: Your Name (TEAM_ID)",
notarize: {
teamId: process.env.APPLE_TEAM_ID,
appleId: process.env.APPLE_ID,
appleIdPassword: process.env.APPLE_APP_PASSWORD,
},
},
});
Windows Code Signing
Get Certificate
# Import certificate
certutil -importpfx cert.pfx
# Or specify password
certutil -f -p PASSWORD -importpfx cert.pfx
Sign Application
electrobun.config.ts:
export default defineConfig({
// ...
win: {
certificateFile: "cert.pfx",
certificatePassword: process.env.WIN_CERT_PASSWORD,
signWithParams: "/tr http://timestamp.digicert.com /td sha256 /fd sha256",
},
});
Manual signing:
# Sign executable
signtool sign /f cert.pfx /p PASSWORD /tr http://timestamp.digicert.com /td sha256 /fd sha256 MyApp.exe
# Verify signature
signtool verify /pa MyApp.exe
Auto-Updater
Update Server Setup
updates.json:
{
"version": "1.0.1",
"releaseDate": "2026-02-21T00:00:00Z",
"platforms": {
"darwin": {
"url": "https://updates.myapp.com/MyApp-1.0.1-mac.zip",
"signature": "MC0CFQCf...",
"size": 15728640
},
"win32": {
"url": "https://updates.myapp.com/MyApp-1.0.1-win.exe",
"signature": "30820...",
"size": 12582912
},
"linux": {
"url": "https://updates.myapp.com/MyApp-1.0.1-linux.AppImage",
"signature": "30440...",
"size": 18874368
}
},
"releaseNotes": "Bug fixes and performance improvements"
}
Client Implementation
src/bun/main.ts:
import { Updater, dialog } from "electrobun/bun";
class UpdateManager {
private updater: Updater;
private updateAvailable = false;
constructor() {
this.updater = new Updater({
url: "https://updates.myapp.com/updates.json",
autoCheck: true,
interval: 4 * 60 * 60 * 1000, // Check every 4 hours
});
this.setupHandlers();
}
private setupHandlers() {
this.updater.on("update-available", async (info) => {
this.updateAvailable = true;
const result = await dialog.showMessageBox({
type: "info",
title: "Update Available",
message: `Version ${info.version} is available`,
detail: info.releaseNotes,
buttons: ["Download Now", "Later"],
defaultId: 0,
});
if (result.response === 0) {
this.updater.downloadAndInstall();
}
});
this.updater.on("update-not-available", () => {
console.log("App is up to date");
});
this.updater.on("download-progress", (progress) => {
console.log(`Download progress: ${progress.percent}%`);
// Update UI with progress
mainWindow.rpc.updateDownloadProgress(progress);
});
this.updater.on("update-downloaded", async () => {
const result = await dialog.showMessageBox({
type: "info",
title: "Update Ready",
message: "Update has been downloaded",
detail: "The app will restart to apply the update",
buttons: ["Restart Now", "Later"],
defaultId: 0,
});
if (result.response === 0) {
this.updater.quitAndInstall();
}
});
this.updater.on("error", (error) => {
console.error("Update error:", error);
dialog.showMessageBox({
type: "error",
title: "Update Error",
message: "Failed to check for updates",
detail: error.message,
});
});
}
checkForUpdates() {
this.updater.checkForUpdates();
}
isUpdateAvailable() {
return this.updateAvailable;
}
}
const updateManager = new UpdateManager();
// Expose to window RPC
win.defineRpc({
handlers: {
checkForUpdates: async () => {
updateManager.checkForUpdates();
},
isUpdateAvailable: async () => {
return updateManager.isUpdateAvailable();
}
}
});
Delta Updates
Electrobun uses bsdiff for tiny delta updates (~14KB):
// Automatically handled by Updater
// Just ensure update server provides:
// - Full package for new installs
// - Delta patches for updates
// Update server structure:
// /updates.json
// /v1.0.0/MyApp-mac.zip (full)
// /v1.0.1/MyApp-mac.zip (full)
// /v1.0.1/delta-1.0.0-to-1.0.1-mac.patch (delta)
Creating Installers
macOS DMG
# Create DMG with background and app arrangement
create-dmg dist/MyApp.app dist/ \
--overwrite \
--window-size 660 400 \
--icon-size 160 \
--icon "MyApp.app" 180 170 \
--hide-extension "MyApp.app" \
--app-drop-link 480 170 \
--background "installer-background.png"
# Result: dist/MyApp-1.0.0.dmg
Windows Installer
Using NSIS:
; installer.nsi
!include "MUI2.nsh"
Name "My Application"
OutFile "MyApp-Setup.exe"
InstallDir "$PROGRAMFILES\My Application"
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_LANGUAGE "English"
Section "Install"
SetOutPath "$INSTDIR"
File /r "dist\*.*"
CreateDirectory "$SMPROGRAMS\My Application"
CreateShortcut "$SMPROGRAMS\My Application\My Application.lnk" "$INSTDIR\MyApp.exe"
CreateShortcut "$DESKTOP\My Application.lnk" "$INSTDIR\MyApp.exe"
WriteUninstaller "$INSTDIR\Uninstall.exe"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\MyApp" "DisplayName" "My Application"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\MyApp" "UninstallString" "$INSTDIR\Uninstall.exe"
SectionEnd
Section "Uninstall"
Delete "$INSTDIR\*.*"
RMDir /r "$INSTDIR"
Delete "$SMPROGRAMS\My Application\*.*"
RMDir "$SMPROGRAMS\My Application"
Delete "$DESKTOP\My Application.lnk"
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\MyApp"
SectionEnd
Linux Packages
AppImage (recommended):
# Create AppImage
appimagetool dist/MyApp-linux-x64 dist/MyApp.AppImage
# Make executable
chmod +x dist/MyApp.AppImage
Debian package:
# Create .deb structure
mkdir -p myapp_1.0.0/DEBIAN
mkdir -p myapp_1.0.0/usr/bin
mkdir -p myapp_1.0.0/usr/share/applications
mkdir -p myapp_1.0.0/usr/share/icons/hicolor/512x512/apps
# Copy files
cp dist/MyApp myapp_1.0.0/usr/bin/
cp myapp.desktop myapp_1.0.0/usr/share/applications/
cp icon.png myapp_1.0.0/usr/share/icons/hicolor/512x512/apps/
# Create control file
cat > myapp_1.0.0/DEBIAN/control << EOF
Package: myapp
Version: 1.0.0
Section: utils
Priority: optional
Architecture: amd64
Maintainer: Your Name <you@example.com>
Description: My Application
A desktop application built with Electrobun
EOF
# Build .deb
dpkg-deb --build myapp_1.0.0
CI/CD Integration
GitHub Actions
.github/workflows/build.yml:
name: Build and Release
on:
push:
tags:
- 'v*'
jobs:
build-mac:
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- name: Setup Bun
uses: oven-sh/setup-bun@v1
- name: Install dependencies
run: bun install
- name: Import signing certificate
env:
CERTIFICATE_BASE64: ${{ secrets.MAC_CERTIFICATE }}
CERTIFICATE_PASSWORD: ${{ secrets.MAC_CERT_PASSWORD }}
run: |
echo $CERTIFICATE_BASE64 | base64 --decode > certificate.p12
security import certificate.p12 -P $CERTIFICATE_PASSWORD
- name: Build app
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
run: bun run build --platform=mac
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: mac-build
path: dist/*.dmg
build-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- name: Setup Bun
uses: oven-sh/setup-bun@v1
- name: Install dependencies
run: bun install
- name: Build app
env:
WIN_CERT_PASSWORD: ${{ secrets.WIN_CERT_PASSWORD }}
run: bun run build --platform=win
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: win-build
path: dist/*.exe
release:
needs: [build-mac, build-windows]
runs-on: ubuntu-latest
steps:
- name: Download artifacts
uses: actions/download-artifact@v3
- name: Create Release
uses: softprops/action-gh-release@v1
with:
files: |
mac-build/*
win-build/*
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Best Practices
Version Management
// Use semantic versioning
const version = "1.2.3"; // MAJOR.MINOR.PATCH
// Update package.json and electrobun.config.ts together
// Automate with script:
import { writeFileSync, readFileSync } from "fs";
function updateVersion(newVersion: string) {
// Update package.json
const pkg = JSON.parse(readFileSync("package.json", "utf-8"));
pkg.version = newVersion;
writeFileSync("package.json", JSON.stringify(pkg, null, 2));
// Update config
const config = readFileSync("electrobun.config.ts", "utf-8");
const updated = config.replace(
/version:\s*"[^"]+"/,
`version: "${newVersion}"`
);
writeFileSync("electrobun.config.ts", updated);
}
Testing Before Release
// Pre-release checklist
const checklist = [
"All tests passing",
"No console errors",
"Auto-updater tested",
"Code signed and notarized",
"Installer tested on clean system",
"Release notes written",
"Documentation updated",
];
Resources
For more on Electrobun:
- Core skill:
electrobun– Basic build setup - Debugging:
electrobun-debugging– Build troubleshooting - Auto-updater docs: https://blackboard.sh/electrobun/docs/guides/auto-updates