DiffDaily

Deep & Concise - OSS変更の定点観測

[rails/thor] マージツール実行時のエンコーディングエラーを修正

rails/thor

背景

Thorでファイル操作を行う際、既存ファイルとの衝突が発生した場合にマージツールを起動できる機能があります。しかし、UTF-8文字を含むファイルに対してマージツールを実行すると、エンコーディングエラーが発生する問題がありました。この問題は #898 で報告されていた類似の問題と同じ根本原因によるものです。

技術的な変更内容

問題の原因

Thor::Shell::Basic#merge メソッドでは、衝突が発生した際に一時ファイルを作成してマージツールに渡していました。しかし、Tempfile.open の呼び出し時に binmode: true オプションが指定されていなかったため、Rubyのデフォルトエンコーディング設定によってはバイナリモードで開かれず、UTF-8などの特定の文字コードを含むファイルの処理時にエンコーディングエラーが発生していました。

修正内容

変更前:

Tempfile.open([File.basename(destination), File.extname(destination)], File.dirname(destination)) do |temp|
  temp.write content
  temp.rewind
  system(*merge_tool, temp.path, destination)
end

変更後:

Tempfile.open([File.basename(destination), File.extname(destination)], File.dirname(destination), binmode: true) do |temp|
  temp.write content
  temp.rewind
  system(*merge_tool, temp.path, destination)
end

binmode: true オプションを追加することで、一時ファイルが常にバイナリモードで開かれるようになり、エンコーディングの変換処理が行われなくなりました。これにより、元のファイル内容がそのまま保持され、UTF-8文字を含むファイルでもマージツールが正常に動作するようになります。

テストの追加

この修正を検証するため、UTF-8文字を含むファイルに対してマージツールを実行する新しいテストケースが追加されました。

it "launches the merge tool when there is a collision and source has utf-8 characters" do
  previous_internal = Encoding.default_internal

  silence_warnings do
    Encoding.default_internal = Encoding::UTF_8
  end

  destination = File.join(destination_root, "encoding_with_utf8.thor")
  FileUtils.mkdir_p(destination_root)

  File.write(destination, "blabla")

  allow(runner.shell).to receive(:merge_tool).and_return("meld")
  expect(Thor::LineEditor).to receive(:readline).and_return("m")
  expect(runner.shell).to receive(:system).with("meld", /encoding_with_utf8.thor/, /encoding_with_utf8.thor/)
  action :copy_file, "encoding_with_utf8.thor"
ensure
  silence_warnings do
    Encoding.default_internal = previous_internal
  end
end

このテストでは、Encoding.default_internal を一時的にUTF-8に設定し、UTF-8文字を含むファイルに対してマージツールが正常に呼び出されることを確認しています。

影響範囲

この変更により、以下のような状況でThorが正常に動作するようになります:

  • UTF-8などの非ASCII文字を含むファイルに対してマージツールを使用する場合
  • Encoding.default_internal が設定されている環境でのファイル操作
  • 国際化されたプロジェクトでのジェネレーター実行時

既存の動作に対する破壊的変更はなく、バイナリモードでの一時ファイル作成により、より安全で予測可能な動作が保証されます。