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

potiuk pushed a commit to branch v3-1-test
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/v3-1-test by this push:
     new d53ec592db4 [v3-1-test] Amend simulation in release command  (#61787) 
(#61792)
d53ec592db4 is described below

commit d53ec592db4517079271788f3a63c27633736c63
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Wed Feb 11 23:49:12 2026 +0100

    [v3-1-test] Amend simulation in release command  (#61787) (#61792)
    
    * Add CI env var to ensure is_ci_environment() returns true and remove 
canary to test
    
    * Move environment check to central and add simulation to find latest
    
    * Create directories for releases
    
    * Try with base execution dir
    
    * Create exact locations
    
    * Pass svn location
    
    * Fix tests and run certain ones not in CI to catch errors
    
    * Fix static checks and mypy errors
    
    * Revert github ci rule
    
    * Update .github/workflows/basic-tests.yml
    
    
    
    ---------
    (cherry picked from commit 6c04a4404ae2533c2159717ef79c884451b7c29a)
    
    Co-authored-by: Bugra Ozturk <[email protected]>
    Co-authored-by: Jarek Potiuk <[email protected]>
---
 .../commands/release_candidate_command.py          |  6 +-
 .../src/airflow_breeze/commands/release_command.py | 51 ++++++++++++--
 .../src/airflow_breeze/utils/environment_check.py  | 24 +++++++
 dev/breeze/tests/test_release_command.py           | 81 ++++++++++++++--------
 4 files changed, 122 insertions(+), 40 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 b6fc228bd39..9df625b6e54 100644
--- a/dev/breeze/src/airflow_breeze/commands/release_candidate_command.py
+++ b/dev/breeze/src/airflow_breeze/commands/release_candidate_command.py
@@ -43,6 +43,7 @@ from airflow_breeze.global_constants import (
 from airflow_breeze.utils.confirm import confirm_action
 from airflow_breeze.utils.console import console_print
 from airflow_breeze.utils.custom_param_types import BetterChoice
+from airflow_breeze.utils.environment_check import is_ci_environment
 from airflow_breeze.utils.path_utils import (
     AIRFLOW_DIST_PATH,
     AIRFLOW_ROOT_PATH,
@@ -59,11 +60,6 @@ SVN_NUM_TRIES = 3
 SVN_OPERATION_RETRY_DELAY = 5
 
 
-def is_ci_environment():
-    """Check if running in CI environment."""
-    return os.environ.get("CI", "").lower() in ("true", "1", "yes")
-
-
 def validate_remote_tracks_apache_airflow(remote_name):
     """Validate that the specified remote tracks the apache/airflow 
repository."""
     console_print(f"[info]Validating remote '{remote_name}' tracks 
apache/airflow...")
diff --git a/dev/breeze/src/airflow_breeze/commands/release_command.py 
b/dev/breeze/src/airflow_breeze/commands/release_command.py
index 2b0f9c0446b..9579e2ec242 100644
--- a/dev/breeze/src/airflow_breeze/commands/release_command.py
+++ b/dev/breeze/src/airflow_breeze/commands/release_command.py
@@ -27,6 +27,7 @@ from airflow_breeze.commands.common_options import 
option_answer, option_dry_run
 from airflow_breeze.commands.release_management_group import 
release_management_group
 from airflow_breeze.utils.confirm import confirm_action
 from airflow_breeze.utils.console import console_print
+from airflow_breeze.utils.environment_check import is_ci_environment
 from airflow_breeze.utils.path_utils import AIRFLOW_ROOT_PATH
 from airflow_breeze.utils.run_utils import run_command
 
@@ -37,7 +38,15 @@ RELEASE_PATTERN = 
re.compile(r"^(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)$"
 SVN_NUM_TRIES = 3
 
 
-def clone_asf_repo(working_dir):
+def clone_asf_repo(working_dir, svn_dev_repo):
+
+    if is_ci_environment():
+        console_print("[info]Running in CI environment - simulating SVN 
checkout")
+        # Create empty directory structure to simulate svn checkout (override 
dry-run if specified)
+        run_command(["mkdir", "-p", svn_dev_repo], check=True, 
dry_run_override=False)
+        console_print("[success]Simulated ASF repo checkout in CI")
+        return
+
     if confirm_action("Clone ASF repo?"):
         run_command(["rm", "-rf", f"{working_dir}/asf-dist"], check=True)
 
@@ -69,7 +78,7 @@ def clone_asf_repo(working_dir):
         run_command(["svn", "update", "--set-depth", "infinity", release_dir], 
check=True)
 
 
-def find_latest_release_candidate(version, svn_dev_repo, component="airflow"):
+def find_latest_release_candidate(version, svn_dev_repo, component="airflow", 
svn_simulation_repo=None):
     """
     Find the latest release candidate for a given version from SVN dev 
directory.
 
@@ -78,6 +87,36 @@ def find_latest_release_candidate(version, svn_dev_repo, 
component="airflow"):
     :param component: Component name ("airflow" or "task-sdk")
     :return: The latest release candidate string (e.g., "3.0.5rc3") or None if 
not found
     """
+    if is_ci_environment():
+
+        def create_simulation_dir(candidate_dir_to_create):
+            svn_dev_component_dir = (
+                f"{svn_simulation_repo}/{candidate_dir_to_create}"
+                if component == "airflow"
+                else 
f"{svn_simulation_repo}/task-sdk/{candidate_dir_to_create}"
+            )
+            print(f"Creating simulation directory: {svn_dev_component_dir}")
+            print(f"candidate_dir_to_create: {candidate_dir_to_create}")
+            run_command(
+                ["mkdir", "-p", 
f"{svn_dev_component_dir}/{candidate_dir_to_create}"],
+                check=True,
+                dry_run_override=False,
+            )
+
+        console_print("[info]Running in CI environment - simulating SVN find 
latest release candidate")
+        airflow_simulate_release_candidate = ["3.1.7rc1", "3.1.7rc2", 
"3.1.7rc3"]
+        task_sdk_simulate_release_candidate = ["1.1.7rc1", "1.1.7rc2", 
"1.1.7rc3"]
+        for candidate in airflow_simulate_release_candidate:
+            create_simulation_dir(candidate)
+        for candidate in task_sdk_simulate_release_candidate:
+            create_simulation_dir(candidate)
+        console_print("[success]Simulated ASF repo checkout in CI")
+        return (
+            airflow_simulate_release_candidate
+            if component == "airflow"
+            else task_sdk_simulate_release_candidate
+        )
+
     if component == "task-sdk":
         search_dir = f"{svn_dev_repo}/task-sdk"
     else:
@@ -425,8 +464,8 @@ def airflow_release(version, task_sdk_version):
     # Clone the asf repo
     os.chdir("..")
     working_dir = os.getcwd()
-    clone_asf_repo(working_dir)
     svn_dev_repo = f"{working_dir}/asf-dist/dev/airflow"
+    clone_asf_repo(working_dir, svn_dev_repo=svn_dev_repo)
     svn_release_repo = f"{working_dir}/asf-dist/release/airflow"
     console_print("SVN dev repo root:", svn_dev_repo)
     console_print("SVN release repo root:", svn_release_repo)
@@ -434,14 +473,16 @@ def airflow_release(version, task_sdk_version):
     # Find the latest release candidate for the given version
     console_print()
     console_print("Finding latest release candidate from SVN dev directory...")
-    release_candidate = find_latest_release_candidate(version, svn_dev_repo, 
component="airflow")
+    release_candidate = find_latest_release_candidate(
+        version=version, svn_dev_repo=svn_dev_repo, component="airflow", 
svn_simulation_repo=svn_release_repo
+    )
     if not release_candidate:
         exit(f"No release candidate found for version {version} in SVN dev 
directory")
 
     task_sdk_release_candidate = None
     if task_sdk_version:
         task_sdk_release_candidate = find_latest_release_candidate(
-            task_sdk_version, svn_dev_repo, component="task-sdk"
+            task_sdk_version, svn_dev_repo, component="task-sdk", 
svn_simulation_repo=svn_release_repo
         )
         if not task_sdk_release_candidate:
             exit(f"No Task SDK release candidate found for version 
{task_sdk_version} in SVN dev directory")
diff --git a/dev/breeze/src/airflow_breeze/utils/environment_check.py 
b/dev/breeze/src/airflow_breeze/utils/environment_check.py
new file mode 100644
index 00000000000..3ffd870d67a
--- /dev/null
+++ b/dev/breeze/src/airflow_breeze/utils/environment_check.py
@@ -0,0 +1,24 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+import os
+
+
+def is_ci_environment():
+    """Check if running in CI environment."""
+    return os.environ.get("CI", "").lower() in ("true", "1", "yes")
diff --git a/dev/breeze/tests/test_release_command.py 
b/dev/breeze/tests/test_release_command.py
index f10c79655fa..2745a8387eb 100644
--- a/dev/breeze/tests/test_release_command.py
+++ b/dev/breeze/tests/test_release_command.py
@@ -16,6 +16,8 @@
 # under the License.
 from __future__ import annotations
 
+import os
+from unittest import mock
 from unittest.mock import patch
 
 import pytest
@@ -23,34 +25,42 @@ import pytest
 from airflow_breeze.commands.release_command import 
find_latest_release_candidate
 
 
+def is_ci_environment() -> bool:
+    """Check if running in CI environment by checking the CI environment 
variable."""
+    return os.environ.get("CI", "").lower() in ("true", "1", "yes")
+
+
 class TestFindLatestReleaseCandidate:
     """Test the find_latest_release_candidate function."""
 
+    @mock.patch.dict(os.environ, {"CI": "false"})
     def test_find_latest_rc_single_candidate(self, tmp_path):
         """Test finding release candidate when only one exists."""
         svn_dev_repo = tmp_path / "dev" / "airflow"
         svn_dev_repo.mkdir(parents=True)
 
         # Create a single RC directory
-        (svn_dev_repo / "3.0.5rc1").mkdir()
+        (svn_dev_repo / "3.1.7rc1").mkdir()
 
-        result = find_latest_release_candidate("3.0.5", str(svn_dev_repo), 
component="airflow")
-        assert result == "3.0.5rc1"
+        result = find_latest_release_candidate("3.1.7", str(svn_dev_repo), 
component="airflow")
+        assert result == "3.1.7rc1"
 
+    @mock.patch.dict(os.environ, {"CI": "false"})
     def test_find_latest_rc_multiple_candidates(self, tmp_path):
         """Test finding latest release candidate when multiple exist."""
         svn_dev_repo = tmp_path / "dev" / "airflow"
         svn_dev_repo.mkdir(parents=True)
 
         # Create multiple RC directories
-        (svn_dev_repo / "3.0.5rc1").mkdir()
-        (svn_dev_repo / "3.0.5rc2").mkdir()
-        (svn_dev_repo / "3.0.5rc3").mkdir()
-        (svn_dev_repo / "3.0.5rc10").mkdir()  # Test that rc10 > rc3
+        (svn_dev_repo / "3.1.7rc1").mkdir()
+        (svn_dev_repo / "3.1.7rc2").mkdir()
+        (svn_dev_repo / "3.1.7rc3").mkdir()
+        (svn_dev_repo / "3.1.7rc10").mkdir()  # Test that rc10 > rc3
 
-        result = find_latest_release_candidate("3.0.5", str(svn_dev_repo), 
component="airflow")
-        assert result == "3.0.5rc10"
+        result = find_latest_release_candidate("3.1.7", str(svn_dev_repo), 
component="airflow")
+        assert result == "3.1.7rc10"
 
+    @mock.patch.dict(os.environ, {"CI": "false"})
     def test_find_latest_rc_ignores_other_versions(self, tmp_path):
         """Test that function ignores RCs for other versions."""
         svn_dev_repo = tmp_path / "dev" / "airflow"
@@ -58,26 +68,28 @@ class TestFindLatestReleaseCandidate:
 
         # Create RCs for different versions
         (svn_dev_repo / "3.0.4rc1").mkdir()
-        (svn_dev_repo / "3.0.5rc1").mkdir()
-        (svn_dev_repo / "3.0.5rc2").mkdir()
+        (svn_dev_repo / "3.1.7rc1").mkdir()
+        (svn_dev_repo / "3.1.7rc2").mkdir()
         (svn_dev_repo / "3.0.6rc1").mkdir()
 
-        result = find_latest_release_candidate("3.0.5", str(svn_dev_repo), 
component="airflow")
-        assert result == "3.0.5rc2"
+        result = find_latest_release_candidate("3.1.7", str(svn_dev_repo), 
component="airflow")
+        assert result == "3.1.7rc2"
 
+    @mock.patch.dict(os.environ, {"CI": "false"})
     def test_find_latest_rc_ignores_non_rc_directories(self, tmp_path):
         """Test that function ignores directories that don't match RC 
pattern."""
         svn_dev_repo = tmp_path / "dev" / "airflow"
         svn_dev_repo.mkdir(parents=True)
 
         # Create RC directory and non-RC directories
-        (svn_dev_repo / "3.0.5rc1").mkdir()
-        (svn_dev_repo / "3.0.5").mkdir()  # Final release directory
+        (svn_dev_repo / "3.1.7rc1").mkdir()
+        (svn_dev_repo / "3.1.7").mkdir()  # Final release directory
         (svn_dev_repo / "some-other-dir").mkdir()
 
-        result = find_latest_release_candidate("3.0.5", str(svn_dev_repo), 
component="airflow")
-        assert result == "3.0.5rc1"
+        result = find_latest_release_candidate("3.1.7", str(svn_dev_repo), 
component="airflow")
+        assert result == "3.1.7rc1"
 
+    @mock.patch.dict(os.environ, {"CI": "false"})
     def test_find_latest_rc_no_match(self, tmp_path):
         """Test that function returns None when no matching RC found."""
         svn_dev_repo = tmp_path / "dev" / "airflow"
@@ -86,25 +98,28 @@ class TestFindLatestReleaseCandidate:
         # Create RCs for different version
         (svn_dev_repo / "3.0.4rc1").mkdir()
 
-        result = find_latest_release_candidate("3.0.5", str(svn_dev_repo), 
component="airflow")
+        result = find_latest_release_candidate("3.1.5", str(svn_dev_repo), 
component="airflow")
         assert result is None
 
+    @mock.patch.dict(os.environ, {"CI": "false"})
     def test_find_latest_rc_directory_not_exists(self, tmp_path):
         """Test that function returns None when directory doesn't exist."""
         svn_dev_repo = tmp_path / "dev" / "airflow"
         # Don't create the directory
 
-        result = find_latest_release_candidate("3.0.5", str(svn_dev_repo), 
component="airflow")
+        result = find_latest_release_candidate("3.1.7", str(svn_dev_repo), 
component="airflow")
         assert result is None
 
+    @mock.patch.dict(os.environ, {"CI": "false"})
     def test_find_latest_rc_empty_directory(self, tmp_path):
         """Test that function returns None when directory is empty."""
         svn_dev_repo = tmp_path / "dev" / "airflow"
         svn_dev_repo.mkdir(parents=True)
 
-        result = find_latest_release_candidate("3.0.5", str(svn_dev_repo), 
component="airflow")
+        result = find_latest_release_candidate("3.1.7", str(svn_dev_repo), 
component="airflow")
         assert result is None
 
+    @mock.patch.dict(os.environ, {"CI": "false"})
     def test_find_latest_rc_task_sdk_component(self, tmp_path):
         """Test finding release candidate for task-sdk component."""
         svn_dev_repo = tmp_path / "dev" / "airflow"
@@ -119,6 +134,7 @@ class TestFindLatestReleaseCandidate:
         result = find_latest_release_candidate("1.0.5", str(svn_dev_repo), 
component="task-sdk")
         assert result == "1.0.5rc3"
 
+    @mock.patch.dict(os.environ, {"CI": "false"})
     def test_find_latest_rc_task_sdk_ignores_airflow_rcs(self, tmp_path):
         """Test that task-sdk component ignores airflow RCs."""
         svn_dev_repo = tmp_path / "dev" / "airflow"
@@ -127,20 +143,25 @@ class TestFindLatestReleaseCandidate:
         task_sdk_dir.mkdir()
 
         # Create airflow RC (should be ignored)
-        (svn_dev_repo / "3.0.5rc1").mkdir()
+        (svn_dev_repo / "3.1.7rc1").mkdir()
         # Create task-sdk RC
-        (task_sdk_dir / "1.0.5rc1").mkdir()
+        (task_sdk_dir / "1.1.7rc1").mkdir()
 
-        result = find_latest_release_candidate("1.0.5", str(svn_dev_repo), 
component="task-sdk")
-        assert result == "1.0.5rc1"
+        result = find_latest_release_candidate("1.1.7", str(svn_dev_repo), 
component="task-sdk")
+        assert result == "1.1.7rc1"
 
+    @mock.patch.dict(os.environ, {"CI": "false"})
     def test_find_latest_rc_handles_oserror(self, tmp_path):
         """Test that function handles OSError gracefully."""
         svn_dev_repo = tmp_path / "dev" / "airflow"
+        svn_simulate_repo = tmp_path / "release" / "airflow"
         svn_dev_repo.mkdir(parents=True)
+        svn_simulate_repo.mkdir(parents=True)
 
         with patch("os.listdir", side_effect=OSError("Permission denied")):
-            result = find_latest_release_candidate("3.0.5", str(svn_dev_repo), 
component="airflow")
+            result = find_latest_release_candidate(
+                "3.1.5", str(svn_dev_repo), component="airflow", 
svn_simulation_repo=svn_simulate_repo
+            )
             assert result is None
 
 
@@ -162,7 +183,7 @@ def release_cmd():
 
 
 def test_remove_old_release_only_collects_release_directories(monkeypatch, 
release_cmd):
-    version = "3.0.5"
+    version = "3.1.7"
     task_sdk_version = "1.0.5"
     svn_release_repo = "/svn/release/repo"
 
@@ -237,7 +258,7 @@ def 
test_remove_old_release_only_collects_release_directories(monkeypatch, relea
 
 
 def test_remove_old_release_returns_early_when_user_declines(monkeypatch, 
release_cmd):
-    version = "3.0.5"
+    version = "3.1.7"
     task_sdk_version = "1.0.5"
     svn_release_repo = "/svn/release/repo"
 
@@ -353,7 +374,7 @@ def 
test_remove_old_release_removes_confirmed_old_releases(monkeypatch, release_
 
 
 def test_remove_old_release_no_old_releases(monkeypatch, release_cmd):
-    version = "3.0.5"
+    version = "3.1.7"
     task_sdk_version = "1.0.5"
     svn_release_repo = "/svn/release/repo"
 
@@ -406,7 +427,7 @@ def test_remove_old_release_no_old_releases(monkeypatch, 
release_cmd):
 
 
 def test_remove_old_release_task_sdk_only(monkeypatch, release_cmd):
-    version = "3.0.5"
+    version = "3.1.7"
     task_sdk_version = "1.0.5"
     svn_release_repo = "/svn/release/repo"
 
@@ -479,7 +500,7 @@ def test_remove_old_release_task_sdk_only(monkeypatch, 
release_cmd):
 
 
 def test_remove_old_release_no_task_sdk_version(monkeypatch, release_cmd):
-    version = "3.0.5"
+    version = "3.1.7"
     task_sdk_version = None
     svn_release_repo = "/svn/release/repo"
 

Reply via email to