zudo-tauri-wisdom

Type to search...

to open search from anywhere

macOS デプロイの落とし穴

作成2026年3月29日更新2026年4月1日Takeshi Takatsudo

微妙でデバッグが困難な問題を引き起こす macOS 固有のデプロイ上の問題

macOS デプロイの落とし穴

このページでは、Tauri の macOS デプロイにおいて最もフラストレーションの溜まるデバッグ問題を記録する。ビルド後に最新のコード変更が反映されない理由を探して何時間も費やしている場合、ほぼ確実にこれが原因である。

最大の落とし穴:cp -rf は .app バンドルに対して壊れている

⚠️ Warning

既存の .app バンドルを上書きするために cp -rf を使用してはならない。必ず古い .app を削除してからコピーすること。

# 間違い - 内部のバイナリが古いまま残る
cp -rf target/release/bundle/macos/MyApp.app /Applications/

# 正解 - 先に削除してからコピー
rm -rf /Applications/MyApp.app
cp -r target/release/bundle/macos/MyApp.app /Applications/

なぜこれが起こるのか

.app バンドルはディレクトリ構造である。

MyApp.app/
  Contents/
    MacOS/
      MyApp              <-- 実際のバイナリ
    Resources/
      ...
    Info.plist

既存の .app の上に新しい .appcp -rf すると、macOS の cp コマンドは予期しない動作をする。

  • 新しいファイルと変更されたファイルを既存のディレクトリにコピーする
  • しかし、Contents/MacOS/ 内のバイナリが置き換えられない場合がある。現在使用中であるか、カーネルにキャッシュされているか、特定のファイルフラグが設定されている場合に発生する
  • その結果、.app バンドルは新しく見える(新しい Info.plist、新しいリソース)が、実際の実行ファイルは古いバージョンのままとなる

古いコードが新しいメタデータと共に実行される状態となり、非常に混乱する動作が発生する。変更が反映されず、ログには正しいバージョンが表示されるのに、動作が間違っているという状況に陥る。

正しい手順

常に以下の順序で実行すること。

# 1. Kill the running app (if any)
killall MyApp 2>/dev/null || true

# 2. Wait briefly for process to exit
sleep 1

# 3. Move old bundle away (not rm -- mv is atomic via rename())
mv /Applications/MyApp.app /tmp/MyApp-old-$$.app 2>/dev/null || true

# 4. Copy the fresh bundle
cp -R target/release/bundle/macos/MyApp.app /Applications/

# 5. Clear quarantine (targeted: only removes quarantine, not all xattrs)
xattr -dr com.apple.quarantine /Applications/MyApp.app

📝 Note

ステップ 3 は rm -rf ではなく mv を使用する。同じファイルシステム上では、mvrename() を呼び出しアトミックに実行される — 古いバンドルは部分的な状態を残さず一回の操作で消える。rm -rf は中断された場合に部分的に削除されたバンドルが残る可能性がある。

ステップ 5 は xattr -cr ではなく xattr -dr com.apple.quarantine を使用する。-dr フラグは検疫属性のみを再帰的に削除し、-cr はすべての拡張属性を削除する。ローカル開発では、対象を絞ったアプローチの方が安全である。

バイナリが最新であることの確認

インストール後、バイナリのタイムスタンプがビルドと一致していることを確認する。

# Check when the binary was last modified
stat -f "%Sm" /Applications/MyApp.app/Contents/MacOS/MyApp

# Compare with the source
stat -f "%Sm" target/release/bundle/macos/MyApp.app/Contents/MacOS/MyApp

両方のタイムスタンプが同一(または数秒以内の差)であるべきである。/Applications/ のバイナリが古いタイムスタンプを持つ場合、コピーが正しく行われていない。

💡 Tip

誤って cp -rf を使用しないよう、インストール手順をシェルエイリアスまたはスクリプトにしておくとよい。

