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}),
+ ]