@@ -39,6 +39,7 @@ def rc_cmd():
3939
4040def test_remove_old_releases_only_collects_rc_directories (monkeypatch , rc_cmd ):
4141 version = "2.10.0rc3"
42+ task_sdk_version = "1.0.6rc3"
4243 repo_root = "/repo/root"
4344
4445 # Arrange: entries include current RC, old RC directories, a matching "file", and non-RC directory.
@@ -59,28 +60,36 @@ def fake_confirm_action(prompt: str, **_kwargs) -> bool:
5960 if prompt == "Do you want to look for old RCs to remove?" :
6061 return True
6162 # For each candidate, we decline removal to avoid running svn commands.
62- if prompt .startswith ("Remove old RC " ):
63+ if prompt .startswith ("Remove old RC " ) or prompt . startswith ( "Remove old Task SDK RC " ) :
6364 return False
6465 raise AssertionError (f"Unexpected confirm prompt: { prompt } " )
6566
67+ def fake_path_exists (path : str ) -> bool :
68+ # Task SDK path doesn't exist in this test
69+ return False
70+
6671 monkeypatch .setattr (rc_cmd .os , "chdir" , lambda path : chdir_calls .append (path ))
6772 monkeypatch .setattr (rc_cmd .os , "scandir" , lambda : iter (entries ))
73+ monkeypatch .setattr (rc_cmd .os .path , "exists" , fake_path_exists )
6874 monkeypatch .setattr (rc_cmd , "confirm_action" , fake_confirm_action )
6975 monkeypatch .setattr (rc_cmd , "console_print" , lambda msg = "" : console_messages .append (str (msg )))
7076 monkeypatch .setattr (rc_cmd , "run_command" , lambda cmd , ** _kwargs : run_command_calls .append (cmd ))
7177
7278 # Act
73- rc_cmd .remove_old_releases (version = version , repo_root = repo_root )
79+ rc_cmd .remove_old_releases (version = version , task_sdk_version = task_sdk_version , repo_root = repo_root )
7480
7581 # Assert: only directory entries matching RC_PATTERN, excluding current version, and sorted.
7682 assert f"{ repo_root } /asf-dist/dev/airflow" in chdir_calls
7783 assert repo_root in chdir_calls
78- assert "The following old releases should be removed: ['2.10.0rc1', '2.10.0rc2']" in console_messages
84+ assert (
85+ "The following old Airflow releases should be removed: ['2.10.0rc1', '2.10.0rc2']" in console_messages
86+ )
7987 assert run_command_calls == []
8088
8189
8290def test_remove_old_releases_returns_early_when_user_declines (monkeypatch , rc_cmd ):
8391 version = "2.10.0rc3"
92+ task_sdk_version = "1.0.6rc3"
8493 repo_root = "/repo/root"
8594
8695 confirm_prompts : list [str ] = []
@@ -98,13 +107,14 @@ def should_not_be_called(*_args, **_kwargs):
98107 monkeypatch .setattr (rc_cmd , "console_print" , should_not_be_called )
99108 monkeypatch .setattr (rc_cmd , "run_command" , should_not_be_called )
100109
101- rc_cmd .remove_old_releases (version = version , repo_root = repo_root )
110+ rc_cmd .remove_old_releases (version = version , task_sdk_version = task_sdk_version , repo_root = repo_root )
102111
103112 assert confirm_prompts == ["Do you want to look for old RCs to remove?" ]
104113
105114
106115def test_remove_old_releases_removes_confirmed_old_releases (monkeypatch , rc_cmd ):
107116 version = "3.1.5rc3"
117+ task_sdk_version = "1.0.6rc3"
108118 repo_root = "/repo/root"
109119
110120 # Unsorted on purpose to verify sorting before prompting/removing.
@@ -129,8 +139,13 @@ def fake_confirm_action(prompt: str, **_kwargs) -> bool:
129139 return False
130140 raise AssertionError (f"Unexpected confirm prompt: { prompt } " )
131141
142+ def fake_path_exists (path : str ) -> bool :
143+ # Task SDK path doesn't exist in this test
144+ return False
145+
132146 monkeypatch .setattr (rc_cmd .os , "chdir" , lambda path : chdir_calls .append (path ))
133147 monkeypatch .setattr (rc_cmd .os , "scandir" , lambda : iter (entries ))
148+ monkeypatch .setattr (rc_cmd .os .path , "exists" , fake_path_exists )
134149 monkeypatch .setattr (rc_cmd , "confirm_action" , fake_confirm_action )
135150 monkeypatch .setattr (rc_cmd , "console_print" , lambda msg = "" : console_messages .append (str (msg )))
136151
@@ -139,21 +154,177 @@ def fake_run_command(cmd: list[str], **kwargs):
139154
140155 monkeypatch .setattr (rc_cmd , "run_command" , fake_run_command )
141156
142- rc_cmd .remove_old_releases (version = version , repo_root = repo_root )
157+ rc_cmd .remove_old_releases (version = version , task_sdk_version = task_sdk_version , repo_root = repo_root )
143158
144159 assert chdir_calls == [f"{ repo_root } /asf-dist/dev/airflow" , repo_root ]
145160 assert confirm_prompts == [
146161 "Do you want to look for old RCs to remove?" ,
147162 "Remove old RC 3.1.0rc1?" ,
148163 "Remove old RC 3.1.5rc2?" ,
149164 ]
150- assert "The following old releases should be removed: ['3.1.0rc1', '3.1.5rc2']" in console_messages
151- assert "Removing old release 3.1.0rc1" in console_messages
152- assert "Removing old release 3.1.5rc2" in console_messages
165+ assert (
166+ "The following old Airflow releases should be removed: ['3.1.0rc1', '3.1.5rc2']" in console_messages
167+ )
168+ assert "Removing old Airflow release 3.1.0rc1" in console_messages
169+ assert "Removing old Airflow release 3.1.5rc2" in console_messages
153170 assert "[success]Old releases removed" in console_messages
154171
155172 # Only rc1 was confirmed, so we should run rm+commit for rc1 only.
156173 assert run_command_calls == [
157174 (["svn" , "rm" , "3.1.0rc1" ], {"check" : True }),
158175 (["svn" , "commit" , "-m" , "Remove old release: 3.1.0rc1" ], {"check" : True }),
159176 ]
177+
178+
179+ def test_remove_old_releases_removes_task_sdk_releases (monkeypatch , rc_cmd ):
180+ version = "3.1.5rc3"
181+ task_sdk_version = "1.0.6rc3"
182+ repo_root = "/repo/root"
183+
184+ # Airflow entries
185+ airflow_entries = [
186+ FakeDirEntry (version , is_dir = True ),
187+ FakeDirEntry ("3.1.5rc2" , is_dir = True ),
188+ ]
189+
190+ # Task SDK entries
191+ task_sdk_entries = [
192+ FakeDirEntry (task_sdk_version , is_dir = True ), # current RC: should be skipped
193+ FakeDirEntry ("1.0.6rc2" , is_dir = True ), # old RC dir: should be included
194+ FakeDirEntry ("1.0.6rc1" , is_dir = True ), # old RC dir: should be included
195+ ]
196+
197+ chdir_calls : list [str ] = []
198+ console_messages : list [str ] = []
199+ run_command_calls : list [tuple [list [str ], dict ]] = []
200+ confirm_prompts : list [str ] = []
201+ scandir_call_count = 0
202+
203+ def fake_confirm_action (prompt : str , ** _kwargs ) -> bool :
204+ confirm_prompts .append (prompt )
205+ if prompt == "Do you want to look for old RCs to remove?" :
206+ return True
207+ # Decline all removals to avoid running svn commands
208+ if prompt .startswith ("Remove old RC " ) or prompt .startswith ("Remove old Task SDK RC " ):
209+ return False
210+ raise AssertionError (f"Unexpected confirm prompt: { prompt } " )
211+
212+ def fake_path_exists (path : str ) -> bool :
213+ # Task SDK path exists in this test
214+ return path == f"{ repo_root } /asf-dist/dev/airflow/task-sdk"
215+
216+ def fake_scandir ():
217+ nonlocal scandir_call_count
218+ scandir_call_count += 1
219+ # First call is for Airflow, second is for Task SDK
220+ if scandir_call_count == 1 :
221+ return iter (airflow_entries )
222+ if scandir_call_count == 2 :
223+ return iter (task_sdk_entries )
224+ raise AssertionError ("Unexpected scandir call" )
225+
226+ monkeypatch .setattr (rc_cmd .os , "chdir" , lambda path : chdir_calls .append (path ))
227+ monkeypatch .setattr (rc_cmd .os , "scandir" , fake_scandir )
228+ monkeypatch .setattr (rc_cmd .os .path , "exists" , fake_path_exists )
229+ monkeypatch .setattr (rc_cmd , "confirm_action" , fake_confirm_action )
230+ monkeypatch .setattr (rc_cmd , "console_print" , lambda msg = "" : console_messages .append (str (msg )))
231+ monkeypatch .setattr (rc_cmd , "run_command" , lambda cmd , ** _kwargs : run_command_calls .append ((cmd , {})))
232+
233+ rc_cmd .remove_old_releases (version = version , task_sdk_version = task_sdk_version , repo_root = repo_root )
234+
235+ assert f"{ repo_root } /asf-dist/dev/airflow" in chdir_calls
236+ assert f"{ repo_root } /asf-dist/dev/airflow/task-sdk" in chdir_calls
237+ assert repo_root in chdir_calls
238+ assert "The following old Airflow releases should be removed: ['3.1.5rc2']" in console_messages
239+ assert (
240+ "The following old Task SDK releases should be removed: ['1.0.6rc1', '1.0.6rc2']" in console_messages
241+ )
242+ assert "[success]Old releases removed" in console_messages
243+ # No removals were confirmed, so no svn commands should be run
244+ assert run_command_calls == []
245+
246+
247+ def test_remove_old_releases_removes_both_airflow_and_task_sdk_releases (monkeypatch , rc_cmd ):
248+ version = "3.1.5rc3"
249+ task_sdk_version = "1.0.6rc3"
250+ repo_root = "/repo/root"
251+
252+ # Airflow entries
253+ airflow_entries = [
254+ FakeDirEntry (version , is_dir = True ),
255+ FakeDirEntry ("3.1.5rc2" , is_dir = True ),
256+ ]
257+
258+ # Task SDK entries
259+ task_sdk_entries = [
260+ FakeDirEntry (task_sdk_version , is_dir = True ),
261+ FakeDirEntry ("1.0.6rc2" , is_dir = True ),
262+ FakeDirEntry ("1.0.6rc1" , is_dir = True ),
263+ ]
264+
265+ chdir_calls : list [str ] = []
266+ console_messages : list [str ] = []
267+ run_command_calls : list [tuple [list [str ], dict ]] = []
268+ confirm_prompts : list [str ] = []
269+ scandir_call_count = 0
270+
271+ def fake_confirm_action (prompt : str , ** _kwargs ) -> bool :
272+ confirm_prompts .append (prompt )
273+ if prompt == "Do you want to look for old RCs to remove?" :
274+ return True
275+ # Confirm removal of one Airflow and one Task SDK release
276+ if prompt == "Remove old RC 3.1.5rc2?" :
277+ return True
278+ if prompt == "Remove old Task SDK RC 1.0.6rc1?" :
279+ return True
280+ # Decline others
281+ if prompt .startswith ("Remove old RC " ) or prompt .startswith ("Remove old Task SDK RC " ):
282+ return False
283+ raise AssertionError (f"Unexpected confirm prompt: { prompt } " )
284+
285+ def fake_path_exists (path : str ) -> bool :
286+ return path == f"{ repo_root } /asf-dist/dev/airflow/task-sdk"
287+
288+ def fake_scandir ():
289+ nonlocal scandir_call_count
290+ scandir_call_count += 1
291+ if scandir_call_count == 1 :
292+ return iter (airflow_entries )
293+ if scandir_call_count == 2 :
294+ return iter (task_sdk_entries )
295+ raise AssertionError ("Unexpected scandir call" )
296+
297+ monkeypatch .setattr (rc_cmd .os , "chdir" , lambda path : chdir_calls .append (path ))
298+ monkeypatch .setattr (rc_cmd .os , "scandir" , fake_scandir )
299+ monkeypatch .setattr (rc_cmd .os .path , "exists" , fake_path_exists )
300+ monkeypatch .setattr (rc_cmd , "confirm_action" , fake_confirm_action )
301+ monkeypatch .setattr (rc_cmd , "console_print" , lambda msg = "" : console_messages .append (str (msg )))
302+
303+ def fake_run_command (cmd : list [str ], ** kwargs ):
304+ run_command_calls .append ((cmd , kwargs ))
305+
306+ monkeypatch .setattr (rc_cmd , "run_command" , fake_run_command )
307+
308+ rc_cmd .remove_old_releases (version = version , task_sdk_version = task_sdk_version , repo_root = repo_root )
309+
310+ assert chdir_calls == [
311+ f"{ repo_root } /asf-dist/dev/airflow" ,
312+ f"{ repo_root } /asf-dist/dev/airflow/task-sdk" ,
313+ repo_root ,
314+ ]
315+ assert "The following old Airflow releases should be removed: ['3.1.5rc2']" in console_messages
316+ assert (
317+ "The following old Task SDK releases should be removed: ['1.0.6rc1', '1.0.6rc2']" in console_messages
318+ )
319+ assert "Removing old Airflow release 3.1.5rc2" in console_messages
320+ assert "Removing old Task SDK release 1.0.6rc1" in console_messages
321+ assert "Removing old Task SDK release 1.0.6rc2" in console_messages
322+ assert "[success]Old releases removed" in console_messages
323+
324+ # Both Airflow and Task SDK removals were confirmed
325+ assert run_command_calls == [
326+ (["svn" , "rm" , "3.1.5rc2" ], {"check" : True }),
327+ (["svn" , "commit" , "-m" , "Remove old release: 3.1.5rc2" ], {"check" : True }),
328+ (["svn" , "rm" , "1.0.6rc1" ], {"check" : True }),
329+ (["svn" , "commit" , "-m" , "Remove old Task SDK release: 1.0.6rc1" ], {"check" : True }),
330+ ]
0 commit comments