電子アップデーターはアプリケーションを自動的に更新およびアップグレードします

Electronには自動アップデート機能が組み込まれていますautoUpdaterが、サービスの構成が少し複雑なので、最終的にelectron-updaterツール プラグインを選択しました。ここでは、electron-updaterアプリケーションを自動的に更新およびアップグレードするための設定方法について説明します。

電子アップデーター

1. プロジェクトの依存関係とスクリプト

インストールしelectron-updaterelectron-log

pnpm add -D electron-updater electron-log

package.json の完全な構成は次のとおりです。

{
    
    
  "name": "post-tools",
  "productName": "Post Tools",
  "version": "3.0.0",
  "description": "一个基于electron和node开发,用于http/https接口测试的工具",
  "main": "./out/main/index.js",
  "author": "Tiven",
  "homepage": "https://tiven.cn",
  "scripts": {
    
    
    "format": "prettier --write .",
    "lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
    "start": "electron-vite preview",
    "dev": "electron-vite dev",
    "dev:debug": "nodemon --watch ./src/main/index.js --exec \" electron-vite dev\" ",
    "build": "electron-vite build",
    "postinstall": "electron-builder install-app-deps",
    "build:win": "npm run build && electron-builder --win --config",
    "build:mac": "npm run build && electron-builder --mac --config",
    "build:linux": "npm run build && electron-builder --linux --config",
    "git": "tive git -c tive.git.config.cjs"
  },
  "dependencies": {
    
    
    "@electron-toolkit/preload": "^2.0.0",
    "@electron-toolkit/utils": "^1.0.2"
  },
  "devDependencies": {
    
    
    "@ant-design/icons": "4.0.0",
    "@electron/notarize": "^1.2.3",
    "@vitejs/plugin-react": "^4.0.0",
    "about-window": "^1.15.2",
    "ahooks": "^3.7.7",
    "antd": "^5.6.2",
    "axios": "^1.4.0",
    "electron": "^24.4.1",
    "electron-builder": "^23.6.0",
    "electron-builder-squirrel-windows": "^24.5.0",
    "electron-log": "^4.4.8",
    "electron-updater": "^5.3.0",
    "electron-vite": "^1.0.23",
    "eslint": "^8.42.0",
    "eslint-config-prettier": "^8.8.0",
    "eslint-plugin-prettier": "^4.2.1",
    "eslint-plugin-react": "^7.32.2",
    "jsoneditor": "8",
    "prettier": "^2.8.8",
    "prop-types": "^15.8.1",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "vite": "^4.3.9"
  }
}

2. パッケージングパラメータを設定する

  • 電子ビルダー.yml
appId: cn.tiven.app
productName: Post Tools
copyright: Copyright © 2023 ${
    
    author}
directories:
  buildResources: build
  output: dist
files:
  - '!**/.vscode/*'
  - '!src/*'
  - '!dist2/*'
  - '!node_modules/*'
  - '!electron.vite.config.{js,ts,mjs,cjs}'
  - '!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}'
  - '!{.env,.env.*,.npmrc,pnpm-lock.yaml}'
