Electronアプリのパッケージングと配布方法
Electronアプリのパッケージングと配布方法
はじめに
Electronアプリケーションの開発が完了したら、次はそれをユーザーに届ける段階です。しかし、開発環境で動作するアプリケーションをそのまま配布することはできません。各オペレーティングシステムに適した形式にパッケージングし、インストーラーを作成する必要があります。
本記事では、Electronアプリケーションを Windows、macOS、Linux の各プラットフォーム向けにパッケージングする方法から、自動アップデート機能の実装、さらには配布戦略まで、初学者の方でも理解できるように詳しく解説します。単にツールの使い方を説明するだけでなく、なぜそのような手順が必要なのか、どのような選択肢があるのかも含めて説明していきます。
パッケージングの基礎知識
なぜパッケージングが必要なのか
開発中のElectronアプリケーションは、以下のような構成になっています:
my-app/
├── node_modules/ # 数千のファイル
├── src/ # ソースコード
├── package.json
├── main.js
└── その他の開発ファイル
この状態では、以下の問題があります:
配布サイズが巨大: node_modulesフォルダには開発用の依存関係も含まれており、数百MBから1GB以上になることもあります。
実行が複雑: ユーザーはNode.jsをインストールし、コマンドラインからnpm startを実行する必要があります。一般的なユーザーには難しすぎます。
セキュリティリスク: ソースコードが完全に公開されており、APIキーなどの機密情報が含まれている可能性があります。
プラットフォーム非対応: 各OSの標準的なアプリケーション形式(.exe、.app、.deb など)になっていません。
パッケージングはこれらの問題を解決し、ユーザーが簡単にインストール・実行できる形式にアプリケーションを変換します。
パッケージング後の構成
パッケージング後のアプリケーションは、以下のような構成になります:
| プラットフォーム | 形式 | 特徴 |
|---|---|---|
| Windows | .exe(実行ファイル) .msi(インストーラー) |
ダブルクリックで実行可能 Program Filesにインストール |
| macOS | .app(アプリケーションバンドル) .dmg(ディスクイメージ) |
Applicationsフォルダにドラッグ&ドロップ コード署名が必要 |
| Linux | .deb(Debian/Ubuntu) .rpm(RedHat/Fedora) .AppImage(汎用) |
パッケージマネージャーでインストール 依存関係の管理 |
主要なパッケージングツール
Electron Forge
Electron Forgeは、Electronの公式が推奨するオールインワンツールです。プロジェクトの作成からパッケージング、配布まで、一貫したワークフローを提供します。
特徴:
- 設定が簡単で初心者に優しい
- テンプレートが豊富
- 自動アップデート機能のサポート
- TypeScript、Webpack との統合
インストールと初期設定:
# 既存プロジェクトに追加
npm install --save-dev @electron-forge/cli
npx electron-forge import
# 新規プロジェクトの作成
npm init electron-app@latest my-app
Electron Builder
Electron Builderは、より高度な設定が可能な強力なパッケージングツールです。大規模なプロジェクトや複雑な要件がある場合に適しています。
特徴:
- 詳細なカスタマイズが可能
- 多様な配布形式のサポート
- 自動アップデート機能の組み込み
- CI/CD との統合が容易
インストール:
npm install --save-dev electron-builder
ツールの比較と選択
| 項目 | Electron Forge | Electron Builder |
|---|---|---|
| 学習曲線 | 緩やか | やや急 |
| 設定の複雑さ | シンプル | 詳細設定可能 |
| カスタマイズ性 | 標準的 | 高い |
| ドキュメント | 良好 | 非常に充実 |
| コミュニティ | 公式サポート | 大規模 |
| 適用プロジェクト | 小〜中規模 | 中〜大規模 |
初心者の方やシンプルなアプリケーションには Electron Forge を、より細かい制御が必要な場合は Electron Builder をおすすめします。
Electron Forgeを使用したパッケージング
基本的な設定
Electron Forgeを使用したパッケージングの設定を見ていきましょう。
// package.json
{
"name": "my-electron-app",
"version": "1.0.0",
"description": "My Electron Application",
"main": "src/main.js",
"scripts": {
"start": "electron-forge start",
"package": "electron-forge package",
"make": "electron-forge make",
"publish": "electron-forge publish"
},
"devDependencies": {
"@electron-forge/cli": "^6.0.0",
"@electron-forge/maker-deb": "^6.0.0",
"@electron-forge/maker-rpm": "^6.0.0",
"@electron-forge/maker-squirrel": "^6.0.0",
"@electron-forge/maker-zip": "^6.0.0",
"electron": "^28.0.0"
}
}
// forge.config.js
module.exports = {
packagerConfig: {
asar: true,
icon: './assets/icon', // 拡張子なし(自動判定)
appBundleId: 'com.yourcompany.yourapp',
appCategoryType: 'public.app-category.productivity',
win32metadata: {
CompanyName: 'Your Company',
FileDescription: 'Your App Description',
OriginalFilename: 'YourApp.exe',
ProductName: 'Your App',
InternalName: 'YourApp'
}
},
rebuildConfig: {},
makers: [
{
name: '@electron-forge/maker-squirrel',
config: {
name: 'my_electron_app',
authors: 'Your Name',
description: 'My Electron Application'
}
},
{
name: '@electron-forge/maker-zip',
platforms: ['darwin']
},
{
name: '@electron-forge/maker-deb',
config: {
options: {
maintainer: 'Your Name',
homepage: 'https://yourwebsite.com'
}
}
},
{
name: '@electron-forge/maker-rpm',
config: {}
}
],
plugins: [
{
name: '@electron-forge/plugin-auto-unpack-natives',
config: {}
}
]
};
パッケージングの実行
# アプリケーションをパッケージング(実行可能な形式に変換)
npm run package
# インストーラーを作成
npm run make
# 特定のプラットフォーム向けにビルド
npm run make -- --platform=win32
npm run make -- --platform=darwin
npm run make -- --platform=linux
パッケージングプロセスでは、以下の処理が行われます:
- 依存関係の解決: 本番環境で必要なモジュールのみを抽出
- ASARアーカイブの作成: ソースコードを単一のアーカイブファイルに圧縮
- 実行ファイルの生成: Electronランタイムとアプリケーションを結合
- インストーラーの作成: 各プラットフォーム向けのインストーラーを生成
Electron Builderを使用したパッケージング
詳細な設定
Electron Builderはより詳細な設定が可能です。
// package.json
{
"name": "my-electron-app",
"version": "1.0.0",
"description": "My Electron Application",
"main": "dist/main.js",
"scripts": {
"start": "electron .",
"build": "electron-builder",
"dist": "electron-builder --publish=never",
"dist:win": "electron-builder --win",
"dist:mac": "electron-builder --mac",
"dist:linux": "electron-builder --linux"
},
"build": {
"appId": "com.yourcompany.yourapp",
"productName": "Your App",
"directories": {
"output": "dist-electron"
},
"files": [
"dist/**/*",
"assets/**/*",
"!**/*.ts",
"!**/*.map"
],
"asarUnpack": [
"node_modules/sharp/**/*"
],
"mac": {
"category": "public.app-category.productivity",
"icon": "assets/icon.icns",
"hardenedRuntime": true,
"gatekeeperAssess": false,
"entitlements": "build/entitlements.mac.plist",
"entitlementsInherit": "build/entitlements.mac.plist",
"target": [
{
"target": "dmg",
"arch": ["x64", "arm64"]
},
{
"target": "zip",
"arch": ["x64", "arm64"]
}
]
},
"win": {
"icon": "assets/icon.ico",
"target": [
{
"target": "nsis",
"arch": ["x64", "ia32"]
},
{
"target": "portable",
"arch": ["x64"]
}
],
"certificateFile": "cert/windows.pfx",
"certificatePassword": "${CERT_PASSWORD}"
},
"linux": {
"icon": "assets/icon.png",
"category": "Utility",
"target": [
{
"target": "AppImage",
"arch": ["x64"]
},
{
"target": "deb",
"arch": ["x64", "arm64"]
},
{
"target": "rpm",
"arch": ["x64"]
}
]
},
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true,
"deleteAppDataOnUninstall": true,
"differentialPackage": true
},
"dmg": {
"contents": [
{
"x": 130,
"y": 220
},
{
"x": 410,
"y": 220,
"type": "link",
"path": "/Applications"
}
]
}
}
}
高度な機能の実装
マルチ言語インストーラー:
// electron-builder.json の nsis セクション
{
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true,
"installerLanguages": ["en_US", "ja_JP", "zh_CN"],
"language": "1041" // 日本語をデフォルトに
}
}
ファイルの関連付け:
{
"fileAssociations": [
{
"ext": "myapp",
"name": "MY_APP_FILE",
"description": "My App Document",
"icon": "assets/document-icon.ico"
}
],
"protocols": [
{
"name": "my-app-protocol",
"schemes": ["myapp"]
}
]
}
コード署名とノータリゼーション
なぜコード署名が必要なのか
コード署名は、アプリケーションの作成者を証明し、配布後に改ざんされていないことを保証する仕組みです。特に以下の理由で重要です:
信頼性の向上: ユーザーがアプリケーションの出所を確認できます。
セキュリティ警告の回避: 署名されていないアプリケーションは、OSから警告が表示されます。
必須要件: macOSではノータリゼーション(公証)が事実上必須となっています。
各プラットフォームでの実装
Windows のコード署名:
// electron-builder の設定
{
"win": {
"certificateFile": "cert/windows-cert.pfx",
"certificatePassword": "${WINDOWS_CERT_PASSWORD}",
"signingHashAlgorithms": ["sha256"],
"rfc3161TimeStampServer": "http://timestamp.digicert.com"
}
}
証明書の取得方法:
- 認証局(CA)から証明書を購入(年間数万円〜)
- 自己署名証明書を作成(開発用のみ)
macOS のコード署名とノータリゼーション:
// electron-builder の設定
{
"mac": {
"hardenedRuntime": true,
"gatekeeperAssess": false,
"entitlements": "build/entitlements.mac.plist",
"entitlementsInherit": "build/entitlements.mac.plist",
"provisioningProfile": "embedded.provisionprofile"
},
"afterSign": "scripts/notarize.js"
}
// scripts/notarize.js
const { notarize } = require('@electron/notarize');
exports.default = async function notarizing(context) {
const { electronPlatformName, appOutDir } = context;
if (electronPlatformName !== 'darwin') {
return;
}
const appName = context.packager.appInfo.productFilename;
return await notarize({
appBundleId: 'com.yourcompany.yourapp',
appPath: `${appOutDir}/${appName}.app`,
appleId: process.env.APPLE_ID,
appleIdPassword: process.env.APPLE_ID_PASSWORD,
teamId: process.env.APPLE_TEAM_ID
});
};
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>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
</dict>
</plist>
自動アップデート機能の実装
アップデートの重要性
アプリケーションをリリースした後も、バグ修正や新機能の追加は続きます。自動アップデート機能により、ユーザーは常に最新版を使用でき、開発者は迅速に修正を配布できます。
electron-updater の実装
npm install electron-updater
// main.js - 自動アップデートの実装
const { app, BrowserWindow, dialog } = require('electron');
const { autoUpdater } = require('electron-updater');
const log = require('electron-log');
// ログの設定
autoUpdater.logger = log;
autoUpdater.logger.transports.file.level = 'info';
// 開発環境でのテスト設定
if (process.env.NODE_ENV === 'development') {
autoUpdater.updateConfigPath = path.join(__dirname, 'dev-app-update.yml');
autoUpdater.forceDevUpdateConfig = true;
}
// アップデートイベントの設定
autoUpdater.on('checking-for-update', () => {
log.info('アップデートを確認中...');
});
autoUpdater.on('update-available', (info) => {
log.info('アップデートが利用可能です。');
dialog.showMessageBox({
type: 'info',
title: 'アップデート利用可能',
message: '新しいバージョンが利用可能です。ダウンロードしますか?',
buttons: ['はい', 'いいえ']
}).then((result) => {
if (result.response === 0) {
autoUpdater.downloadUpdate();
}
});
});
autoUpdater.on('update-not-available', (info) => {
log.info('アップデートはありません。');
});
autoUpdater.on('error', (err) => {
log.error('アップデートエラー: ' + err);
dialog.showErrorBox('アップデートエラー',
'アップデートの確認中にエラーが発生しました。');
});
autoUpdater.on('download-progress', (progressObj) => {
let log_message = 'ダウンロード速度: ' + progressObj.bytesPerSecond;
log_message = log_message + ' - ダウンロード済み ' + progressObj.percent + '%';
log_message = log_message + ' (' + progressObj.transferred + "/" + progressObj.total + ')';
log.info(log_message);
// メインウィンドウにプログレスを送信
if (mainWindow) {
mainWindow.webContents.send('download-progress', progressObj);
}
});
autoUpdater.on('update-downloaded', (info) => {
log.info('アップデートがダウンロードされました。');
dialog.showMessageBox({
type: 'info',
title: 'アップデート準備完了',
message: 'アップデートがダウンロードされました。アプリケーションを再起動して適用しますか?',
buttons: ['再起動', '後で']
}).then((result) => {
if (result.response === 0) {
autoUpdater.quitAndInstall();
}
});
});
// アプリケーション起動時にアップデートをチェック
app.whenReady().then(() => {
// ウィンドウ作成後にアップデートをチェック
setTimeout(() => {
autoUpdater.checkForUpdatesAndNotify();
}, 3000);
});
// 定期的なアップデートチェック(1時間ごと)
setInterval(() => {
autoUpdater.checkForUpdatesAndNotify();
}, 60 * 60 * 1000);
アップデートサーバーの設定
GitHub Releases を使用する場合:
// package.json
{
"repository": {
"type": "git",
"url": "https://github.com/yourusername/your-app.git"
},
"build": {
"publish": [
{
"provider": "github",
"owner": "yourusername",
"repo": "your-app"
}
]
}
}
独自サーバーを使用する場合:
# app-update.yml
provider: generic
url: https://your-server.com/updates
updaterCacheDirName: your-app-updater
サーバー側のディレクトリ構造:
updates/
├── latest.yml # Windows用メタデータ
├── latest-mac.yml # macOS用メタデータ
├── latest-linux.yml # Linux用メタデータ
├── YourApp-1.0.0.exe
├── YourApp-1.0.0.dmg
└── YourApp-1.0.0.AppImage
配布戦略
配布チャネルの選択
アプリケーションを配布する方法は複数あり、それぞれに利点と欠点があります。
| 配布方法 | 利点 | 欠点 | 適用ケース |
|---|---|---|---|
| 公式ストア | 信頼性が高い 自動アップデート 課金システム |
審査が必要 手数料が発生 制限が多い |
一般消費者向け |
| 自社サイト | 完全な制御 手数料なし 迅速な更新 |
信頼性の構築が必要 インフラ管理 |
企業向け・専門ツール |
| GitHub | 開発者に親しみやすい 無料 バージョン管理 |
一般ユーザーには難しい 容量制限 |
オープンソース |
| CDN | 高速配信 グローバル対応 |
コストが発生 技術的知識が必要 |
大規模配布 |
Microsoft Store への公開
// electron-builder設定
{
"appx": {
"applicationId": "YourCompany.YourApp",
"identityName": "YourCompany.YourApp",
"publisherDisplayName": "Your Company",
"languages": ["ja-JP", "en-US"],
"backgroundColor": "#ffffff"
}
}
公開手順:
- Microsoft開発者アカウントの作成(年間約$19)
- アプリケーションの登録
- .appxまたは.msixパッケージの作成
- ストア審査への提出
Mac App Store への公開
Mac App Storeへの公開は最も複雑ですが、macOSユーザーへの信頼性は最高です。
必要な準備:
- Apple Developer Program への登録(年間$99)
- App Store Connect でのアプリ登録
- サンドボックス対応
- App Store ガイドラインへの準拠
// mas(Mac App Store)ビルド設定
{
"mac": {
"target": [
{
"target": "mas",
"arch": ["x64", "arm64"]
}
],
"type": "distribution",
"category": "public.app-category.productivity",
"entitlements": "build/entitlements.mas.plist",
"provisioningProfile": "build/embedded.provisionprofile"
}
}
パフォーマンス最適化
アプリケーションサイズの削減
Electronアプリケーションのサイズを削減する方法:
不要な依存関係の除外:
// package.json
{
"devDependencies": {
// 開発時のみ必要なパッケージ
},
"dependencies": {
// 実行時に必要なパッケージのみ
}
}
ビルド時の最適化:
// webpack.config.js
module.exports = {
mode: 'production',
optimization: {
minimize: true,
usedExports: true,
sideEffects: false
},
externals: {
// ネイティブモジュールは外部化
'electron': 'commonjs electron'
}
};
ASARアーカイブの圧縮:
{
"asarUnpack": [
// 展開が必要なファイルのみ指定
"node_modules/sqlite3/**/*"
]
}
起動時間の短縮
アプリケーションの起動を高速化する技術:
// main.js - 遅延読み込みの実装
const { app, BrowserWindow } = require('electron');
// 重いモジュールは必要時に読み込む
let heavyModule;
function getHeavyModule() {
if (!heavyModule) {
heavyModule = require('./heavy-module');
}
return heavyModule;
}
// プリロード処理
app.whenReady().then(() => {
// 最小限のウィンドウをすぐに表示
const splash = new BrowserWindow({
width: 400,
height: 300,
frame: false,
alwaysOnTop: true,
transparent: true
});
splash.loadFile('splash.html');
// バックグラウンドで本体を準備
setTimeout(() => {
createMainWindow();
splash.close();
}, 2000);
});
トラブルシューティング
よくある問題と解決方法
ビルドエラー「Module not found」
原因:ネイティブモジュールが正しくリビルドされていない
解決方法:
# electron-rebuild を使用
npm install --save-dev electron-rebuild
npx electron-rebuild
# または
npm run postinstall
Windows Defender による誤検知
原因:署名されていない実行ファイル
解決方法:
- コード署名証明書の取得
- Windows Defender への除外申請
- SmartScreen への申請
macOS「開発元が未確認」エラー
原因:ノータリゼーションが完了していない
解決方法:
# 手動でノータリゼーション
xcrun altool --notarize-app \
--primary-bundle-id "com.yourcompany.yourapp" \
--username "your@email.com" \
--password "@keychain:AC_PASSWORD" \
--file "YourApp.dmg"
デバッグ方法
パッケージング後のデバッグ:
// main.js
const isDev = process.env.NODE_ENV === 'development';
if (isDev || process.argv.includes('--debug')) {
require('electron-debug')();
// ログファイルの出力
const log = require('electron-log');
log.transports.file.level = 'debug';
log.transports.file.file = path.join(app.getPath('userData'), 'debug.log');
}
ベストプラクティス
セキュリティの考慮
パッケージング時のセキュリティ対策:
ソースコードの保護:ASARアーカイブは簡単に展開できるため、重要なロジックは難読化を検討
APIキーの管理:環境変数や暗号化された設定ファイルを使用
アップデートの検証:自動アップデートでは必ず署名を検証
ユーザー体験の向上
インストーラーのカスタマイズ:企業ロゴやカスタムメッセージの追加
初回起動時のセットアップ:ウィザード形式での設定
アンインストール時のクリーンアップ:ユーザーデータの扱いを明確に
継続的インテグレーション
GitHub Actions を使用した自動ビルド:
# .github/workflows/build.yml
name: Build Electron App
on:
push:
tags:
- 'v*'
jobs:
build:
strategy:
matrix:
os: [windows-latest, macos-latest, ubuntu-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Install dependencies
run: npm ci
- name: Build
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npm run dist
- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
name: ${{ matrix.os }}-build
path: dist-electron/*
まとめ
Electronアプリケーションのパッケージングと配布は、開発の最終段階として非常に重要なプロセスです。本記事で解説した主要なポイントを振り返ってみましょう:
パッケージングツールの選択:
- 初心者にはElectron Forge
- 高度なカスタマイズにはElectron Builder
- プロジェクトの規模と要件に応じて選択
プラットフォーム対応:
- 各OSの特性を理解
- 適切な配布形式の選択
- コード署名とノータリゼーションの実施
自動アップデート:
- electron-updaterの実装
- 適切なサーバー構成
- ユーザー体験を考慮した更新フロー
配布戦略:
- ターゲットユーザーに応じたチャネル選択
- 公式ストアと独自配布のバランス
- 継続的なサポート体制の構築
パッケージングは単なる技術的な作業ではなく、ユーザーに価値を届けるための重要なステップです。適切なツールを選び、各プラットフォームの要件を満たし、スムーズな配布とアップデートの仕組みを構築することで、ユーザーに愛されるアプリケーションを提供できるでしょう。
最初は複雑に感じるかもしれませんが、一つずつ確実に実装していけば、必ず完成度の高いアプリケーションを配布できるようになります。本記事で紹介した知識を基に、ぜひ自分のアプリケーションを世界に届けてください。