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

turaga 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 9d61f742746 Default logical_date to now in airflowctl dagrun trigger 
to match UI behavior (#61047)
9d61f742746 is described below

commit 9d61f7427460f1a7f866dc6b0011bdf8011b1e4e
Author: Dheeraj Turaga <[email protected]>
AuthorDate: Thu Jan 29 22:12:42 2026 -0600

    Default logical_date to now in airflowctl dagrun trigger to match UI 
behavior (#61047)
    
    * Default logical_date to now in airflowctl dagrun trigger to match UI 
behavior
    
      When triggering DAG runs via airflowctl without specifying 
--logical-date, the
      parameter now defaults to the current timestamp instead of None. This 
aligns
      with the Airflow UI behavior where the trigger form pre-populates 
logical_date
      with the current time, providing a more intuitive user experience.
    
    * Add tests
---
 .../airflowctl_tests/test_airflowctl_commands.py   |  2 +
 airflow-ctl/src/airflowctl/ctl/cli_config.py       | 10 +++++
 .../tests/airflow_ctl/ctl/test_cli_config.py       | 48 ++++++++++++++++++++++
 3 files changed, 60 insertions(+)

diff --git 
a/airflow-ctl-tests/tests/airflowctl_tests/test_airflowctl_commands.py 
b/airflow-ctl-tests/tests/airflowctl_tests/test_airflowctl_commands.py
index b6e537af97d..4e85b45be52 100644
--- a/airflow-ctl-tests/tests/airflowctl_tests/test_airflowctl_commands.py
+++ b/airflow-ctl-tests/tests/airflowctl_tests/test_airflowctl_commands.py
@@ -86,6 +86,8 @@ TEST_COMMANDS = [
     "dags list-warning",
     # Order of trigger and pause/unpause is important for test stability 
because state checked
     f"dags trigger --dag-id=example_bash_operator 
--logical-date={ONE_DATE_PARAM} --run-after={ONE_DATE_PARAM}",
+    # Test trigger without logical-date (should default to now)
+    "dags trigger --dag-id=example_bash_operator",
     "dags pause example_bash_operator",
     "dags unpause example_bash_operator",
     # DAG Run commands
diff --git a/airflow-ctl/src/airflowctl/ctl/cli_config.py 
b/airflow-ctl/src/airflowctl/ctl/cli_config.py
index a578396acd4..6c455693b49 100644
--- a/airflow-ctl/src/airflowctl/ctl/cli_config.py
+++ b/airflow-ctl/src/airflowctl/ctl/cli_config.py
@@ -621,6 +621,16 @@ class CommandFactory:
 
             if datamodel:
                 if datamodel_param_name:
+                    # Special handling for TriggerDAGRunPostBody: default 
logical_date to now
+                    # This matches the Airflow UI behavior where the form 
pre-fills with current time
+                    if (
+                        datamodel.__name__ == "TriggerDAGRunPostBody"
+                        and "logical_date" in 
method_params[datamodel_param_name]
+                        and 
method_params[datamodel_param_name]["logical_date"] is None
+                    ):
+                        method_params[datamodel_param_name]["logical_date"] = 
datetime.datetime.now(
+                            datetime.timezone.utc
+                        )
                     method_params[datamodel_param_name] = 
datamodel.model_validate(
                         method_params[datamodel_param_name]
                     )
diff --git a/airflow-ctl/tests/airflow_ctl/ctl/test_cli_config.py 
b/airflow-ctl/tests/airflow_ctl/ctl/test_cli_config.py
index c0ac022a256..ddf3909339b 100644
--- a/airflow-ctl/tests/airflow_ctl/ctl/test_cli_config.py
+++ b/airflow-ctl/tests/airflow_ctl/ctl/test_cli_config.py
@@ -355,3 +355,51 @@ class TestCliConfigMethods:
                 assert "subcommand2" in sub_command_names
                 assert "subcommand3" in sub_command_names
                 assert "subcommand4" in sub_command_names
+
+    def test_trigger_dag_run_defaults_logical_date_to_now(self):
+        """Test that trigger command defaults logical_date to now when not 
provided."""
+        from datetime import datetime, timezone
+
+        from airflowctl.api.datamodels.generated import TriggerDAGRunPostBody
+
+        # Simulate the logic in _get_func from cli_config.py
+        # This is the actual code path that runs when user doesn't provide 
--logical-date
+
+        # Step 1: Simulate CLI args being parsed (logical_date=None)
+        method_params = {
+            "trigger_dag_run": {
+                "dag_run_id": None,
+                "data_interval_start": None,
+                "data_interval_end": None,
+                "logical_date": None,  # User did not provide --logical-date
+                "run_after": None,
+                "conf": None,
+                "note": None,
+                "partition_key": None,
+            }
+        }
+
+        # Step 2: Apply the defaulting logic (from cli_config.py lines 622-630)
+        datamodel = TriggerDAGRunPostBody
+        datamodel_param_name = "trigger_dag_run"
+
+        if (
+            datamodel.__name__ == "TriggerDAGRunPostBody"
+            and "logical_date" in method_params[datamodel_param_name]
+            and method_params[datamodel_param_name]["logical_date"] is None
+        ):
+            method_params[datamodel_param_name]["logical_date"] = 
datetime.now(timezone.utc)
+
+        # Step 3: Create the Pydantic model (what happens in the actual code)
+        trigger_body = 
datamodel.model_validate(method_params[datamodel_param_name])
+
+        # Step 4: Verify logical_date was set to now
+        assert trigger_body.logical_date is not None, "logical_date should be 
defaulted to now"
+        assert isinstance(trigger_body.logical_date, datetime)
+
+        # Verify it's close to current time (within 5 seconds)
+        time_diff = abs((datetime.now(timezone.utc) - 
trigger_body.logical_date).total_seconds())
+        assert time_diff < 5, f"logical_date should be close to now, but diff 
is {time_diff} seconds"
+
+        # Also verify timezone is UTC
+        assert trigger_body.logical_date.tzinfo is not None, "logical_date 
should have timezone info"

Reply via email to