Mac環境のElectronでWindowsインストーラーとアップデートに対応させる

作成したEelctronアプリをWindowsのインストーラーでインストールできるようにします。さらに、アプリを起動したときにアップデートがあれば、アップデートするようにしてみます。

ディレクトリ構成


sample/
  ├─ app/
  │   ├─ dev/  アプリには含めないモジュール(SCSSのコンパイルなど)
  │   └─ src/  アプリ本体
  ├─ build/    パッケージ化されたアプリ
  └─ release/  インストーラー形式のアプリ

こんな感じでフォルダを作成してください。

サンプルアプリの作成


npm init -y

sample/app/srcに移動して、ターミナルコマンドを入力します。


{
  "name": "sample",
  "version": "0.0.1",
  "description": "",
  "main": "main.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

name"sample"version"0.0.1" に、main"main.js" に変更します。


sample/
  ├─ app/
  │   ├─ dev/
  │   └─ src/
  │       ├─ index.html
  │       ├─ main.js
  │       └─ package.json
  ├─ build/
  └─ release/

srcフォルダにindex.htmlmain.jsを追加します。


<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>electron</title>
</head>
<body>
  <b>Hello World! v0.0.1</b>
</body>
</html>

// モジュール
var electron = require('electron');
var app = electron.app;
var BrowserWindow = electron.BrowserWindow;
var dialog = electron.dialog;

// メインウィンドウ
var mainWindow;

// 全てのウィンドウが閉じたら終了
app.on('window-all-closed', function() {
  if (process.platform != 'darwin') {
    app.quit();
  }
});

// Electronの初期化後に実行
app.on('ready', function() {
  // メイン画面の表示
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600
  });
  mainWindow.loadURL('file://' + __dirname + '/index.html');

  //ウィンドウが閉じたらアプリを終了
  mainWindow.on('closed', function() {
    mainWindow = null;
  });
});

アプリの起動


cd ../
electron src

cd ../ で1つ上の階層へ移動し、electron srcsrcフォルダ内のアプリを起動します。

アプリの終了

デフォルトだとMacでは左上の赤丸ボタンをクリックしてもアプリは終了しません。ターミナル上でCtrl+Cで終了するか、アプリ上でCommand+Qで終了させます。

アプリのパッケージ化

Windowsインストーラーを作成する前に、まずはパッケージ化する必要があります。


npm init -y

今度は、sample/app/devフォルダに移動し、コマンドを入力します。すると、package.jsonが作成されますが、今回は特に編集はしません。

gulpの導入


npm install gulp -g

gulpをインストールしていなければします。


gulp -v
[16:12:36] CLI version 3.9.1
[16:12:36] Local version 3.9.1

バージョンが表示されていればインストールできています。

electron-packagerの導入


npm install electron-packager --save-dev

アプリをパッケージ化するツールを導入します。依存ライブラリとして wineをインストールする必要があるかもしれませんが、ターミナルに表示される通りに依存ライブラリをインストールしていけば問題ありません。

gulpfile.jsの作成


sample/
  ├─ app/
  │   ├─ dev/
  │   │   ├─ gulpfile.js
  │   │   └─ package.json
  │   └─ src/
  │       ├─ index.html
  │       ├─ main.js
  │       └─ package.json
  ├─ build/
  └─ release/

gulpfile.jsを作成します。


var gulp = require('gulp'),
    packager = require('electron-packager');

gulp.task('packager', function(done) {
  packager({
    dir: '../src',             // アプリ本体のフォルダ 
    out: '../../build',        // 出力先のフォルダ
    name: 'sample',            // アプリ名
    arch: 'x64',               // 32bit/64bit
    platform: 'win32',         // OS
    electronVersion: '1.7.9',  // Electronのバージョン
    overwrite: true            // すでにフォルダがある場合は上書き
  });
});

他にもオプションはたくさんありますが、最低限これでいいと思います。


electron -v
v1.7.9

electronVersion が分からなければ、このコマンドで表示されます。

タスクの実行


gulp packager

このコマンドを入力すると、sample/buildフォルダ内にsample-win32-x64というフォルダが作成されます。この中のsample.exeをクリックすればアプリを起動することができます。

Windowsインストーラーの作成


npm install electron-winstaller --save-dev

再び、sample/app/devフォルダでコマンドを実行します。Windowsインストーラー形式で書き出せるツールをインストールします。


var gulp = require('gulp'),
    packager = require('electron-packager'),
    electronInstaller = require('electron-winstaller');

