← ./articles-ja

Tauri v2 Updaterをlocal mock serverで検証する

Tauri updaterのテストは、静かに失敗することがあります。アプリは起動する、update bannerは出ない、UI上のerrorも薄い。その状態だけでは、updater codeが間違っているとは言えません。

local testでは、署名済みupdater artifact、有効なupdater JSON、WebViewからfetchできるmock server、local HTTPを許可するtest-only設定の4つが揃っている必要があります。

この記事では、UIの見た目から推測するのではなく、request logでどこまで進んだかを確認する手順を整理します。

症状: update checkしてもbannerもerrorも出ない

典型的な失敗はこうです。

  • frontendまたはRust側でcheck()は実行される
  • アプリは現在versionのまま
  • update bannerが出ない
  • mock serverにrequestが来ない、または1回だけ来る
  • production updater endpointは関係していない

原因はいくつかあります。artifactが署名されていない、JSONのsignatureが違う、WebViewがCORSでmock serverを拒否している、HTTP endpointをTauriが拒否している、などです。

UIを書き換える前に、requestがどこで止まっているかを証明します。

先に署名済みupdater artifactを作る

Tauri updaterのinstallには署名が必要です。通常のinstallerだけでは、現実的なupdater testになりません。

updater artifactを作るrelease build commandを実行します。

npm run tauri -- build --features updater --config src-tauri/tauri.updater.conf.json

次に、期待するsignatureがあるか確認します。

Get-ChildItem src-tauri\target\release\bundle\nsis -Filter *.sig

.sigファイルがないなら、そこで止めます。mock serverでは欠落したupdater artifactは直せません。

updater JSONとinstallerをlocal mockで配信する

各requestをlogに出す小さなserverを使います。logが最初の証拠です。

const http = require("node:http");
const fs = require("node:fs");
const path = require("node:path");

const port = 8899;
const bundleDir = path.join("src-tauri", "target", "release", "bundle", "nsis");
const installerName = "my-app_1.0.1_x64-setup.exe";
const signatureName = `${installerName}.sig`;

const server = http.createServer((req, res) => {
  console.log(`${req.method} ${req.url}`);

  res.setHeader("Access-Control-Allow-Origin", "*");
  res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
  res.setHeader("Access-Control-Allow-Headers", "Content-Type");

  if (req.method === "OPTIONS") {
    res.writeHead(204);
    res.end();
    return;
  }

  if (req.url === "/updater.json") {
    const signature = fs
      .readFileSync(path.join(bundleDir, signatureName), "utf8")
      .trim();

    res.writeHead(200, { "Content-Type": "application/json" });
    res.end(JSON.stringify({
      version: "1.0.1",
      notes: "Local updater test",
      pub_date: new Date().toISOString(),
      platforms: {
        "windows-x86_64": {
          signature,
          url: `http://127.0.0.1:${port}/${installerName}`
        }
      }
    }));
    return;
  }

  if (req.url === `/${installerName}`) {
    const installerPath = path.join(bundleDir, installerName);
    res.writeHead(200, {
      "Content-Type": "application/octet-stream",
      "Content-Length": fs.statSync(installerPath).size
    });
    fs.createReadStream(installerPath).pipe(res);
    return;
  }

  res.writeHead(404);
  res.end("not found");
});

server.listen(port, "127.0.0.1", () => {
  console.log(`Updater mock server listening on http://127.0.0.1:${port}`);
});

CORS headerは飾りではありません。updater requestはWebView runtimeから行われます。curlでは読めるlocal serverでも、browser layerが拒否すればアプリ内では失敗します。

HTTP用のtest configを一時的に追加する

productionのupdater endpointはHTTPSにするべきです。local mock serverだけ、test-only configでHTTPを許可します。

{
  "plugins": {
    "updater": {
      "endpoints": ["http://127.0.0.1:8899/updater.json"],
      "dangerousInsecureTransportProtocol": true
    }
  }
}

この設定をproduction release configに入れないでください。名前の通り、insecure transportを許可します。local testには便利ですが、実配布には不適切です。

繰り返し使うなら、ファイル名も明確にします。

src-tauri/tauri.updater.local-test.conf.json

build toolが複数configをmergeできるなら、updater overlayとlocal endpoint overlayを両方適用します。できない場合は、local test用のcombined configを作ります。

request、download、install、relaunchを順に確認する

まずmanifest requestを確認します。

GET /updater.json

mock serverにこのrequestが来ないなら、アプリはendpointに到達していません。endpoint config、feature gate、plugin initialization、HTTP許可を確認します。

次にinstaller requestを確認します。

GET /my-app_1.0.1_x64-setup.exe

manifest requestだけ来る場合は、JSON fieldを見ます。

  • versionがinstalled app versionより新しい
  • platforms.windows-x86_64.urlが配信しているinstallerまたはupdater bundleを指している
  • platforms.windows-x86_64.signature.sigファイルの中身が入っている
  • mock serverが200を返している

最後にinstall behaviorを確認します。Windowsでは、install stepでアプリが終了することがあります。UIが消えたから失敗とは限りません。relaunch後に新versionが起動するか確認します。

mock testとrelease configを分ける

よいupdater test setupには明確な境界があります。

  • production configはHTTPSを使う
  • local mock configだけHTTPを許す
  • production configではdangerousInsecureTransportProtocolを有効にしない
  • local mock serverはrequestをすべてlogする
  • test artifactは使い捨てにする

local endpointをrelease build用の設定ファイルに混ぜないでください。急いだrelease時に見逃しやすい危険になります。

troubleshooting checklist

アプリコードを変える前に、この順で見ます。

  1. buildで.sigファイルが生成されているか
  2. mock serverが起動し、/updater.jsonを返すか
  3. アプリが/updater.jsonをrequestしているか
  4. アプリがinstallerまたはupdater bundle URLをrequestしているか
  5. JSONのversionがinstalled app versionより新しいか
  6. signature fieldはpathではなくfile contentか
  7. HTTP許可はlocal test configだけに入っているか
  8. install後にrelaunchまたは新version確認ができるか

この順番なら、missing signatureをUI bugと誤認したり、CORS失敗をupdater APIの問題と誤認したりしにくくなります。

参考

まとめ

local updater testでは、network path、manifest、署名済みartifact、install stepをそれぞれ証明します。CORS header付きmock serverを使い、HTTP許可はtest configだけに置き、UIより先にrequest logを確認します。