記事一覧に戻る

Electronアプリのパッケージングと配布方法

13分で読めます
Electronパッケージング配布

Electronアプリのパッケージングと配布方法

はじめに

Electronアプリケーションの開発が完了したら、次はそれをユーザーに届ける段階です。しかし、開発環境で動作するアプリケーションをそのまま配布することはできません。各オペレーティングシステムに適した形式にパッケージングし、インストーラーを作成する必要があります。

本記事では、Electronアプリケーションを Windows、macOS、Linux の各プラットフォーム向けにパッケージングする方法から、自動アップデート機能の実装、さらには配布戦略まで、初学者の方でも理解できるように詳しく解説します。単にツールの使い方を説明するだけでなく、なぜそのような手順が必要なのか、どのような選択肢があるのかも含めて説明していきます。

パッケージングの基礎知識

なぜパッケージングが必要なのか

開発中のElectronアプリケーションは、以下のような構成になっています:

code
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 との統合

インストールと初期設定

bash
# 既存プロジェクトに追加
npm install --save-dev @electron-forge/cli
npx electron-forge import

# 新規プロジェクトの作成
npm init electron-app@latest my-app

Electron Builder

Electron Builderは、より高度な設定が可能な強力なパッケージングツールです。大規模なプロジェクトや複雑な要件がある場合に適しています。

特徴

  • 詳細なカスタマイズが可能
  • 多様な配布形式のサポート
  • 自動アップデート機能の組み込み
  • CI/CD との統合が容易

インストール

bash
npm install --save-dev electron-builder

ツールの比較と選択

項目 Electron Forge Electron Builder
学習曲線 緩やか やや急
設定の複雑さ シンプル 詳細設定可能
カスタマイズ性 標準的 高い
ドキュメント 良好 非常に充実
コミュニティ 公式サポート 大規模
適用プロジェクト 小〜中規模 中〜大規模

初心者の方やシンプルなアプリケーションには Electron Forge を、より細かい制御が必要な場合は Electron Builder をおすすめします。

Electron Forgeを使用したパッケージング

基本的な設定

Electron Forgeを使用したパッケージングの設定を見ていきましょう。

json
// 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"
  }
}
javascript
// 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: {}
    }
  ]
};

パッケージングの実行

bash
# アプリケーションをパッケージング(実行可能な形式に変換)
npm run package

# インストーラーを作成
npm run make

# 特定のプラットフォーム向けにビルド
npm run make -- --platform=win32
npm run make -- --platform=darwin
npm run make -- --platform=linux

パッケージングプロセスでは、以下の処理が行われます:

  1. 依存関係の解決: 本番環境で必要なモジュールのみを抽出
  2. ASARアーカイブの作成: ソースコードを単一のアーカイブファイルに圧縮
  3. 実行ファイルの生成: Electronランタイムとアプリケーションを結合
  4. インストーラーの作成: 各プラットフォーム向けのインストーラーを生成

Electron Builderを使用したパッケージング

詳細な設定

Electron Builderはより詳細な設定が可能です。

json
// 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"
        }
      ]
    }
  }
}

高度な機能の実装

マルチ言語インストーラー

json
// electron-builder.json の nsis セクション
{
  "nsis": {
    "oneClick": false,
    "allowToChangeInstallationDirectory": true,
    "installerLanguages": ["en_US", "ja_JP", "zh_CN"],
    "language": "1041" // 日本語をデフォルトに
  }
}

ファイルの関連付け