gulp.task('packager', function(done) {
  packager({
    dir: '../src',
    out: '../../build',
    name: 'sample',
    arch: 'x64',
    platform: 'win32',
    electronVersion: '1.7.9',
    overwrite: true
  });
});

gulp.task('installer', function() {
  electronInstaller.createWindowsInstaller({
    appDirectory: '../../build/sample-win32-x64',
    outputDirectory: '../../release',
    description: 'description...',
    authors: 'Takamasa',
    exe: 'sample.exe'
  });
});

gulpfile.jsに追記します。

色々なオプションがあるので一度目を通しておくといいと思います。


gulp packager

gulpコマンドを実行しますが、Mac上ではうまく動きませんでした。なので、Parallelsを使ったWindows仮想環境上で実行するとうまくいきました。

gulp-sassをプロジェクトで使用している場合、Macのnode-sassとWindowsのnode-sassが干渉してしまったので、electron-packagerelectron-winstallerだけ別のgulpfile.jsで管理するといいかもしれません。


sample/
  ├─ app/
  │   ├─ dev/
  │   │   ├─ gulpfile.js
  │   │   └─ package.json
  │   └─ src/
  │       ├─ index.html
  │       ├─ main.js
  │       └─ package.json
  ├─ build/
  │   └─ sample-win32-x64/
  └─ release/
      ├─ RELEASES
      ├─ Setup.exe
      ├─ Setup.msi
      └─ sample-0.0.1-full.nupkg

sample/releaseフォルダに4つのファイルが出力されます。Setup.exeを実行すればインストーラーが起動し、インストールが始まります。インストール中に表示される緑色のGIF画像はオプションで変更することができます。

インストールすることはできますが、1つ問題があります。それは、アプリ起動のショートカットがデスクトップに追加されないということです。そのため、2回目以降アプリを起動することができません。

デスクトップにショートカット追加


npm install electron-squirrel-startup --save

sample/app/srcフォルダでコマンドを実行します。このパッケージは自動的にデスクトップにショートカットを追加してくれるものです。


// デスクトップにショートカットアイコン追加
if (require('electron-squirrel-startup')) return;

そして、main.jsの一番上に追加します。これで、パッケージ化とインストーラーの作成を行えば、インストール時にショートカットが追加されるようになります。

自動アップデート

アップデート機能を追加


const autoUpdater = electron.autoUpdater;
autoUpdater.setFeedURL('https://アップロードしたURL/update');
autoUpdater.checkForUpdates();

autoUpdater.on("update-downloaded", () => {
  index = dialog.showMessageBox({
    message: "アップデートあり",
    detail: "再起動してインストールできます。",
    buttons: ["再起動", "後で"],
    noLink: true
  });
  if (index === 0) {
    autoUpdater.quitAndInstall();
  }
});
autoUpdater.on("update-not-available", () => {
  dialog.showMessageBox({
    message: "アップデートはありません",
    buttons: ["OK"]
  });
});
autoUpdater.on("error", () => {
  dialog.showMessageBox({
    message: "アップデートエラーが起きました",
    buttons: ["OK"]
  });
});

gulpfile.jsにアップデート機能を追加します。アップロードしたURLは、後述する処理を行ってから入力します。

v0.0.2を作成


<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>electron</title>
</head>
<body>
  <b>Hello World! v0.0.1</b>
  <b>Hello World! v0.0.2</b>
</body>
</html>

バージョンアップされたことが分かるように書き換えておきます。


{
  ...
  "version": "0.0.1",
  "version": "0.0.2",
  ...
}

sample/app/srcpackage.jsonのバージョンを0.0.2にします。

そして、パッケージ化とインストーラーを作成します。

サーバーにアップロード


update/
  ├─ RELEASES
  └─ sample-0.0.2-full.nupkg

RELEASESsample-0.0.2-full.nupkgのファイルをupdateフォルダの中に入れてレンタルサーバー等にアップロードしてください。


autoUpdater.setFeedURL('https://アップロードしたURL/update');

フォルダ名はupdateである必要はありませんが、変更する場合は setFeedURL()/update 部分も変更してください。

実際の挙動

アプリを起動してしばらくすると、このようなメッセージボックスが出てきます。再起動を選択すればすぐにバージョンアップが行われます。後でを選択すると、アプリを終了後にアップデートされ、次回起動時には最新版になっています。つまり、どちらの選択肢でもアップデートされます。

アップデートするかどうかをユーザーに任せるなら、最新版かどうかを返すAPIを作成して、ユーザーがアップデートするという選択をしたら autoUpdater.checkForUpdates(); を実行するという風にすれば実装できます。