😽

electron-updaterのメモ

6 min read

  • ドキュメントに書いてある通りsetFeedUrlは使わない。
     - ソースのコメントを見る限り動きそうだが、ヘッダーつけたりできない。

  • HttpHeaderはconstructorで指定する

  • package.json | electron-builder.yml ...等で書いた設定はapp-update.ymlに反映されるが、上書きできる。ただ、キャッシュのdirectoryとかでも使われる。
    ビルドしないでやるときは、適当にdev-app-update.ymlをつくってあげるとよい。

クラス

Provider > GenericProvicer
         > GithubProvicer
AppUpdater > MacUpdater
AppUpdater > BaseUpdater > NsisUpdater
AppUpdater > BaseUpdater > AppImageUpdater

default

electron-bulilderのbiuld時のpublishの値が使われる。

app-update.yml

Providerの生成

  protected async getUpdateInfoAndProvider(): Promise<UpdateInfoAndProvider> {
    await this.app.whenReady()

    if (this.clientPromise == null) {
      // ここでつくる 
      this.clientPromise = this.configOnDisk.value.then(it => createClient(it, this, this.createProviderRuntimeOptions()))
    }

    const client = await this.clientPromise
    const stagingUserId = await this.stagingUserIdPromise.value
    client.setRequestHeaders(this.computeFinalHeaders({"x-user-staging-id": stagingUserId}))
    return {
      info: await client.getLatestVersion(),
      provider: client,
    }
  }

configOnDiskを追っていくとapp-update.ymlを読んでいる箇所にぶつかる。

  private async loadUpdateConfig(): Promise<any> {
    if (this._appUpdateConfigPath == null) {
      this._appUpdateConfigPath = this.app.appUpdateConfigPath
    }
    return load(await readFile(this._appUpdateConfigPath, "utf-8"))
  }
  get appUpdateConfigPath(): string {
    return this.isPackaged ? path.join(process.resourcesPath!!, "app-update.yml") : path.join(this.app.getAppPath(), "dev-app-update.yml")
  }

or SetFeed Url

 setFeedURL(options: PublishConfiguration | AllPublishOptions | string) {
    const runtimeOptions = this.createProviderRuntimeOptions()
    // https://github.com/electron-userland/electron-builder/issues/1105
    let provider: Provider<any>
    if (typeof options === "string") {
      provider = new GenericProvider({provider: "generic", url: options}, this, {
        ...runtimeOptions,
        isUseMultipleRangeRequest: isUrlProbablySupportMultiRangeRequests(options),
      })
    }
    else {
      provider = createClient(options, this, runtimeOptions)
    }
    this.clientPromise = Promise.resolve(provider)
  }

これはconstructorでオプションを指定した時も呼ばれる。

    if (options != null) {
      this.setFeedURL(options)

      if (typeof options !== "string" && options.requestHeaders) {
        this.requestHeaders = options.requestHeaders
      }
    }

autoUpdater.setFeedUrl()

そもそもドキュメントに使うなと書いてある。

  • httpHeadersの指定はできなそう。

https://github.com/electron-userland/electron-builder/blob/1643d569600/packages/electron-updater/src/AppUpdater.ts#L178

constructorで指定したやつはheaderを保持するが、setFeedUrlでは無視されてそう。
(とういうか実際効かない)

https://github.com/electron-userland/electron-builder/blob/1643d569600a197858585e895e3176948d3eec85/packages/electron-updater/src/providerFactory.ts#L46

https://github.com/electron-userland/electron-builder/blob/1643d569600a197858585e895e3176948d3eec85/packages/electron-updater/src/providers/GenericProvider.ts

configurationでheaderの参照もない。

  protected async getUpdateInfoAndProvider(): Promise<UpdateInfoAndProvider> {
    await this.app.whenReady()

    if (this.clientPromise == null) {
      this.clientPromise = this.configOnDisk.value.then(it => createClient(it, this, this.createProviderRuntimeOptions()))
    }

    const client = await this.clientPromise
    const stagingUserId = await this.stagingUserIdPromise.value
    // ここでheaderをつけている。
    client.setRequestHeaders(this.computeFinalHeaders({"x-user-staging-id": stagingUserId}))
    return {
      info: await client.getLatestVersion(),
      provider: client,
    }
  }
  private computeFinalHeaders(headers: OutgoingHttpHeaders) {
    if (this.requestHeaders != null) {
      Object.assign(headers, this.requestHeaders)
    }
    return headers
  }

NsisUpdater

  quitAndInstall(isSilent = false, isForceRunAfter = false): void {
    this._logger.info(`Install on explicit quitAndInstall`)
    const isInstalled = this.install(isSilent, isSilent ? isForceRunAfter : true)
    if (isInstalled) {
      setImmediate(() => {
        this.app.quit()
      })
    }
    else {
      this.quitAndInstallCalled = false
    }
  }

quitしているだけなんだけど、quitイベントにhookがかかっている。


  protected addQuitHandler(): void {
   if (this.quitHandlerAdded || !this.autoInstallOnAppQuit) {
     return
   }

   this.quitHandlerAdded = true

   this.app.onQuit(exitCode => {
     if (this.quitAndInstallCalled) {
       this._logger.info("Update installer has already been triggered. Quitting application.")
       return
     }

     if (exitCode !== 0) {
       this._logger.info(`Update will be not installed on quit because application is quitting with exit code ${exitCode}`)
       return
     }

     this._logger.info("Auto install update on quit")
     this.install(true, false)
   })
 }

その後installerを実行したり。

https://github.com/electron-userland/electron-builder/blob/1643d569600a197858585e895e3176948d3eec85/packages/electron-updater/src/NsisUpdater.ts#L117

管理者権限が必要なときはelevate.exeを実行したり。

installerの実態はダウンロードしたexeに見えるけど・・・

https://github.com/electron-userland/electron-builder/blob/1643d569600a197858585e895e3176948d3eec85/packages/electron-updater/src/BaseUpdater.ts#L47

https://github.com/electron-userland/electron-builder/blob/1643d569600a197858585e895e3176948d3eec85/packages/electron-updater/src/DownloadedUpdateHelper.ts#L55

this._fileはprivateなのでここで全て。

nsisのinstallerがダウンロードされるのかな??

Setup.exeとかが吐かれてた気がするので、多分これがインストーラーだ。理解。

うーん、Windowsマシンがないのが少しやりにくい。


downloadとinstallの違い。