Tauri のアップデーター機能の利用

# はじめに

Electron にも[標準でアップデーターの機能がある](https://www.electronjs.org/ja/docs/latest/api/auto-updater) が、[Tauri にもその機能がある](https://tauri.app/v1/guides/distribution/updater/
)。
このアップデーターを使用する方法を調べてまとめてみた。

https://tauri.app/v1/guides/distribution/updater/

### 環境

* Windows 11 Pro 23H2
* Tauri v1.4

# 主な手順

次項で詳しく内容をまとめるが、若干ややこしいので、この手順でやったらいいだろうというのを先に手順だけまとめておく。

1. 認証情報の発行
2. `tauri.config.json` への `tauri.updater` の項目追加
3. `tauri.config.json` への認証情報の追記
4. `tauri.config.json` へのエンドポイント `endpoints` の設定
5. Updater 付インストーラー作成環境の設定 (環境変数の設定)
6. Updater 付インストーラーのビルド
7. Updater 付インストーラーの配置

# アップデーターの設定

## 認証情報の発行

まずは認証情報を発行する。
以下のコマンドは pnpm、npm、yarn、bun などでも可。

```powershell
cargo tauri signer generate -w ~/.tauri/myapp.key
```

Windows の場合は、以下の方を使用する。

```powershell
cargo tauri signer generate -w $HOME/.tauri/myapp.key
```

コマンドを実行すると、パスワードの入力を求められるので、パスワードを入力する。

```
Please enter a password to protect the secret key.
Password: 
Password (one more time): 
Deriving a key from the password in order to encrypt the secret key... done
```

すると、指定したディレクトリに以下の二つのファイルが作成される。

```
Your keypair was generated successfully
Private: C:\Users\aurat\.tauri\myapp.key (Keep it secret!)
Public: C:\Users\aurat\.tauri\myapp.key.pub
```

* **myapp.key**
    * 暗号キー
    * 秘密情報をとして扱う
* **myapp.key.pub**
    * 公開キー
    * tauri.config.json に記載する

## Tauri コンフィグを設定する

アップデーターを有効にするには、以下の条件が必要になる。

* `tauri.updater.active` を `true` にすること
* `tauri.updater.endpoints` に記載すること
* `tauri.updater.pubkey` に記載すること
    * ハッシュ文字列
    * 先に作成した `myapp.key.pub` の中身を記載する


以下の様に Tauri コンフィグを設定する。
エンドポイント (endpoints) の設定方法は後の方で説明する。

```json
{
  "tauri": {
    "updater": {
      "active": true,
      "endpoints": [
        "https://releases.myapp.com/{{target}}/{{arch}}/{{current_version}}"
      ],
      "dialog": true,
      "pubkey": "<myapp.key.pub の中身 (ハッシュ値) をこちらに記載する。>"
    }
  }
}
```

## どのアップデート認識を使用するかを選択する

Tauri アップデーターには、二通りのアップデート認証方式が存在する。
この違いによって、先のエンドポイントの設定やインストーラーの配置の仕方が変わってくるので注意する。

* 静的 JSON ファイルを使用する
* ダイナミックアップデートサーバーを使用する

## 環境変数の設定

認証情報を有効にしてビルドするために、環境変数に以下の情報を設定する。

* `TAURI_PRIVATE_KEY`
    * myapp.key ファイルのファイルパス
* `TAURI_KEY_PASSWORD`
    * myapp.key を作成した際に設定したパスワード

### VSCode の tasks.json で設定する

もし、ソースコードをプライベートに管理出来て、プロジェクトルートに `.tauri/update.key` みたいに管理できるのであれば、VS Code の場合、以下の様にして勝手にフルパスが代入されるようにしてミスを減らすのも手。

```json:.vscode/tasks.json
{
	"version": "2.0.0",
	"tasks": [
		{
			"label": "ui:build",
			"type": "shell",
			"command": "cargo",
			"args": ["tauri", "build"],
			"group": "build",
			"options": {
				"env": {
					"TAURI_PRIVATE_KEY": "${workspaceFolder}\\.tauri\\update.key",
					"TAURI_KEY_PASSWORD": "123456"
				}
			}
		}
	]
}
```

## ビルドする

