1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Electronで自動アップデートをしたい

Last updated at Posted at 2024-10-28

viviONグループでは、DLsiteやcomipoなど、二次元コンテンツを世の中に届けるためのサービスを運営しています。
ともに働く仲間を募集していますので、興味のある方はこちらまで。

Electronで自動アップデートをしたい

知り合いの自転車屋さんに、商品のポップを簡単にデザインできるツールを作れないか相談を受けたので、Electronを使ったデスクトップアプリで作ってみることにしました :bike:

実際に使ってもらって、そのフィードバックを反映した新しいバージョンのアプリを配信したいのですが、毎回exeファイルを送ってインストールしなおしてもらうのはちょっと手間ですよね。
こちらが新しいバージョンのアプリを登録したら、アプリの起動時にアップデートチェックが走って勝手に再インストールされる仕組みを導入してみます。

Electron自動アップデートの概要

軽く調べてみたところElectronアプリの自動アップデートを実現したい場合、いくつかの方法があるようです。

  • AWS S3のようなクラウドストレージを利用する
    • アプリ本体とメタデータをクラウドストレージに配置するだけなので一番簡単
  • update.electronjs.orgというElectronチームが提供するオープンソースWEBサービスを利用する
    • アプリがパブリックなGitHubリポジトリのReleasesで公開されている必要がある
    • 専用のモジュールが公開されているので、アプリ側での制御が簡単
    • プライベートなGitHubリポジトリでは利用できない
  • アップデート用サーバーを自分でデプロイする

今回はパブリックなリポジトリを使っていませんが、別にアプリ自体が公開されて問題があるわけではないので、AWS S3を利用した方法を試してみます。

検証環境

Windows10
Node:20.10.0
Vue CLI:5.0.8

環境構築

ある程度これに倣えば同じように検証ができるように記載しています。

前回投稿から引き続き同じ環境での検証になります。
Electronでマルチウィンドウを制御したい

まずは自動更新をするために必要となるautoUpdaterモジュールをインストールします。

npm install electron-updater

そして、Electronアプリを配布可能形式にパッケージング(exeやdmgファイルを作る)するためのモジュールであるelectron-builderも導入します。

npm install --save-dev electron-builder

更新処理を組み込む

メインプロセス側に更新処理を組み込みます。
アプリが起動したタイミングでアップデートがあるかをチェックして、もし更新があればダイアログを表示してアップデートを実行します。

background.ts
'use strict'

import path from "path";
import { app, protocol, BrowserWindow, ipcMain, dialog } from 'electron'
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
import { autoUpdater } from "electron-updater";
import installExtension, { VUEJS3_DEVTOOLS } from 'electron-devtools-installer'
const isDevelopment = process.env.NODE_ENV !== 'production'

// Scheme must be registered before the app is ready
protocol.registerSchemesAsPrivileged([
  { scheme: 'app', privileges: { secure: true, standard: true } }
])

// ★★★ 更新ファイルダウンロード完了 ★★★
autoUpdater.addListener("update-downloaded", () => {
  if (!mainWindow) return;
  // ダイアログを表示して更新があることを伝える
  dialog.showMessageBoxSync(mainWindow, {
    type: "info",
    buttons: ["Yes"],
    defaultId: 1,
    title: "お知らせ",
    message: "新しいバージョンのアプリが存在します",
    detail: "アップデートを実行します",
  });
  // アプリを終了してインストール
  autoUpdater.quitAndInstall();

  return true;
});

let mainWindow, subWindow

async function createWindow() {
  // Create the browser window.
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      
      // Use pluginOptions.nodeIntegration, leave this alone
      // See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration for more info
      nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,
      contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION,
      preload: path.join(__dirname, 'preload.js')
    }
  })

  // メインウィンドウがレンダリングされたタイミング
  mainWindow.once("ready-to-show", async () => {
    // ★★★ 最新バージョンがあるか、チェックしてダウンロード ★★★
    await autoUpdater.checkForUpdates();
  });

  if (process.env.WEBPACK_DEV_SERVER_URL) {
    // Load the url of the dev server if in development mode
    await mainWindow.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
    if (!process.env.IS_TEST) mainWindow.webContents.openDevTools()
  } else {
    createProtocol('app')
    // Load the index.html when not in development
    mainWindow.loadURL('app://./index.html')
  }
}

アプリのバージョンを画面に表示して更新できたかわかるようにしてみます。

MainWindow.vue
<template>
  <h1>アプリバージョン:{{ appVersion }}</h1>
</template>

<script>
export default {
  name: 'MainWindow',
  data() {
    return {
      appVersion: require('../../package.json').version,
    }
  },
}
</script>

S3にバケットを作成する

パッケージされたアプリを配置しておくS3のバケットを作成します。
image.png

バケット名やその他の設定は任意で問題ないです。
注意点としては、パブリックなアクセスを可能とするために、ここのチェックを以下のようにする必要があります。
image.png

バケットを作成したらバケットポリシーを設定します。
オブジェクトの取得だけができる状態にします。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::auto-update-electron-sample/*"
        }
    ]
}

今回はお試しなのでバケットを公開状態で進めていますが、実運用では適切なIAMポリシーを付与したIAMユーザーを作成して、バケットへのアクセスを制限することをオススメします。
Electron Builder側もIAMユーザーのアクセスキーを介してのS3アクセスに対応しているようです。

アプリをパッケージング

更新用ファイルをチェックしにいくエンドポイントを定義します。
builderOptions内が追加した項目です。

vue.config.js
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
  transpileDependencies: true,
  pluginOptions: {
    electronBuilder: {
      preload: "src/preload.js",
      builderOptions: {
        appId: "com.app.multi-window-app",
        win: {
          publish: [
            {
              provider: "generic",
              url: "https://auto-update-electron-sample.s3.ap-northeast-1.amazonaws.com/win32/",
            },
          ],
        },
      },
    },
  },
})

ビルドコマンドを実行してパッケージング。

npm run electron:build

image.png

うまくいけばdist_electronディレクトリに以下の3ファイルが作成されます。

  • [アプリ名] Setup 1.0.0.exe
    • アプリ本体
  • [アプリ名] Setup 1.0.0.exe.blockmap
    • おそらく更新差分情報
  • latest.yml
    • バージョンの情報(これをチェックして更新があるか判定しているはず)

試してみる

まずは作成したexeファイルを実行してアプリをインストールします。

package.jsonにしているバージョンが表示されているはずです。
image.png

次にpackage.jsonのversionを更新して再ビルドします。

{
  "version": "1.0.1",
}

S3のバケットにwin32というディレクトリを作成して、その中に作成した3ファイルをアップロードします。

image.png

この状態でアプリを起動すると…

image.png

ダイアログが表示され、「はい」を選択すると自動でアップデートが始まります。

無事にアプリを自動更新することができました :clap:

image.png

余談

今回は手動で更新ファイルをS3にアップロードしていますが、これをコマンド一発で実行する方法もあるようです。
後ほどそちらも試してみようと思います。

一緒に二次元業界を盛り上げていきませんか?

株式会社viviONでは、フロントエンドエンジニアを募集しています。

また、フロントエンドエンジニアに限らず、バックエンド・SRE・スマホアプリなど様々なエンジニア職を募集していますので、ぜひ採用情報をご覧ください。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?