# ~/.zshrc
install-tauri() {
  local app_name="${1:?Usage: install-tauri AppName}"
  killall "$app_name" 2>/dev/null || true
  sleep 1
  mv "/Applications/${app_name}.app" "/tmp/${app_name}-old-$$.app" 2>/dev/null || true
  cp -R "target/release/bundle/macos/${app_name}.app" /Applications/
  xattr -dr com.apple.quarantine "/Applications/${app_name}.app"
  echo "Installed ${app_name}.app"
}

その他の macOS の問題

App Translocation

macOS は、特定の場所(ダウンロードフォルダなど)から初めて開かれたアプリを「トランスロケート」することがある。トランスロケーションはアプリをランダムな一時ディレクトリにコピーしてそこから実行するため、相対パスが壊れる。

検疫属性を xattr -cr でクリアすると、これを防止できる。

xattr -cr /Applications/MyApp.app

Launch Services キャッシュ

macOS はアプリ登録、ドキュメントタイプ、URL ハンドラ、アイコンを追跡するデータベース(Launch Services)を管理している。同じパスで .app バンドルを置き換えた場合、アプリの変更時刻が新しくないと自動的に再登録されないため、Launch Services が古いメタデータ(古いアイコン、古いドキュメントタイプ関連付け)を表示し続ける場合がある。

正しくインストールした後に Finder が古いアプリ情報を表示する場合:

# Force re-register the app
touch /Applications/MyApp.app
/System/Library/Frameworks/CoreServices.framework/Frameworks/\
LaunchServices.framework/Support/lsregister -f /Applications/MyApp.app

# Nuclear option: rebuild the entire Launch Services database
/System/Library/Frameworks/CoreServices.framework/Frameworks/\
LaunchServices.framework/Support/lsregister \
  -kill -r -domain local -domain system -domain user
killall Finder

📝 Note

完全なデータベース再構築が必要になることは稀である。まず対象を絞った lsregister -f を試すこと。lsregister -kill -r はシステム全体のすべてのアプリ登録をリセットするため、再構築に時間がかかる場合がある。

コード署名の検証

.app バンドルを置き換えた後、コード署名が有効であることを確認できる:

# Verify code signature
codesign --verify --deep --strict --verbose=2 /Applications/MyApp.app

# Check Gatekeeper assessment (bypassing cache to get fresh result)
spctl --assess --type execute --verbose --ignore-cache /Applications/MyApp.app

--ignore-cache なしで spctl が一貫性のない結果を返す場合、Gatekeeper の評価キャッシュが古くなっている。--ignore-cache フラグは新しい評価を強制する。

Gatekeeper の警告

署名されていないアプリは Gatekeeper の警告を発生させる。開発時には以下の方法で回避できる。

  1. アプリを右クリックして「開く」を選択(ダブルクリックではなく)
  2. または拡張属性をクリア:xattr -cr /Applications/MyApp.app

配布用には、Apple の開発者プログラムを通じてアプリの署名と公証を行う必要がある。

バイナリコード署名

開発時であっても、Tauri が作成した .app バンドル内のバイナリを作成後に変更すると、コード署名が無効になる。macOS がアプリの実行を拒否したり、「破損している」というエラーを表示することがある。

正しくインストールした後にこの問題が発生した場合は、以下を試すこと。

codesign --remove-signature /Applications/MyApp.app
xattr -cr /Applications/MyApp.app

まとめチェックリスト

「変更が反映されない」と報告する前に、以下を確認すること。

  1. コピー前に古い .appmv または rm -rf で削除したか?(cp -rf ではない)
  2. 実行中のアプリを killall で終了させたか?
  3. ソースとインストール先のバイナリタイムスタンプは一致しているか?
  4. xattr -dr com.apple.quarantine で検疫フラグをクリアしたか?
  5. フロントエンドアセットが実際に埋め込まれていることを確認したか?(Cargo キャッシュを参照)
  6. Finder が古い情報を表示していないか?lsregister -f でアプリを再登録してみること