ElectronのAuto Updateを実装する(javascript)

JavaScript

手順

  • 必要なモジュールをインストール
  • main.jsを作成
  • index.htmlを作成
  • GitHubと連携

必要なモジュールをインストール

まずはelectron, electron-builder, electron-updaterをインストールします。

npm install electron -D
npm install electron-builder -D
npm install electron-updater

詳細は後述しますが、最終形のpackage.jsonはこのようになります。

package.json

{
  "name": "{PROJECT_NAME}",
  "version": "0.1.0",
  "description": "",
  "main": "main.js",
  "scripts": {
    "start": "chcp 65001 && electron . --develop --debug-mode",
    "build": "electron-builder --win --x64 --publish never",
    "publish": "electron-builder --win --x64 --publish always"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/{USER_NAME}/{PROJECT_NAME}.git"
  },
  "author": "{USER_NAME}",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/{USER_NAME}/{PROJECT_NAME}/issues"
  },
  "homepage": "https://github.com/{USER_NAME}/{PROJECT_NAME}#readme",
  "build": {
    "appId": "your.project.id",
    "files": [
      "build",
      "package.json",
      "package-lock.json",
      "main.js",
      "index.html"
    ],
    "win": {
      "icon": "build/style/img/{PROJECT_NAME}.png",
      "publish": {
        "provider": "github",
        "owner": "{USER_NAME}"
      },
      "target": {
        "target": "nsis"
      }
    },
    "nsis": {
      "oneClick": false,
      "allowToChangeInstallationDirectory": true
    }
  },
  "devDependencies": {
    "electron": "^20.0.1",
    "electron-builder": "^23.3.3"
  },
  "dependencies": {
    "electron-updater": "^5.2.1"
  }
}

scripts

  "scripts": {
    "start": "chcp 65001 && electron . --develop --debug-mode",
    "build": "electron-builder --win --x64 --publish never",
    "publish": "electron-builder --win --x64 --publish always"
  },

上から順にElectronの実行script、
electron-builderを用いたbuild script(GitHubへのPublishなし)
electron-builderを用いたbuild script(GitHubへのPublishあり)

今回はwindowsしかターゲットにしていないので、–win –x64を引数に指定します。
必要ならbuild以下を改変してください。

  "build": {
    "appId": "your.project.id",
    "files": [
      "build",
      "package.json",
      "package-lock.json",
      "main.js",
      "index.html"
    ],
    "win": {
      "icon": "build/style/img/{PROJECT_NAME}.png",
      "publish": {
        "provider": "github",
        "owner": "{USER_NAME}"
      },
      "target": {
        "target": "nsis"
      }
    },
    "nsis": {
      "oneClick": false,
      "allowToChangeInstallationDirectory": true
    }
  },

build以下の設定はelectron-builderが利用します。
filesはビルド時に取り込まれるファイル群になります。
今回はmain.js、index.htmlが含まれていれば問題ありませんが、最終的にはtypescriptで書いてbuildディレクトリにトランスパイルされたファイルが出力される想定のため、root直下にbuildフォルダを作成した上でfilesに追記しています。

win以下はビルド対象毎の設定になります。
今回はiconの指定をしていますが、試すだけなら不要です。
publish.providerにはgithubを指定します。
S3等にアップロードする場合は適宜値を変更してください。

また今回はインストーラ形式で配布したいため、targetにnsisを指定します。
ここもポータブル等必要な形式に書き換えてください。ただし、ポータブル形式の場合、ユーザ設定ファイルをどこに設置するべきか?Update時に消えないか?など気にすることが増えると思います。

nsisの詳細設定を書きたい場合にはnsis以下に記載します。
今回はoneClickインストールはfalseで、ユーザがインストールディレクトリを変更できるようにします。

main.jsを作成

まずは単にindex.htmlを読み込んでwindowを開くコードを書きます。ElectronのGet Startedページ等を参照してください。
そして自動更新関係の処理を追記していきます。

main.js

const { app, dialog, BrowserWindow } = require("electron");
const { autoUpdater } = require("electron-updater");

initialize();