上記まで設定ができれば、後は通常通りにビルド (`tauri build`) するだけ。

```powershell
cargo tauri build
```

こうしてビルドすると、Windows で NSIS タイプのインストーラーの場合、以下のアーティファクトが作成される。

* `*.exe` 拡張子のインストーラー
* `*.nsis.zip` という名前+拡張子のアップデーター用ファイル
* `*.nsis.zip.sig` という名前+拡張子の署名ファイル

このした二つをアップデーターとして利用する。

## エンドポイントの設定の仕方

https://tauri.app/v1/api/config/#updaterconfig.endpoints

エンドポイント URL の記載には以下の変数が利用できる。

| 変数 | 説明 |
|:-:|:-|
|`{{target}}`|`linux`、`windows` や `darwin` が利用可能。|
|`{{arch}}`| `x86_64`、`i686`、`aarch64`、`armv7` のいずれかを利用可能。|
|`{{current_version}}`|バージョンを指定してアップデートを促したい場合はこちらを使用して入力する。|

しかしながら、最新を提供したい場合であれば、単一のファイルでも良い。
以下の様に `latest.json` のみを作成し、所定の場所に配置する運用もある。

```
https://example.org/app/myapp/latest.json
```

:::note info
エンドポイントは複数設定可能なので、例えば target + arch ごとに分けるという運用は多く発生しそう。
しかしながら、アプリ内にこのエンドポイントは埋め込むことになるので、 veersion 等のハードコードはあまり望ましくないと思われる。
:::

### 静的 JSON ファイル

例えば、GitHub の Releases を参照する場合は次の様に JSON ファイルを設定する。

```json
{
  "version": "v1.0.0",
  "notes": "Test version",
  "pub_date": "2020-06-22T19:25:57Z",
  "platforms": {
    "darwin-x86_64": {
      "signature": "Content of app.tar.gz.sig",
      "url": "https://github.com/username/reponame/releases/download/v1.0.0/app-x86_64.app.tar.gz"
    },
    "darwin-aarch64": {
      "signature": "Content of app.tar.gz.sig",
      "url": "https://github.com/username/reponame/releases/download/v1.0.0/app-aarch64.app.tar.gz"
    },
    "linux-x86_64": {
      "signature": "Content of app.AppImage.tar.gz.sig",
      "url": "https://github.com/username/reponame/releases/download/v1.0.0/app-amd64.AppImage.tar.gz"
    },
    "windows-x86_64": {
      "signature": "Content of app-setup.nsis.sig or app.msi.sig, depending on the chosen format",
      "url": "https://github.com/username/reponame/releases/download/v1.0.0/app-x64-setup.nsis.zip"
    }
  }
}
```

:::note info
**静的 JSON ファイルの運用イメージ**
先にも記載のとおり、より具体的なファイル名として、 `latest.json` といった感じで、「最新は何か？」というものだけを扱うように設定していくのがこのアップデートコンフィグの設定用途。
「**必ず最新だけを確認できるようにする**」という認識でいると理解しやすい。
:::

#### 仕様

