This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new d34942ef1fa Remove old task SDK RC when creating RC (#59459)
d34942ef1fa is described below

commit d34942ef1fa1b75285ef06f291c9a951cc274783
Author: Ephraim Anierobi <[email protected]>
AuthorDate: Tue Dec 16 02:18:46 2025 +0100

    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
---
 .../commands/release_candidate_command.py          |  32 +++-
 dev/breeze/tests/test_release_candidate_command.py | 187 ++++++++++++++++++++-
 2 files changed, 207 insertions(+), 12 deletions(-)

diff --git 
a/dev/breeze/src/airflow_breeze/commands/release_candidate_command.py 
b/dev/breeze/src/airflow_breeze/commands/release_candidate_command.py
index 37c67da5d55..2587ad04688 100644
--- a/dev/breeze/src/airflow_breeze/commands/release_candidate_command.py
+++ b/dev/breeze/src/airflow_breeze/commands/release_candidate_command.py
@@ -588,12 +588,13 @@ def push_release_candidate_tag_to_github(version, 
remote_name):
         console_print("[success]Release candidate tag pushed to GitHub")
 
 
-def remove_old_releases(version, repo_root):
+def remove_old_releases(version, task_sdk_version, repo_root):
     if not confirm_action("Do you want to look for old RCs to remove?"):
         return
 
     os.chdir(f"{repo_root}/asf-dist/dev/airflow")
 
+    # Remove old Airflow releases
     old_releases = []
     for entry in os.scandir():
         if entry.name == version:
@@ -602,15 +603,38 @@ def remove_old_releases(version, repo_root):
         if entry.is_dir() and RC_PATTERN.match(entry.name):
             old_releases.append(entry.name)
     old_releases.sort()
-    console_print(f"The following old releases should be removed: 
{old_releases}")
+    console_print(f"The following old Airflow releases should be removed: 
{old_releases}")
     for old_release in old_releases:
-        console_print(f"Removing old release {old_release}")
+        console_print(f"Removing old Airflow release {old_release}")
         if confirm_action(f"Remove old RC {old_release}?"):
             run_command(["svn", "rm", old_release], check=True)
             run_command(
                 ["svn", "commit", "-m", f"Remove old release: {old_release}"],
                 check=True,
             )
+
+    # Remove old Task SDK releases
+    task_sdk_path = f"{repo_root}/asf-dist/dev/airflow/task-sdk"
+    if os.path.exists(task_sdk_path):
+        os.chdir(task_sdk_path)
+        old_task_sdk_releases = []
+        for entry in os.scandir():
+            if entry.name == task_sdk_version:
+                # Don't remove the current RC
+                continue
+            if entry.is_dir() and RC_PATTERN.match(entry.name):
+                old_task_sdk_releases.append(entry.name)
+        old_task_sdk_releases.sort()
+        console_print(f"The following old Task SDK releases should be removed: 
{old_task_sdk_releases}")
+        for old_release in old_task_sdk_releases:
+            console_print(f"Removing old Task SDK release {old_release}")
+            if confirm_action(f"Remove old Task SDK RC {old_release}?"):
+                run_command(["svn", "rm", old_release], check=True)
+                run_command(
+                    ["svn", "commit", "-m", f"Remove old Task SDK release: 
{old_release}"],
+                    check=True,
+                )
+
     console_print("[success]Old releases removed")
     os.chdir(repo_root)
 
@@ -758,7 +782,7 @@ def publish_release_candidate(
     push_artifacts_to_asf_repo(version, task_sdk_version, airflow_repo_root)
 
     # Remove old releases
-    remove_old_releases(version, airflow_repo_root)
+    remove_old_releases(version, task_sdk_version, airflow_repo_root)
 
     # Delete asf-dist directory
     delete_asf_repo(airflow_repo_root)
diff --git a/dev/breeze/tests/test_release_candidate_command.py 
b/dev/breeze/tests/test_release_candidate_command.py
index 3070f9420a4..c92d7b0aad7 100644
--- a/dev/breeze/tests/test_release_candidate_command.py
+++ b/dev/breeze/tests/test_release_candidate_command.py
@@ -39,6 +39,7 @@ def rc_cmd():
 
 def test_remove_old_releases_only_collects_rc_directories(monkeypatch, rc_cmd):
     version = "2.10.0rc3"
+    task_sdk_version = "1.0.6rc3"
     repo_root = "/repo/root"
 
     # Arrange: entries include current RC, old RC directories, a matching 
"file", and non-RC directory.
@@ -59,28 +60,36 @@ def 
test_remove_old_releases_only_collects_rc_directories(monkeypatch, rc_cmd):
         if prompt == "Do you want to look for old RCs to remove?":
             return True
         # For each candidate, we decline removal to avoid running svn commands.
-        if prompt.startswith("Remove old RC "):
+        if prompt.startswith("Remove old RC ") or prompt.startswith("Remove 
old Task SDK RC "):
             return False
         raise AssertionError(f"Unexpected confirm prompt: {prompt}")
 
+    def fake_path_exists(path: str) -> bool:
+        # Task SDK path doesn't exist in this test
+        return False
+
     monkeypatch.setattr(rc_cmd.os, "chdir", lambda path: 
chdir_calls.append(path))
     monkeypatch.setattr(rc_cmd.os, "scandir", lambda: iter(entries))
+    monkeypatch.setattr(rc_cmd.os.path, "exists", fake_path_exists)
     monkeypatch.setattr(rc_cmd, "confirm_action", fake_confirm_action)
     monkeypatch.setattr(rc_cmd, "console_print", lambda msg="": 
console_messages.append(str(msg)))
     monkeypatch.setattr(rc_cmd, "run_command", lambda cmd, **_kwargs: 
run_command_calls.append(cmd))
 
     # Act
-    rc_cmd.remove_old_releases(version=version, repo_root=repo_root)
+    rc_cmd.remove_old_releases(version=version, 
task_sdk_version=task_sdk_version, repo_root=repo_root)
 
     # Assert: only directory entries matching RC_PATTERN, excluding current 
version, and sorted.
     assert f"{repo_root}/asf-dist/dev/airflow" in chdir_calls
     assert repo_root in chdir_calls
-    assert "The following old releases should be removed: ['2.10.0rc1', 
'2.10.0rc2']" in console_messages
+    assert (
+        "The following old Airflow releases should be removed: ['2.10.0rc1', 
'2.10.0rc2']" in console_messages
+    )
     assert run_command_calls == []
 
 
 def test_remove_old_releases_returns_early_when_user_declines(monkeypatch, 
rc_cmd):
     version = "2.10.0rc3"
+    task_sdk_version = "1.0.6rc3"
     repo_root = "/repo/root"
 
     confirm_prompts: list[str] = []
@@ -98,13 +107,14 @@ def 
test_remove_old_releases_returns_early_when_user_declines(monkeypatch, rc_cm
     monkeypatch.setattr(rc_cmd, "console_print", should_not_be_called)
     monkeypatch.setattr(rc_cmd, "run_command", should_not_be_called)
 
-    rc_cmd.remove_old_releases(version=version, repo_root=repo_root)
+    rc_cmd.remove_old_releases(version=version, 
task_sdk_version=task_sdk_version, repo_root=repo_root)
 
     assert confirm_prompts == ["Do you want to look for old RCs to remove?"]
 
 
 def test_remove_old_releases_removes_confirmed_old_releases(monkeypatch, 
rc_cmd):
     version = "3.1.5rc3"
+    task_sdk_version = "1.0.6rc3"
     repo_root = "/repo/root"
 
     # Unsorted on purpose to verify sorting before prompting/removing.
@@ -129,8 +139,13 @@ def 
test_remove_old_releases_removes_confirmed_old_releases(monkeypatch, rc_cmd)
             return False
         raise AssertionError(f"Unexpected confirm prompt: {prompt}")
 
+    def fake_path_exists(path: str) -> bool:
+        # Task SDK path doesn't exist in this test
+        return False
+
     monkeypatch.setattr(rc_cmd.os, "chdir", lambda path: 
chdir_calls.append(path))
     monkeypatch.setattr(rc_cmd.os, "scandir", lambda: iter(entries))
+    monkeypatch.setattr(rc_cmd.os.path, "exists", fake_path_exists)
     monkeypatch.setattr(rc_cmd, "confirm_action", fake_confirm_action)
     monkeypatch.setattr(rc_cmd, "console_print", lambda msg="": 
console_messages.append(str(msg)))
 
@@ -139,7 +154,7 @@ def 
test_remove_old_releases_removes_confirmed_old_releases(monkeypatch, rc_cmd)
 
     monkeypatch.setattr(rc_cmd, "run_command", fake_run_command)
 
-    rc_cmd.remove_old_releases(version=version, repo_root=repo_root)
+    rc_cmd.remove_old_releases(version=version, 
task_sdk_version=task_sdk_version, repo_root=repo_root)
 
     assert chdir_calls == [f"{repo_root}/asf-dist/dev/airflow", repo_root]
     assert confirm_prompts == [
@@ -147,9 +162,11 @@ def 
test_remove_old_releases_removes_confirmed_old_releases(monkeypatch, rc_cmd)
         "Remove old RC 3.1.0rc1?",
         "Remove old RC 3.1.5rc2?",
     ]
-    assert "The following old releases should be removed: ['3.1.0rc1', 
'3.1.5rc2']" in console_messages
-    assert "Removing old release 3.1.0rc1" in console_messages
-    assert "Removing old release 3.1.5rc2" in console_messages
+    assert (
+        "The following old Airflow releases should be removed: ['3.1.0rc1', 
'3.1.5rc2']" in console_messages
+    )
+    assert "Removing old Airflow release 3.1.0rc1" in console_messages
+    assert "Removing old Airflow release 3.1.5rc2" in console_messages
     assert "[success]Old releases removed" in console_messages
 
     # Only rc1 was confirmed, so we should run rm+commit for rc1 only.
@@ -157,3 +174,157 @@ def 
test_remove_old_releases_removes_confirmed_old_releases(monkeypatch, rc_cmd)
         (["svn", "rm", "3.1.0rc1"], {"check": True}),
         (["svn", "commit", "-m", "Remove old release: 3.1.0rc1"], {"check": 
True}),
     ]
+
+
+def test_remove_old_releases_removes_task_sdk_releases(monkeypatch, rc_cmd):
+    version = "3.1.5rc3"
+    task_sdk_version = "1.0.6rc3"
+    repo_root = "/repo/root"
+
+    # Airflow entries
+    airflow_entries = [
+        FakeDirEntry(version, is_dir=True),
+        FakeDirEntry("3.1.5rc2", is_dir=True),
+    ]
+
+    # Task SDK entries
+    task_sdk_entries = [
+        FakeDirEntry(task_sdk_version, is_dir=True),  # current RC: should be 
skipped
+        FakeDirEntry("1.0.6rc2", is_dir=True),  # old RC dir: should be 
included
+        FakeDirEntry("1.0.6rc1", is_dir=True),  # old RC dir: should be 
included
+    ]
+
+    chdir_calls: list[str] = []
+    console_messages: list[str] = []
+    run_command_calls: list[tuple[list[str], dict]] = []
+    confirm_prompts: list[str] = []
+    scandir_call_count = 0
+
+    def fake_confirm_action(prompt: str, **_kwargs) -> bool:
+        confirm_prompts.append(prompt)
+        if prompt == "Do you want to look for old RCs to remove?":
+            return True
+        # Decline all removals to avoid running svn commands
+        if prompt.startswith("Remove old RC ") or prompt.startswith("Remove 
old Task SDK RC "):
+            return False
+        raise AssertionError(f"Unexpected confirm prompt: {prompt}")
+
+    def fake_path_exists(path: str) -> bool:
+        # Task SDK path exists in this test
+        return path == f"{repo_root}/asf-dist/dev/airflow/task-sdk"
+
+    def fake_scandir():
+        nonlocal scandir_call_count
+        scandir_call_count += 1
+        # First call is for Airflow, second is for Task SDK
+        if scandir_call_count == 1:
+            return iter(airflow_entries)
+        if scandir_call_count == 2:
+            return iter(task_sdk_entries)
+        raise AssertionError("Unexpected scandir call")
+
+    monkeypatch.setattr(rc_cmd.os, "chdir", lambda path: 
chdir_calls.append(path))
+    monkeypatch.setattr(rc_cmd.os, "scandir", fake_scandir)
+    monkeypatch.setattr(rc_cmd.os.path, "exists", fake_path_exists)
+    monkeypatch.setattr(rc_cmd, "confirm_action", fake_confirm_action)
+    monkeypatch.setattr(rc_cmd, "console_print", lambda msg="": 
console_messages.append(str(msg)))
+    monkeypatch.setattr(rc_cmd, "run_command", lambda cmd, **_kwargs: 
run_command_calls.append((cmd, {})))
+
+    rc_cmd.remove_old_releases(version=version, 
task_sdk_version=task_sdk_version, repo_root=repo_root)
+
+    assert f"{repo_root}/asf-dist/dev/airflow" in chdir_calls
+    assert f"{repo_root}/asf-dist/dev/airflow/task-sdk" in chdir_calls
+    assert repo_root in chdir_calls
+    assert "The following old Airflow releases should be removed: 
['3.1.5rc2']" in console_messages
+    assert (
+        "The following old Task SDK releases should be removed: ['1.0.6rc1', 
'1.0.6rc2']" in console_messages
+    )
+    assert "[success]Old releases removed" in console_messages
+    # No removals were confirmed, so no svn commands should be run
+    assert run_command_calls == []
+
+
+def 
test_remove_old_releases_removes_both_airflow_and_task_sdk_releases(monkeypatch,
 rc_cmd):
+    version = "3.1.5rc3"
+    task_sdk_version = "1.0.6rc3"
+    repo_root = "/repo/root"
+
+    # Airflow entries
+    airflow_entries = [
+        FakeDirEntry(version, is_dir=True),
+        FakeDirEntry("3.1.5rc2", is_dir=True),
+    ]
+
+    # Task SDK entries
+    task_sdk_entries = [
+        FakeDirEntry(task_sdk_version, is_dir=True),
+        FakeDirEntry("1.0.6rc2", is_dir=True),
+        FakeDirEntry("1.0.6rc1", is_dir=True),
+    ]
+
+    chdir_calls: list[str] = []
+    console_messages: list[str] = []
+    run_command_calls: list[tuple[list[str], dict]] = []
+    confirm_prompts: list[str] = []
+    scandir_call_count = 0
+
+    def fake_confirm_action(prompt: str, **_kwargs) -> bool:
+        confirm_prompts.append(prompt)
+        if prompt == "Do you want to look for old RCs to remove?":
+            return True
+        # Confirm removal of one Airflow and one Task SDK release
+        if prompt == "Remove old RC 3.1.5rc2?":
+            return True
+        if prompt == "Remove old Task SDK RC 1.0.6rc1?":
+            return True
+        # Decline others
+        if prompt.startswith("Remove old RC ") or prompt.startswith("Remove 
old Task SDK RC "):
+            return False
+        raise AssertionError(f"Unexpected confirm prompt: {prompt}")
+
+    def fake_path_exists(path: str) -> bool:
+        return path == f"{repo_root}/asf-dist/dev/airflow/task-sdk"
+
+    def fake_scandir():
+        nonlocal scandir_call_count
+        scandir_call_count += 1
+        if scandir_call_count == 1:
+            return iter(airflow_entries)
+        if scandir_call_count == 2:
+            return iter(task_sdk_entries)
+        raise AssertionError("Unexpected scandir call")
+
+    monkeypatch.setattr(rc_cmd.os, "chdir", lambda path: 
chdir_calls.append(path))
+    monkeypatch.setattr(rc_cmd.os, "scandir", fake_scandir)
+    monkeypatch.setattr(rc_cmd.os.path, "exists", fake_path_exists)
+    monkeypatch.setattr(rc_cmd, "confirm_action", fake_confirm_action)
+    monkeypatch.setattr(rc_cmd, "console_print", lambda msg="": 
console_messages.append(str(msg)))
+
+    def fake_run_command(cmd: list[str], **kwargs):
+        run_command_calls.append((cmd, kwargs))
+
+    monkeypatch.setattr(rc_cmd, "run_command", fake_run_command)
+
+    rc_cmd.remove_old_releases(version=version, 
task_sdk_version=task_sdk_version, repo_root=repo_root)
+
+    assert chdir_calls == [
+        f"{repo_root}/asf-dist/dev/airflow",
+        f"{repo_root}/asf-dist/dev/airflow/task-sdk",
+        repo_root,
+    ]
+    assert "The following old Airflow releases should be removed: 
['3.1.5rc2']" in console_messages
+    assert (
+        "The following old Task SDK releases should be removed: ['1.0.6rc1', 
'1.0.6rc2']" in console_messages
+    )
+    assert "Removing old Airflow release 3.1.5rc2" in console_messages
+    assert "Removing old Task SDK release 1.0.6rc1" in console_messages
+    assert "Removing old Task SDK release 1.0.6rc2" in console_messages
+    assert "[success]Old releases removed" in console_messages
+
+    # Both Airflow and Task SDK removals were confirmed
+    assert run_command_calls == [
+        (["svn", "rm", "3.1.5rc2"], {"check": True}),
+        (["svn", "commit", "-m", "Remove old release: 3.1.5rc2"], {"check": 
True}),
+        (["svn", "rm", "1.0.6rc1"], {"check": True}),
+        (["svn", "commit", "-m", "Remove old Task SDK release: 1.0.6rc1"], 
{"check": True}),
+    ]

Reply via email to