Skip to main content

Overview

MQTT Explorer uses electron-builder to create platform-specific installers and packages. The packaging system supports multiple formats per platform and handles code signing, notarization, and store submissions.

Package Configuration

Packaging is configured in package.json under the build key and orchestrated by package.ts:
package.json
{
  "build": {
    "appId": "mqtt-explorer",
    "productName": "MQTT Explorer",
    "directories": {
      "app": "./",
      "buildResources": "res",
      "output": "build"
    },
    "afterPack": "./dist/scripts/afterPack.js",
    "afterSign": "./dist/scripts/notarize.js"
  }
}

Build Preparation

Before packaging, create a clean build environment:
1

Prepare release build

yarn prepare-release
This script:
  1. Creates a fresh clone in build/clean/
  2. Installs dependencies with frozen lockfile
  3. Builds the application
  4. Installs only production dependencies
  5. Removes app/node_modules to reduce package size
2

Verify build output

ls -la build/clean/dist/
Should contain compiled TypeScript and bundled app.
The prepare-release script ensures a clean, reproducible build by cloning from the local git repository.

Platform Packaging

The package.ts script handles multi-format packaging for each platform.

Windows Packages

Create Windows installers:
yarn package win
Format: .exe portable applicationArchitecture: x64Configuration:
const winPortable: builder.CliOptions = {
  x64: true,
  ia32: false,
  projectDir: './build/clean',
  publish: 'always',
}
No installation required - runs directly.
Format: .exe installerArchitecture: x64Features:
  • Desktop shortcut creation
  • Start menu integration
  • Uninstaller included
  • Auto-update support

macOS Packages

Create macOS disk images:
yarn package mac
Format: .dmg disk imageArchitectures:
  • x64 (Intel)
  • arm64 (Apple Silicon)
Configuration:
const mac: builder.CliOptions = {
  x64: true,
  arm64: true,
  projectDir: './build/clean',
  publish: 'always',
}
Entitlements:
  • res/entitlements.mac.plist - Parent app
  • res/entitlements.mac.inherit.plist - Child processes
Code Signing: Automatic with environment variables setNotarization: Required for macOS 10.15+
Format: .pkg for App Store submissionEntitlements: res/entitlements.mas.plistSandboxing: Full App Store sandbox restrictionsProvisioning Profile: res/MQTT_Explorer_Store_Distribution_Profile.provisionprofile
MAS builds require a Distribution certificate and provisioning profile from Apple Developer.

Linux Packages

Create Linux packages:
yarn package linux
Format: .AppImage portable applicationArchitectures:
  • x64
  • armv7l (32-bit ARM)
  • arm64 (64-bit ARM)
Configuration:
const linuxAppImage: builder.CliOptions = {
  x64: true,
  armv7l: true,
  arm64: true,
  projectDir: './build/clean',
  publish: 'always',
}
Features:
  • Single file, no installation
  • Runs with --no-sandbox flag (see afterPack.ts)
  • Desktop integration on first run
Format: .snap packageArchitecture: x64 onlyStore: Published to Snapcraft StoreConfiguration:
"snap": {
  "environment": {
    "DISABLE_WAYLAND": "1"
  }
}
Snap builds for ARM architectures are not supported on x64 build machines.
Format: .deb packageArchitectures: x64, armv7l, arm64Package Metadata:
"linux": {
  "category": "Development",
  "maintainer": "Thomas Nordquist"
}
Compatible with: Debian, Ubuntu, Linux Mint, Pop!_OS

Windows Store Package

Create Microsoft Store package:
yarn package appx
Format: .appx application package Configuration:
"appx": {
  "applicationId": "mqttexplorer",
  "identityName": "51031thomas.nordquist.MQTT-Explorer",
  "publisherDisplayName": "Thomas Nordquist",
  "publisher": "CN=0A6DE643-4AA2-4FF2-9729-6935C9ED8C13",
  "backgroundColor": "transparent",
  "showNameOnTiles": true
}
APPX builds require a valid Windows Store developer certificate.

Docker Packaging

Browser mode can be packaged as a Docker container.

Multi-Platform Docker Build

The GitHub Actions workflow builds for multiple architectures:
# Build locally for your platform
docker build -f Dockerfile.browser -t mqtt-explorer:local .

# Build multi-platform with buildx
docker buildx build \
  --platform linux/amd64,linux/arm64,linux/arm/v7 \
  -f Dockerfile.browser \
  -t mqtt-explorer:latest \
  --push .