function initialize() {
  // autoUpdater.checkForUpdatesAndNotify();
  autoUpdater.checkForUpdates();

  autoUpdater.on("update-downloaded", (info) => {
    const dialogOpts = {
      type: "info",
      buttons: ["Restart", "Later"],
      message: "UPDATE",
      detail:
        "A new version has been downloaded. Restart the application to apply the updates."
    };

    dialog.showMessageBox(mainWin, dialogOpts).then((returnValue) => {
      if (returnValue.response === 0) {
        autoUpdater.quitAndInstall();
      }
    });
  });

  autoUpdater.on("error", (err) => {
    console.error("There was a problem updating the application!");
    console.error(err);
  });

  app.whenReady().then(() => {
    createWindow();

    app.on("activate", () => {
      if (BrowserWindow.getAllWindows().length === 0) {
        createWindow();
      }
    });
  });

  app.on("window-all-closed", () => {
    if (process.platform !== "darwin") {
      app.quit();
    }
  });
}

function createWindow() {
  const win = new BrowserWindow({
    width: 800,
    height: 600
  });

  win.loadFile("index.html");
}

AutoUpdateの最小構成

最小構成の場合は以下の2行で終わりとなります。
起動時に対象のリポジトリをチェックし、更新があった場合はアプリの終了時に自動インストールされます。

const { autoUpdater } = require("electron-updater");
autoUpdater.checkForUpdatesAndNotify();

更新発生時には以下のような通知ダイアログが出現します。

これだけでは物足らない&通知用のToastが期待したものではないため改変します。

const { autoUpdater } = require("electron-updater");

autoUpdater.checkForUpdates();

autoUpdater.on("update-downloaded", (info) => {
  const dialogOpts = {
    type: "info",
    buttons: ["Restart", "Later"],
    message: "UPDATE",
    detail:
      "A new version has been downloaded. Restart the application to apply the updates."
  };

  dialog.showMessageBox(mainWin, dialogOpts).then((returnValue) => {
    if (returnValue.response === 0) {
      autoUpdater.quitAndInstall();
    }
  });
});

autoUpdater.on("error", (err) => {
  console.error("There was a problem updating the application!");
  console.error(err);
});

checkForUpdatesAndNotifyではToastが出現するため、checkForUpdatesに置き換えます。
その上で、autoUpdater.on(“update-downloaded” のイベントをリッスンし、electronのダイアログを表示してユーザに更新の有無を選択させます。

index.htmlを作成

正直中身は何でもいいです。
画面上で更新されたことが伝わるように何かしら文字を書いておきます。(今回はversion0.1.0)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <h1>version 0.1.0</h1>
  </body>
</html>

GitHubとの連携

ここまでくれば後はGitHubへのモジュールのアップロードだけです。
GitHubのリリース機能を利用します。

Electron-UpdaterはReleaseにアップロードされている特定のファイルを読んでバージョンアップの有無を判定します。

全体の流れとしては
1.GitHub Releaseにインストーラと付属ファイルを配置する。
2.Electron-Updaterが付属ファイルを読んで更新の有無を判断し、インストーラをダウンロードして実行する。
だけです。

1.の部分を手動で行うのは煩雑なため、Electron-Builderには専用のオプションが用意されています。
それがPublish引数ですね。

    "build": "electron-builder --win --x64 --publish never",
    "publish": "electron-builder --win --x64 --publish always"

–publishを付けて実行すると、ビルドが終わった後に自動でreleaseのdraftを書いた上でモジュール類をアップロードしてくれます。が、手元で動作確認したいだけの時にアップロードされても困るので、neverを付けたbuildコマンドも用意しています。

GitHubのトークンを設定

とりあえず今のままでPublish Scriptを実行してみましょう。
そうするとGH_Tokenがない、と言う感じのエラーが出ると思います。

他人が勝手にReleaseにファイルをアップロードされては困るためトークンが必要になります。
ProfileボタンからSettings>Developer Settingsへと進んで、personal access tokensから発行します。

さて、ここで取得したトークンをElectron-Builderに渡してあげる必要があります。
環境変数に追加しても良ければ追加すればOKです。
CIで回している場合も適宜環境変数を設定してあげてください。
今回は環境を汚したくない&リポジトリに閉じていて欲しいのでprojectのrootディレクトリ(package.jsonと同階層)にelectron-builder.envファイルを設定します。

GH_TOKEN=ghp_sadfafdasf1234567891164598764545465

これで環境変数として認識されます。(間違ってコミットしないように.envはignoreしてください)

あとはPublish Scriptを実行し、GitHub上でDraftになっているリリースを確定させれば完成です。

完成

この状態で新しいリリースを作成すると、無事新しいバージョンにアップデートされました。

コメント