Skip to content

Commit d34942e

Browse files
authored
Remove old task SDK RC when creating RC (#59459)
The old task sdk releases should also be removed when creating a new RC. This commit ensures that and also added tests for the removal
1 parent 551ad63 commit d34942e

File tree

2 files changed

+207
-12
lines changed

2 files changed

+207
-12
lines changed

dev/breeze/src/airflow_breeze/commands/release_candidate_command.py

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -588,12 +588,13 @@ def push_release_candidate_tag_to_github(version, remote_name):
588588
console_print("[success]Release candidate tag pushed to GitHub")
589589

590590

591-
def remove_old_releases(version, repo_root):
591+
def remove_old_releases(version, task_sdk_version, repo_root):
592592
if not confirm_action("Do you want to look for old RCs to remove?"):
593593
return
594594

595595
os.chdir(f"{repo_root}/asf-dist/dev/airflow")
596596

597+
# Remove old Airflow releases
597598
old_releases = []
598599
for entry in os.scandir():
599600
if entry.name == version:
@@ -602,15 +603,38 @@ def remove_old_releases(version, repo_root):
602603
if entry.is_dir() and RC_PATTERN.match(entry.name):
603604
old_releases.append(entry.name)
604605
old_releases.sort()
605-
console_print(f"The following old releases should be removed: {old_releases}")
606+
console_print(f"The following old Airflow releases should be removed: {old_releases}")
606607
for old_release in old_releases:
607-
console_print(f"Removing old release {old_release}")
608+
console_print(f"Removing old Airflow release {old_release}")
608609
if confirm_action(f"Remove old RC {old_release}?"):
609610
run_command(["svn", "rm", old_release], check=True)
610611
run_command(
611612
["svn", "commit", "-m", f"Remove old release: {old_release}"],
612613
check=True,
613614
)
615+
616+
# Remove old Task SDK releases
617+
task_sdk_path = f"{repo_root}/asf-dist/dev/airflow/task-sdk"
618+
if os.path.exists(task_sdk_path):
619+
os.chdir(task_sdk_path)
620+
old_task_sdk_releases = []
621+
for entry in os.scandir():
622+
if entry.name == task_sdk_version:
623+
# Don't remove the current RC
624+
continue
625+
if entry.is_dir() and RC_PATTERN.match(entry.name):
626+
old_task_sdk_releases.append(entry.name)
627+
old_task_sdk_releases.sort()
628+
console_print(f"The following old Task SDK releases should be removed: {old_task_sdk_releases}")
629+
for old_release in old_task_sdk_releases:
630+
console_print(f"Removing old Task SDK release {old_release}")
631+
if confirm_action(f"Remove old Task SDK RC {old_release}?"):
632+
run_command(["svn", "rm", old_release], check=True)
633+
run_command(
634+
["svn", "commit", "-m", f"Remove old Task SDK release: {old_release}"],
635+
check=True,
636+
)
637+
614638
console_print("[success]Old releases removed")
615639
os.chdir(repo_root)
616640

@@ -758,7 +782,7 @@ def publish_release_candidate(
758782
push_artifacts_to_asf_repo(version, task_sdk_version, airflow_repo_root)
759783

760784
# Remove old releases
761-
remove_old_releases(version, airflow_repo_root)
785+
remove_old_releases(version, task_sdk_version, airflow_repo_root)
762786

763787
# Delete asf-dist directory
764788
delete_asf_repo(airflow_repo_root)

dev/breeze/tests/test_release_candidate_command.py

Lines changed: 179 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ def rc_cmd():
3939

4040
def 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

8290
def 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

106115
def 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

Comments
 (0)