Contents — 6 sections

Windows用のUnityスモーク試験runnerを作っていた。

やることは単純。

対象branchをworktreeへ展開し、Unityをbatch modeで起動し、smoke.txt と error CS を拾う。

Script自体のロジックは大したことがない。

でもWindows PowerShell 5.1で実行すると壊れた。

原因はC#でもUnityでもなかった。

日本語を含む .ps1 の文字コードだった。

BOMなしUTF-8をそのまま読んでくれなかった

対象は tools/windows/run-smoke.ps1。

Script内には、日本語を含むコメントや文字列があった。

開発側ではUTF-8として普通に見えている。

ところが当時のWindows PowerShell 5.1環境では、BOMなしUTF-8の日本語部分がUTF-8として認識されず、システム既定のANSIコードページ側として解釈された。

その結果、文字列境界などが崩れ、最終的には構文エラーになる。

見た目は、

「PowerShell Scriptの文法がおかしい」

なのに、実際に直す場所はロジックではなくencodingだった。

対応はBOM付きUTF-8へ固定

対応は .ps1 をUTF-8 with BOMで保存すること。

さらに人間のEditor設定任せにしないため、.editorconfig 側でもPowerShell fileのencodingを固定した。

狙いは、

「このPCではたまたま動く」

を避けること。

Windows側へhandoffするScriptなら、実行環境がWindows PowerShell 5.1である可能性も含めて、保存形式をRepositoryへ残す。

なぜ気づきにくかったか

この問題が面倒なのは、コードレビューではほぼ正常に見えること。

GitHub上でもEditor上でも日本語は読める。

差分も正しい。

構文も正しい。

でも「そのRuntimeがファイルをどうdecodeするか」だけが違う。

つまりSource Codeの見た目と、Runtimeが読むbyte列の解釈が一致していなかった。

Windowsのhandoffでは「Script本体」以外も仕様

今回のrunnerでは、

  • branchをworktree展開
  • Unity batchmode
  • smoke result
  • error CS 抽出

を自動化した。

こういうhandoff用Scriptでは、実行コマンドだけ書けば十分に見える。

でも実際には、

  • PowerShell version
  • encoding
  • line ending
  • execution policy
  • path
  • installed dependency

も再現性の一部。

今回壊れたのは、そのうちencodingだった。

「UTF-8ならOK」と一括りにしない

ここでの話は、

「PowerShellはUTF-8がダメ」

という意味ではない。

PowerShellの世代や既定encodingは違う。

今回問題になったのは、Windows PowerShell 5.1という実行環境と、BOMなしUTF-8の日本語入りScriptの組み合わせだった。

だから対策も、その環境を対象にBOM付きUTF-8へ固定した。

いまの結論

障害の見た目が「構文エラー」でも、原因が構文とは限らない。

特にWindowsの古いShellへScriptをhandoffするときは、

文字コードも依存関係として固定する。

UnityやVRChatのスモーク試験を自動化しているのに、試験runner自身がencodingで壊れるのはかなり間抜けだった。

でも、こういう一回踏めば二度と忘れない失敗は、短いKnowledgeとして残す価値がある。