asarUnpack:
  - resources/**
afterSign: build/notarize.js
win:
  executableName: Post Tools
  icon: resources/icon.ico
  publisherName: tiven
  verifyUpdateCodeSignature: false
  target:
    - nsis
    - squirrel
nsis:
  oneClick: false
  artifactName: ${
    
    name}-${
    
    version}-setup.${
    
    ext}
  shortcutName: ${
    
    productName}
  uninstallDisplayName: ${
    
    productName}
  createDesktopShortcut: always
  perMachine: true
  allowToChangeInstallationDirectory: true
  guid: 2cf313e9-0f05-xxxx-1006-e278272e9b2a
squirrelWindows:
  loadingGif: resources/loading.gif
  iconUrl: https://tiven.cn/static/img/net-stats.ico
mac:
  category: public.app-category.developer-tools
  entitlementsInherit: build/entitlements.mac.plist
  extendInfo:
    - NSCameraUsageDescription: Application requests access to the device's camera.
    - NSMicrophoneUsageDescription: Application requests access to the device's microphone.
    - NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder.
    - NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder.
dmg:
  artifactName: ${
    
    name}-${
    
    version}.${
    
    ext}
linux:
  target:
    - AppImage
    - snap
    - deb
  maintainer: electronjs.org
  category: Utility
appImage:
  artifactName: ${
    
    name}-${
    
    version}.${
    
    ext}
npmRebuild: false
publish:
  provider: generic
  url: http://localhost:3000/

最後の 3 行のパラメータ設定が更新の鍵publicとなります。ローカル デバッグに使用されるため、ダウンロード速度は非常に速く、オンラインになった後は正式なドメイン名サービスに置き換えることができます。electron-updater
http://localhost:3000/

  • dev-app-update.yml
provider: generic
url: http://localhost:3000/
updaterCacheDirName: post-tools-updater

3. 主な更新ロジック

新しいファイルを作成するsrc/main/autoUpdater.js

// src/main/autoUpdater.js

import {
    
     app, dialog } from 'electron'
import {
    
     join } from 'path'
import {
    
     autoUpdater } from 'electron-updater'
import logger from 'electron-log'
import {
    
     getLocalData, setLocalData, sleep } from './helper'
import {
    
     productName } from '@package'

export async function autoUpdateInit() {
    
    
  //打印log到本地
  logger.transports.file.maxSize = 1002430 // 10M
  logger.transports.file.format = '[{y}-{m}-{d} {h}:{i}:{s}.{ms}] [{level}]{scope} {text}'
  logger.transports.file.resolvePath = () => join(app.getPath('appData'), 'logs/main.log')

  await sleep(5000)
  //每次启动自动更新检查 更新版本 --可以根据自己方式更新,定时或者什么
  autoUpdater.checkForUpdates()

  autoUpdater.logger = logger
  autoUpdater.disableWebInstaller = false
  autoUpdater.autoDownload = false //这个必须写成false,写成true时,我这会报没权限更新,也没清楚什么原因
  autoUpdater.on('error', (error) => {
    
    
    logger.error(['检查更新失败', error])
  })
  //当有可用更新的时候触发。 更新将自动下载。
  autoUpdater.on('update-available', (info) => {
    
    
    logger.info('检查到有更新,开始下载新版本')
    logger.info(info)
    const {
    
     version } = info
    askUpdate(version)
  })
  //当没有可用更新的时候触发。
  autoUpdater.on('update-not-available', () => {
    
    
    logger.info('没有可用更新')
  })
  // 在应用程序启动时设置差分下载逻辑
  autoUpdater.on('download-progress', async (progress) => {
    
    
    logger.info(progress)
  })
  //在更新下载完成的时候触发。
  autoUpdater.on('update-downloaded', (res) => {
    
    
    logger.info('下载完毕!提示安装更新')
    logger.info(res)
    //dialog 想要使用,必须在BrowserWindow创建之后
    dialog
      .showMessageBox({
    
    
        title: '升级提示!',
        message: '已为您下载最新应用,点击确定马上替换为最新版本!',
      })
      .then(() => {
    
    
        logger.info('退出应用,安装开始!')
        //重启应用并在下载后安装更新。 它只应在发出 update-downloaded 后方可被调用。
        autoUpdater.quitAndInstall()
      })
  })
}

async function askUpdate(version) {
    
    
  logger.info(`最新版本 ${
      
      version}`)
  let {
    
     updater } = getLocalData()
  let {
    
     auto, version: ver, skip } = updater || {
    
    }
  logger.info(
    JSON.stringify({
    
    
      ...updater,
      ver: ver,
    })
  )
  if (skip && version === ver) return
  if (auto) {
    
    
    // 不再询问 直接下载更新
    autoUpdater.downloadUpdate()
  } else {
    
    
    const {
    
     response, checkboxChecked } = await dialog.showMessageBox({
    
    
      type: 'info',
      buttons: ['关闭', '跳过这个版本', '安装更新'],
      title: '软件更新提醒',
      message: `${
      
      productName} 最新版本是 ${
      
      version},您现在的版本是 ${
      
      app.getVersion()},现在要下载更新吗?`,
      defaultId: 2,
      cancelId: -1,
      checkboxLabel: '以后自动下载并安装更新',
      checkboxChecked: false,
      textWidth: 300,
    })
    if ([1, 2].includes(response)) {
    
    
      let updaterData = {
    
    
        version: version,
        skip: response === 1,
        auto: checkboxChecked,
      }
      setLocalData({
    
    
        updater: {
    
    
          ...updaterData,
        },
      })
      if (response === 2) autoUpdater.downloadUpdate()
      logger.info(['更新操作', JSON.stringify(updaterData)])
    } else {
    
    
      logger.info(['更新操作', '关闭更新提醒'])
    }
  }
}

その中には、helper.jsカプセル化された永続データ関連の操作メソッドがあります。

// src/main/helper.js

import {
    
     join } from 'path'
import fs from 'fs'
import {
    
     app } from 'electron'
const dataPath = join(app.getPath('userData'), 'data.json')

export function getLocalData(key) {
    
    
  if (!fs.existsSync(dataPath)) {
    
    
    fs.writeFileSync(dataPath, JSON.stringify({
    
    }), {
    
     encoding: 'utf-8' })
  }
  let data = fs.readFileSync(dataPath, {
    
     encoding: 'utf-8' })
  let json = JSON.parse(data)
  return key ? json[key] : json
}

export function setLocalData(key, value) {
    
    
  let args = [...arguments]
  let data = fs.readFileSync(dataPath, {
    
     encoding: 'utf-8' })
  let json = JSON.parse(data)
  if (args.length === 0 || args[0] === null) {
    
    
    json = {
    
    }
  } else if (args.length === 1 && typeof key === 'object' && key) {
    
    
    json = {
    
    
      ...json,
      ...args[0],
    }
  } else {
    
    
    json[key] = value
  }
  fs.writeFileSync(dataPath, JSON.stringify(json), {
    
     encoding: 'utf-8' })
}

export async function sleep(ms) {
    
    
  return new Promise((resolve) => {
    
    
    const timer = setTimeout(() => {
    
    
      resolve()
      clearTimeout(timer)
    }, ms)
  })
}

メインプロセスでカプセル化された初期化メソッドapp.whenReadyを呼び出します。autoUpdateInit

app.whenReady().then(() => {
    
    
    // ...

    // 版本更新初始化
    autoUpdateInit()
})

4. パッケージ化とデバッグ

ローカルの開発環境では更新が検出されないためelectron-updater、パッケージ化して運用する必要があります。
electron-updater更新検出は主にサーバー側のファイル内のバージョン情報latest.yml(Mac ソフトウェアが生成)を検出し、このバージョン番号はに基づいて生成されます。latest-mac.ymlpackage.jsonversion

システムは MacOS であるpackage.json仮定します。version: 1.0.0

デバッグ手順は次のとおりです。

  1. パッケージングの実行
npm run build:mac
  1. distディレクトリ内に生成されたインストール パッケージ ファイルで、.dmg接尾辞が次のファイルを見つけます。
  2. インストール
  3. package.json を変更し、アップグレードのバージョン番号を に変更しますversion: 1.1.0
  4. パッケージングコマンドを再度実行してください
  5. ローカル静的ファイル サービスを開始します。serveツールキットを使用して、serve をグローバルにインストールすることをお勧めします。
pnpm i -g serve 
  1. プロジェクトのルート ディレクトリでコマンドを実行しますserve dist。このコマンドの機能は、dist ディレクトリ内のすべてのファイルを静的リソースに変換し、httpリクエストを通じて対応するリソースを取得することです。
  2. デフォルトでは、serve が提供する静的サービスが3000ポート上にあります。占有されている場合は、ランダムなポートが割り当てられます。 以降に対応するアドレスを変更することを忘れないでelectron-builder.ymlくださいdev-app-update.ymlすべてが正常な場合は、http://localhost:3000/latest-mac.yml にアクセスすると、対応するリソースを確認できます。
  3. すべての準備ができたら、手順 3 でインストールしたアプリケーションを起動します。数秒待つと、上に表示されたアップデートのリマインダーが表示されます。
  • 注: MacOS で証明書を構成する必要があります。そうしないと、更新の検出時に問題が発生する可能性があります。コード署名証明書は自分で生成できます。「 Mac 構成の自己作成証明書」を参照してください

天文ブログへようこそ

おすすめ

転載: blog.csdn.net/tiven_/article/details/131713981