json
{
  "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 のコード署名

javascript
// electron-builder の設定
{
  "win": {
    "certificateFile": "cert/windows-cert.pfx",
    "certificatePassword": "${WINDOWS_CERT_PASSWORD}",
    "signingHashAlgorithms": ["sha256"],
    "rfc3161TimeStampServer": "http://timestamp.digicert.com"
  }
}

証明書の取得方法:

  1. 認証局(CA)から証明書を購入(年間数万円〜)
  2. 自己署名証明書を作成(開発用のみ)

macOS のコード署名とノータリゼーション

javascript
// electron-builder の設定
{
  "mac": {
    "hardenedRuntime": true,
    "gatekeeperAssess": false,
    "entitlements": "build/entitlements.mac.plist",
    "entitlementsInherit": "build/entitlements.mac.plist",
    "provisioningProfile": "embedded.provisionprofile"
  },
  "afterSign": "scripts/notarize.js"
}
javascript
// 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
<?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 の実装

bash
npm install electron-updater
javascript
// 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 を使用する場合

json
// package.json
{
  "repository": {
    "type": "git",
    "url": "https://github.com/yourusername/your-app.git"
  },
  "build": {
    "publish": [
      {
        "provider": "github",
        "owner": "yourusername",
        "repo": "your-app"
      }
    ]
  }
}

独自サーバーを使用する場合

yaml
# app-update.yml
provider: generic
url: https://your-server.com/updates
updaterCacheDirName: your-app-updater

サーバー側のディレクトリ構造:

code
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 への公開

javascript
// electron-builder設定
{
  "appx": {
    "applicationId": "YourCompany.YourApp",
    "identityName": "YourCompany.YourApp",
    "publisherDisplayName": "Your Company",
    "languages": ["ja-JP", "en-US"],
    "backgroundColor": "#ffffff"
  }
}

公開手順:

  1. Microsoft開発者アカウントの作成(年間約$19)
  2. アプリケーションの登録
  3. .appxまたは.msixパッケージの作成
  4. ストア審査への提出

Mac App Store への公開

Mac App Storeへの公開は最も複雑ですが、macOSユーザーへの信頼性は最高です。

必要な準備:

  • Apple Developer Program への登録(年間$99)
  • App Store Connect でのアプリ登録
  • サンドボックス対応
  • App Store ガイドラインへの準拠
javascript
// 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アプリケーションのサイズを削減する方法:

不要な依存関係の除外

json
// package.json
{
  "devDependencies": {
    // 開発時のみ必要なパッケージ
  },
  "dependencies": {
    // 実行時に必要なパッケージのみ
  }
}

ビルド時の最適化

javascript
// webpack.config.js
module.exports = {
  mode: 'production',
  optimization: {
    minimize: true,
    usedExports: true,
    sideEffects: false
  },
  externals: {
    // ネイティブモジュールは外部化
    'electron': 'commonjs electron'
  }
};

ASARアーカイブの圧縮

json
{
  "asarUnpack": [
    // 展開が必要なファイルのみ指定
    "node_modules/sqlite3/**/*"
  ]
}

起動時間の短縮

アプリケーションの起動を高速化する技術:

javascript
// 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」

原因:ネイティブモジュールが正しくリビルドされていない

解決方法:

bash
# electron-rebuild を使用
npm install --save-dev electron-rebuild
npx electron-rebuild

# または
npm run postinstall

Windows Defender による誤検知

原因:署名されていない実行ファイル

解決方法:

  • コード署名証明書の取得
  • Windows Defender への除外申請
  • SmartScreen への申請

macOS「開発元が未確認」エラー

原因:ノータリゼーションが完了していない

解決方法:

bash
# 手動でノータリゼーション
xcrun altool --notarize-app \
  --primary-bundle-id "com.yourcompany.yourapp" \
  --username "your@email.com" \
  --password "@keychain:AC_PASSWORD" \
  --file "YourApp.dmg"

デバッグ方法

パッケージング後のデバッグ:

javascript
// 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');
}

ベストプラクティス

セキュリティの考慮

パッケージング時のセキュリティ対策:

  1. ソースコードの保護:ASARアーカイブは簡単に展開できるため、重要なロジックは難読化を検討

  2. APIキーの管理:環境変数や暗号化された設定ファイルを使用

  3. アップデートの検証:自動アップデートでは必ず署名を検証

ユーザー体験の向上

  1. インストーラーのカスタマイズ:企業ロゴやカスタムメッセージの追加

  2. 初回起動時のセットアップ:ウィザード形式での設定

  3. アンインストール時のクリーンアップ:ユーザーデータの扱いを明確に

継続的インテグレーション

GitHub Actions を使用した自動ビルド:

yaml
# .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の実装
  • 適切なサーバー構成
  • ユーザー体験を考慮した更新フロー

配布戦略

  • ターゲットユーザーに応じたチャネル選択
  • 公式ストアと独自配布のバランス
  • 継続的なサポート体制の構築

パッケージングは単なる技術的な作業ではなく、ユーザーに価値を届けるための重要なステップです。適切なツールを選び、各プラットフォームの要件を満たし、スムーズな配布とアップデートの仕組みを構築することで、ユーザーに愛されるアプリケーションを提供できるでしょう。

最初は複雑に感じるかもしれませんが、一つずつ確実に実装していけば、必ず完成度の高いアプリケーションを配布できるようになります。本記事で紹介した知識を基に、ぜひ自分のアプリケーションを世界に届けてください。