Docker Image Features

  • Base: Alpine Linux with Node.js 24
  • Size: ~200MB final image
  • User: Non-root (UID 1001)
  • Health Check: Built-in endpoint
  • Signal Handling: dumb-init for proper process management
  • Persistent Data: /app/data volume

Post-Build Scripts

afterPack Hook

Runs after each platform package is created (scripts/afterPack.ts):
// For Linux AppImage builds
export default async function (context: Context) {
  const isLinux = context.targets.find(target => target.name === 'appImage')
  if (!isLinux) return
  
  // Wrap binary to add --no-sandbox flag
  await exec('mv', ['mqtt-explorer', 'mqtt-explorer.bin'])
  const wrapperScript = `#!/bin/bash
    "\${BASH_SOURCE%/*}"/mqtt-explorer.bin "$@" --no-sandbox
  `
  fs.writeFileSync('mqtt-explorer', wrapperScript)
  await exec('chmod', ['+x', 'mqtt-explorer'])
}

afterSign Hook

Runs after macOS code signing (scripts/notarize.ts):
export default async function notarizing(context: Context) {
  if (context.electronPlatformName !== 'darwin') return
  
  const { appleId, appleIdPassword, teamId } = process.env
  if (!appleId || !appleIdPassword || !teamId) {
    console.warn('Skipping notarization: credentials not set')
    return
  }
  
  await notarize({
    appPath,
    appleId,
    appleIdPassword,
    teamId,
  })
}
See Notarization for complete setup.

Package Output

Packages are created in the build/ directory:
build/
├── MQTT Explorer-0.4.0-beta.5.dmg              # macOS
├── MQTT Explorer-0.4.0-beta.5-arm64.dmg        # macOS Apple Silicon
├── MQTT Explorer Setup 0.4.0-beta.5.exe        # Windows NSIS
├── MQTT Explorer 0.4.0-beta.5.exe              # Windows Portable
├── mqtt-explorer_0.4.0-beta.5_amd64.deb        # Debian x64
├── mqtt-explorer_0.4.0-beta.5_armhf.deb        # Debian ARM
├── mqtt-explorer_0.4.0-beta.5_arm64.deb        # Debian ARM64
├── MQTT-Explorer-0.4.0-beta.5.AppImage         # Linux x64
├── MQTT-Explorer-0.4.0-beta.5-armv7l.AppImage  # Linux ARM
├── MQTT-Explorer-0.4.0-beta.5-arm64.AppImage   # Linux ARM64
└── mqtt-explorer_0.4.0-beta.5_amd64.snap       # Snap package

Publishing Packages

Packages are automatically published when built in CI/CD:
const config: builder.CliOptions = {
  publish: 'always',  // Publish to GitHub Releases
  // or
  publish: 'onTag',   // Only publish on git tags
}
GitHub Releases: Requires GH_TOKEN environment variable Snapcraft Store: Requires SNAPCRAFT_STORE_CREDENTIALS secret

Testing Packages Locally

Before CI/CD, test packages locally:
1

Build for your platform

yarn build
yarn prepare-release

# macOS
yarn package mac

# Linux
yarn package linux

# Windows
yarn package win
2

Install and test

Locate the package in build/ and install:
# macOS
open "build/MQTT Explorer-*.dmg"

# Linux AppImage
chmod +x build/*.AppImage
./build/*.AppImage

# Linux Debian
sudo dpkg -i build/*.deb

# Windows
.\build\MQTT-Explorer-Setup-*.exe
3

Verify functionality

  • Application launches without errors
  • Can connect to MQTT broker
  • Auto-update mechanism works (if applicable)
  • File associations work (if applicable)

Troubleshooting

Package Build Fails

yarn install --frozen-lockfile
yarn prepare-release
Verify certificates are installed:
security find-identity -v -p codesigning
Check environment variables:
echo $APPLE_ID
echo $APPLE_TEAM_ID
snapcraft login
snapcraft export-login snapcraft.login
# Upload snapcraft.login contents as GitHub secret
Clean build artifacts:
rm -rf build/clean
rm -rf build/*.dmg build/*.exe build/*.AppImage

Package Won’t Install

macOS: “App is damaged and can’t be opened”
  • Notarization failed or wasn’t performed
  • See Notarization
Linux: Permission denied
chmod +x *.AppImage
Windows: SmartScreen warning
  • Expected for unsigned or new publishers
  • Code signing certificate reduces warnings

Next Steps

Releases

Automate the release process with semantic versioning

Notarization

Set up macOS notarization for secure distribution

Build docs developers (and LLMs) love