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 4670c047f44 Stop airflow config update from writing a backup during 
dry-run (#70979)
4670c047f44 is described below

commit 4670c047f44312471114df855eb8ce217fdc26df
Author: Y-C <[email protected]>
AuthorDate: Wed Aug 19 02:55:47 2026 +0800

    Stop airflow config update from writing a backup during dry-run (#70979)
    
    Without --fix the command is meant to be a pure preview, so it should
    leave the filesystem alone. Creating the .bak unconditionally also made
    the command abort before printing anything when the config directory is
    not writable, hiding the very preview the user asked for.
    
    Co-authored-by: Eason09053360 
<[email protected]>
---
 .../src/airflow/cli/commands/config_command.py     | 20 +++++++++----------
 .../tests/unit/cli/commands/test_config_command.py | 23 ++++++++++++++++++++++
 2 files changed, 33 insertions(+), 10 deletions(-)

diff --git a/airflow-core/src/airflow/cli/commands/config_command.py 
b/airflow-core/src/airflow/cli/commands/config_command.py
index e464fa1e80b..77b187b5899 100644
--- a/airflow-core/src/airflow/cli/commands/config_command.py
+++ b/airflow-core/src/airflow/cli/commands/config_command.py
@@ -941,8 +941,8 @@ def update_config(args) -> None:
     the breaking configuration changes by scanning the current configuration 
file for parameters that have
     been renamed, removed, or had their default values changed in Airflow 3.0. 
To see or fix all recommended
     changes, use the --all-recommendations argument. To automatically update 
your airflow.cfg file, use
-    the --fix argument. This command cleans up the existing comments in 
airflow.cfg but creates a backup of
-    the old airflow.cfg file.
+    the --fix argument. Applying --fix cleans up the existing comments in 
airflow.cfg, so a backup of the
+    old airflow.cfg file is written first. A dry-run leaves the filesystem 
untouched.
 
     CLI Arguments:
         --fix: flag (optional)
@@ -1066,14 +1066,6 @@ def update_config(args) -> None:
                 modifications.add_remove(conf_section, conf_option)
                 changes_applied.append(f"{prefix} Removed 
'{conf_section}/{conf_option}' from configuration.")
 
-    backup_path = f"{AIRFLOW_CONFIG}.bak"
-    try:
-        shutil.copy2(AIRFLOW_CONFIG, backup_path)
-        console.print(f"Backup saved as '{backup_path}'.")
-    except Exception as e:
-        console.print(f"Failed to create backup: {e}")
-        raise AirflowConfigException("Backup creation failed. Aborting 
update_config operation.")
-
     if dry_run:
         console.print("[blue]Dry-run mode enabled. No changes will be written 
to airflow.cfg.[/blue]")
         with StringIO() as config_output:
@@ -1086,6 +1078,14 @@ def update_config(args) -> None:
             new_config = config_output.getvalue()
         console.print(new_config)
     else:
+        backup_path = f"{AIRFLOW_CONFIG}.bak"
+        try:
+            shutil.copy2(AIRFLOW_CONFIG, backup_path)
+            console.print(f"Backup saved as '{backup_path}'.")
+        except Exception as e:
+            console.print(f"Failed to create backup: {e}")
+            raise AirflowConfigException("Backup creation failed. Aborting 
update_config operation.")
+
         with open(AIRFLOW_CONFIG, "w") as config_file:
             conf.write_custom_config(
                 file=config_file,
diff --git a/airflow-core/tests/unit/cli/commands/test_config_command.py 
b/airflow-core/tests/unit/cli/commands/test_config_command.py
index c6345b96434..3e3913d6947 100644
--- a/airflow-core/tests/unit/cli/commands/test_config_command.py
+++ b/airflow-core/tests/unit/cli/commands/test_config_command.py
@@ -627,6 +627,29 @@ class TestCliConfigUpdate:
         current_cfg = cfg_file.read_text()
         assert initial_config in current_cfg, "Dry-run should not modify the 
config file."
 
+    @conf_vars({("core", "executor"): "SequentialExecutor"})
+    def test_update_config_dry_run_does_not_touch_filesystem(self, tmp_path, 
monkeypatch, capsys):
+        cfg_file = tmp_path / "airflow.cfg"
+        cfg_file.write_text("[core]\nexecutor = SequentialExecutor\n")
+
+        monkeypatch.setattr(config_command, "AIRFLOW_CONFIG", str(cfg_file))
+        monkeypatch.setattr(conf, "write_custom_config", lambda file, 
**kwargs: file.write("preview_config"))
+
+        def read_only_copy2(src, dst):
+            raise OSError("Read-only file system")
+
+        monkeypatch.setattr(shutil, "copy2", read_only_copy2)
+
+        parser = cli_parser.get_parser()
+        args = parser.parse_args(["config", "update", "--all-recommendations"])
+
+        config_command.update_config(args)
+
+        output = capsys.readouterr().out
+        assert "preview_config" in output
+        assert "Backup saved as" not in output
+        assert not (tmp_path / "airflow.cfg.bak").exists()
+
     @conf_vars({("core", "executor"): "SequentialExecutor"})
     def test_update_config_all_options_fix(self, tmp_path, monkeypatch, 
capsys):
         cfg_file = tmp_path / "airflow.cfg"

Reply via email to