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

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


The following commit(s) were added to refs/heads/airflow-ctl/v0-1-test by this 
push:
     new 8fc5499c3a1 [airflow-ctl/v0-1-test] Report invalid variables in 
airflowctl variables import as readable errors (#76174) (#76196)
8fc5499c3a1 is described below

commit 8fc5499c3a199bfa6db6803da830d68c881c5280
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Sun Nov 1 10:41:46 2026 +0200

    [airflow-ctl/v0-1-test] Report invalid variables in airflowctl variables 
import as readable errors (#76174) (#76196)
    
    * [airflow-ctl/v0-1-test] Report invalid variables in airflowctl variables 
import as readable errors (#76174)
    
    A variable with a bad value, such as a non-string description or a blank
    or over-long key, crashed `airflowctl variables import` with a raw
    pydantic traceback that did not say which variable was wrong. Report the
    variable key, the file and the reason instead, as
    `airflowctl connections import` already does.
    (cherry picked from commit 122f7b43fd73dea356212462ae7738c75342e22f)
    
    Co-authored-by: Rishabh <[email protected]>
    
    * Drop the blank-key import test case from the v0-1-test backport
    
    The blank-key case relies on the pattern constraint that #74896 added to 
VariableBody.key. That change is not on airflow-ctl/v0-1-test: the branch 
models follow the 3.2 spec, and Airflow 3.2 and 3.3 servers accept a 
whitespace-only key.
    
    ---------
    
    Co-authored-by: Rishabh <[email protected]>
    Co-authored-by: Shahar Epstein <[email protected]>
---
 .../airflowctl/ctl/commands/variable_command.py    | 17 ++++++----
 .../ctl/commands/test_variable_command.py          | 39 ++++++++++++++++++++++
 2 files changed, 50 insertions(+), 6 deletions(-)

diff --git a/airflow-ctl/src/airflowctl/ctl/commands/variable_command.py 
b/airflow-ctl/src/airflowctl/ctl/commands/variable_command.py
index 19321002e44..750b94eb77f 100644
--- a/airflow-ctl/src/airflowctl/ctl/commands/variable_command.py
+++ b/airflow-ctl/src/airflowctl/ctl/commands/variable_command.py
@@ -21,7 +21,9 @@ import os
 import sys
 
 import rich
+from pydantic import ValidationError
 from rich.console import Console
+from rich.markup import escape
 
 from airflowctl.api.client import NEW_API_CLIENT, ClientKind, 
provide_api_client
 from airflowctl.api.datamodels.generated import (
@@ -30,6 +32,7 @@ from airflowctl.api.datamodels.generated import (
     BulkCreateActionVariableBody,
     VariableBody,
 )
+from airflowctl.ctl.utils.validation import split_validation_errors
 
 
 def _print_file_error(message: str, file_path: str) -> None:
@@ -63,13 +66,15 @@ def import_(args, api_client=NEW_API_CLIENT) -> list[str]:
         if isinstance(v, dict) and "value" in v:
             value, description = v["value"], v.get("description")
 
-        vars_to_update.append(
-            VariableBody(
-                key=k,
-                value=value,
-                description=description,
+        try:
+            vars_to_update.append(VariableBody(key=k, value=value, 
description=description))
+        except ValidationError as e:
+            _, invalid = split_validation_errors(VariableBody, e, str)
+            Console().print(
+                f"[red]Invalid variable {escape(repr(k))} in 
{escape(args.file)}: {escape('; '.join(invalid))}",
+                soft_wrap=True,
             )
-        )
+            sys.exit(1)
 
     bulk_body = BulkBodyVariableBody(
         actions=[
diff --git 
a/airflow-ctl/tests/airflow_ctl/ctl/commands/test_variable_command.py 
b/airflow-ctl/tests/airflow_ctl/ctl/commands/test_variable_command.py
index f573585935f..bbc51738e8a 100644
--- a/airflow-ctl/tests/airflow_ctl/ctl/commands/test_variable_command.py
+++ b/airflow-ctl/tests/airflow_ctl/ctl/commands/test_variable_command.py
@@ -136,6 +136,45 @@ class TestCliVariableCommands:
         assert "Invalid variable file:" in output
         assert expected_json_path.as_posix() in output
 
+    @pytest.mark.parametrize(
+        ("variable_file", "expected_key", "expected_reason"),
+        [
+            (
+                {"my_key": {"value": "v", "description": 5}},
+                "'my_key'",
+                "description: Input should be a valid string",
+            ),
+            ({"k" * 251: "v"}, f"'{'k' * 251}'", "key: String should have at 
most 250 characters"),
+        ],
+        ids=["non-string-description", "too-long-key"],
+    )
+    def test_import_reports_invalid_variable(
+        self, tmp_path, monkeypatch, capsys, variable_file, expected_key, 
expected_reason
+    ):
+        monkeypatch.chdir(tmp_path)
+        expected_json_path = tmp_path / self.export_file_name
+        expected_json_path.write_text(json.dumps(variable_file))
+        bulk_called = False
+
+        def bulk(variables):
+            nonlocal bulk_called
+            bulk_called = True
+
+        api_client = SimpleNamespace(variables=SimpleNamespace(bulk=bulk))
+
+        with pytest.raises(SystemExit) as exit_info:
+            variable_command.import_(
+                self.parser.parse_args(["variables", "import", 
expected_json_path.as_posix()]),
+                api_client=api_client,
+            )
+
+        assert exit_info.value.code == 1
+        output = " ".join(capsys.readouterr().out.split())
+        assert (
+            f"Invalid variable {expected_key} in 
{expected_json_path.as_posix()}: {expected_reason}" in output
+        )
+        assert not bulk_called
+
     def test_import_error(self, api_client_maker, tmp_path, monkeypatch):
         api_client = api_client_maker(
             path="/api/v2/variables",

Reply via email to