Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions airflow-core/src/airflow/cli/commands/config_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -933,8 +933,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)
Expand Down Expand Up @@ -1058,14 +1058,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:
Expand All @@ -1078,6 +1070,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,
Expand Down
23 changes: 23 additions & 0 deletions airflow-core/tests/unit/cli/commands/test_config_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,29 @@ def fake_write_custom_config(file, **kwargs):
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"
Expand Down