| キー | 説明 |
|:-:|:-|
| version | バージョン名。セマンティックバージョン表記のみに対応 Ex: `1.0.0`、`v1.0.0` |
| platforms |プラットフォームの記載 `OS`-`ARCH` という感じに、ハイフンでつないで記載する。例: `windows-x86_64`|
| url | アップデーターバンドル (生成された .zip ファイルなど) への URL。 |
| signature | ビルド時に生成された署名。同じバージョンであってもビルド毎に変動するので注意する。 |
| notes | パブリッシュノート。日本語などのマルチバイト文字も入力可。 |
| pub_date | 公開日を [RFC 3339(https://datatracker.ietf.org/doc/html/rfc3339#section-5.8) 形式で記載する。 Ex: 2024-05-19T20:04:01Z |


このアップデーター用静的 JSON ファイルを用意する際には必須項目がある。
上記を用いて、

* `version`
* `platforms.[target].url`
* `platforms.[target].signature`

は必須項目となるので埋めておく。


#### シンプルな具体例

これについてシンプルな具体例を作成してみる。
例えば、以下のファイルを S3 など、アプリの実行者から読める場所に配置する。

```json
{
	"version": "1.3.3",
	"notes": "新規機能の実装",
	"pub_date": "2024-05-19T20:04:01Z",
	"platforms": {
		"windows-x86_64": {
			"signature": "<ビルド時に発行された署名>",
			"url": ".../Gaia_0.27.0_x64-setup.nsis.zip"
		}
	}
}

```

### ダイナミックアップデートサーバーを使用する

:::note info
**ダイナミックアップデートサーバーを利用するメリット**
ダイナミックアップデーターを使用する場合、例えば、v1.0.5 をリリースした場合、そのバージョンに致命的なエラーがあったとする。その場合に v1.0.4 などのバージョンへの**ロールバックを促したい場合などに対応できる**。
:::

:::note warn
こちらはまた機会があったら別途まとめる。
サーバー設定とかもろもろ書いてるとかなりの規模になりそうなので。
:::


# アップデーターの利用

## アップデーターの利用タイミング

アップデーターが走る・走らせるタイミングとしては以下のようなケースがある。

* 起動時
* アップデート確認ボタンを押したとき

この中で「起動時」に関しては、デフォルトのアップデートダイアログを表示する設定だと必ず走るようになる。

```json:tauri.config.json
{
  "tauri": {
    "updater": {
      "dialog": true
    }
  }
}
```

## アップデーターダイアログのカスタマイズ

アプリのデザインに合わせてアップデートダイアログをカスタムしたい場合は、デフォルトのダイアログを切って実装することもできる。

そのためにはまずはコンフィグでダイアログの表示をオフにする。

```json:tauri.config.json
{
  "tauri": {
    "updater": {
      "dialog": false
    }
  }
}
```

### 起動時のアップデート確認

例えば Svelte の場合、トップレベルの `+layout.svelte` ファイルに次のようなスクリプトを埋め込む。

```svelte:+layout.svelte
<script lang="ts">
    import {
        checkUpdate,
    } from '@tauri-apps/api/updater'

    onMount(() => {
		checkUpdate()
	});
<script>
```

### アップデート確認ボタン

```svelte:+layout.svelte
<script lang="ts">
    import {
        checkUpdate,
    } from '@tauri-apps/api/updater'

    onMount(() => {
		checkUpdate()
	});
<script>

<button>

```

# Tuari v2 への対応

v2 では、Tauri コマンドでのアップデーター追加が行えるようになっている。

https://v2.tauri.app/plugin/updater/

ベースとなる部分は基本的に v1 と変わらないが、v2 と v1 への継続サポートということで少しだけオプションが追加されている。

その一つが、updater コンフィグあとリビューとの中に [`createUpdaterArtifacts`](https://v2.tauri.app/reference/config/#createupdaterartifacts) アトリビュートを加えるというもの。

v2 のみの場合は以下の様に書くが、

```json:v2
{
  "bundle": {
    "createUpdaterArtifacts": true
  }
}
```

v1 の Updater と互換性を保つ場合は以下の様に指定する。

```json:v1Compatible
{
  "bundle": {
    "createUpdaterArtifacts": "v1Compatible"
  }
}
```


# トラブルシュート

どんだけ慎重に進めていてもトラブルはつきもの。
次の項目は良く遭遇すると思われるので、一応まとめる。

## `Error Invalid byte 46, offset 6.`

他にも `Error Encoded text cannot have a 6-bit remainder.` といったエラーも発生する。

### 原因

以下の原因の可能性がある。

* プライベートキーのパスを間違えている
    * これの可能性が高い
    * 特に Windows の場合、パスの扱いをミスりやすいので凝りうる
* パブリックキーとプライベートキーのペアが一致しない
    * 間違えてはいけないのが、 `tauri.config.json` に記載するのは、**パブリックキーのファイルの中の文字列 (ハッシュ値)**。プライベートキーはパスで指定するので、間違えてパブリックキーもパスで指定してしまうと行った事が起こると同様のエラーが発生する

### 解決方法

* プライベートキーへのパスを正しく入力する
    * Windows の場合、ミスりやすいので気を付けよう
    * 先の通り、VSCode などで管理し、必ず指定のパスでビルド時に環境変数が設定されるようにする方がミスらなくていい
* パブリックキーとプライベートキーのペアが一致しない
    * こちらの問題は、正しく入力しかない