特定ホスト指定のリセット処理を修正し、デプロイ完了後のロック解除失敗を防止
kamal upgrade 実行時に、特定ホストの指定が適切にクリアされず、デプロイ完了後のロック解除が失敗する問題が修正されました。with_specific_hosts を使用した後に環境変数が残り続けることで、誤ったホストに対してロック解除を試みていたことが原因です。
背景
with_specific_hosts メソッドは、特定のホストに対してのみ操作を実行するために一時的にホスト指定を変更する仕組みです。しかし、このメソッド実行後に specific_hosts や specific_roles に nil を渡しても、内部的に保持されている変数が適切にクリアされていませんでした。
この問題は特に kamal upgrade のような長時間実行される処理で顕著に現れます。処理の途中で with_specific_hosts を使用すると、その後の処理で意図しないホストが選択され続け、最終的なロック解除処理が失敗する原因となっていました。
技術的な変更
lib/kamal/commander.rb の specific_roles= と specific_hosts= メソッドが修正され、nil が渡された場合にインスタンス変数を確実にクリアするようになりました。
変更前:
def specific_hosts=(hosts)
@specifics = nil
if hosts.present?
@specific_hosts = Kamal::Utils.filter_specific_items(hosts, config.all_hosts)
if @specific_hosts.empty?
raise ArgumentError, "No --hosts match for #{hosts.join(',')}"
end
@specific_hosts
end
end
変更後:
def specific_hosts=(hosts)
@specifics = nil
@specific_hosts = if hosts.present?
filtered = Kamal::Utils.filter_specific_items(hosts, config.all_hosts)
raise ArgumentError, "No --hosts match for #{hosts.join(',')}" if filtered.empty?
filtered
end
end
変更前の実装では、hosts が nil や空の配列の場合、if hosts.present? の条件が偽となり、メソッドが何も返さずに終了していました。この結果、@specific_hosts インスタンス変数は以前の値を保持し続けていました。変更後は、条件式全体を代入式に組み込むことで、hosts.present? が偽の場合に @specific_hosts へ明示的に nil が代入されます。
テストによる動作保証
test/commander_test.rb に3つのテストケースが追加され、with_specific_hosts ブロックの前後で primary_host が正しく復元されることが保証されました。
test "with_specific_hosts restores primary_host after block" do
original_primary = @kamal.primary_host
assert_equal "1.1.1.1", original_primary
@kamal.with_specific_hosts("1.1.1.3") do
assert_equal "1.1.1.3", @kamal.primary_host
end
assert_equal original_primary, @kamal.primary_host
end
このテストは、単一ホスト指定の場合の復元を確認します。さらに、複数ホストをイテレーションする場合と、ブロック内で例外が発生した場合の復元も検証されています。特に例外発生時のテストは、ensure ブロックによるクリーンアップが正しく機能することを保証する重要なケースです。
設計判断
条件式を代入式に組み込む方式 が採用されました。
従来の if 文による条件分岐では、条件が偽の場合にメソッドが暗黙的に nil を返すものの、その戻り値が変数代入に使用されていませんでした。修正後は、三項演算子的な if 式を使用することで、どちらの分岐でも必ず代入が発生する構造になっています。
この変更により、コードの行数が削減されただけでなく、「設定のリセット」という意図がより明確に表現されています。@specific_hosts = nil という代入が明示的に行われることで、将来的なバグの混入リスクも低減します。
本PRは、環境変数のライフサイクル管理における微妙なバグを修正した変更です。nil 代入を明示的に行う構造に変更することで、一時的なホスト指定が確実にクリアされ、kamal upgrade のような複雑な処理フローでも正確なロック解除が保証されるようになりました。