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 3de732ab9b2 Fix BashOperator script detection after templating (#70369)
3de732ab9b2 is described below

commit 3de732ab9b20a63b368d660c553633fe7830f4c7
Author: Vincent Hsiao <[email protected]>
AuthorDate: Thu Jul 30 17:51:36 2026 +0800

    Fix BashOperator script detection after templating (#70369)
    
    BashOperator template fields are rendered after construction, so script 
detection based on the raw constructor argument can choose the wrong execution 
path for templated commands.
---
 .../src/airflow/providers/standard/decorators/bash.py   |  1 -
 .../src/airflow/providers/standard/operators/bash.py    |  5 ++---
 .../standard/tests/unit/standard/operators/test_bash.py | 17 +++++++++++++++++
 scripts/ci/prek/validate_operators_init_exemptions.txt  |  1 -
 4 files changed, 19 insertions(+), 5 deletions(-)

diff --git 
a/providers/standard/src/airflow/providers/standard/decorators/bash.py 
b/providers/standard/src/airflow/providers/standard/decorators/bash.py
index 169a8dea58a..8cf704b9f84 100644
--- a/providers/standard/src/airflow/providers/standard/decorators/bash.py
+++ b/providers/standard/src/airflow/providers/standard/decorators/bash.py
@@ -88,7 +88,6 @@ class _BashDecoratedOperator(DecoratedOperator, BashOperator):
         if not isinstance(self.bash_command, str) or self.bash_command.strip() 
== "":
             raise TypeError("The returned value from the TaskFlow callable 
must be a non-empty string.")
 
-        self._is_inline_cmd = 
self._is_inline_command(bash_command=self.bash_command)
         self.render_template_fields(context)
         return super().execute(context)
 
diff --git 
a/providers/standard/src/airflow/providers/standard/operators/bash.py 
b/providers/standard/src/airflow/providers/standard/operators/bash.py
index 8f5205c11bd..c0949287360 100644
--- a/providers/standard/src/airflow/providers/standard/operators/bash.py
+++ b/providers/standard/src/airflow/providers/standard/operators/bash.py
@@ -178,9 +178,7 @@ class BashOperator(BaseOperator):
         self.cwd = cwd
         self.append_env = append_env
         self.output_processor = output_processor
-        self._is_inline_cmd = None
-        if isinstance(bash_command, str):
-            self._is_inline_cmd = 
self._is_inline_command(bash_command=bash_command)
+        self._is_inline_cmd: bool | None = None
 
     @cached_property
     def subprocess_hook(self):
@@ -215,6 +213,7 @@ class BashOperator(BaseOperator):
                 raise AirflowException(f"The cwd {self.cwd} must be a 
directory")
         env = self.get_env(context)
 
+        self._is_inline_cmd = self._is_inline_command(bash_command=cast("str", 
self.bash_command))
         if self._is_inline_cmd:
             result = self._run_inline_command(bash_path=bash_path, env=env)
         else:
diff --git a/providers/standard/tests/unit/standard/operators/test_bash.py 
b/providers/standard/tests/unit/standard/operators/test_bash.py
index 1a8aad37981..b38a53d9d07 100644
--- a/providers/standard/tests/unit/standard/operators/test_bash.py
+++ b/providers/standard/tests/unit/standard/operators/test_bash.py
@@ -33,6 +33,7 @@ from airflow.providers.common.compat.sdk import (
     AirflowTaskTimeout,
     timezone,
 )
+from airflow.providers.standard.hooks.subprocess import SubprocessResult
 from airflow.providers.standard.operators.bash import BashOperator
 from airflow.utils.state import State
 from airflow.utils.types import DagRunType
@@ -286,6 +287,22 @@ class TestBashOperator:
         assert task.bash_command == 'echo "test_templated_fields_dag"'
         assert task.cwd == Path(__file__).absolute().parent.as_posix()
 
+    @mock.patch.object(BashOperator, "_run_inline_command")
+    @mock.patch.object(
+        BashOperator, "_run_rendered_script_file", 
return_value=SubprocessResult(exit_code=0, output="ok")
+    )
+    def test_execute_detects_script_after_bash_command_is_rendered(
+        self, mock_run_rendered_script_file, mock_run_inline_command, context
+    ):
+        op = BashOperator(task_id="abc", bash_command="{{ bash_script }}")
+        op.bash_command = "sample.sh"
+
+        result = op.execute(context)
+
+        assert result == "ok"
+        mock_run_rendered_script_file.assert_called_once()
+        mock_run_inline_command.assert_not_called()
+
     @pytest.mark.db_test
     def test_templated_bash_script(self, dag_maker, 
create_task_instance_of_operator, tmp_path, session):
         """
diff --git a/scripts/ci/prek/validate_operators_init_exemptions.txt 
b/scripts/ci/prek/validate_operators_init_exemptions.txt
index c993ca2a163..4cab009b46b 100644
--- a/scripts/ci/prek/validate_operators_init_exemptions.txt
+++ b/scripts/ci/prek/validate_operators_init_exemptions.txt
@@ -28,4 +28,3 @@ 
providers/google/src/airflow/providers/google/cloud/transfers/gcs_to_gcs.py::GCS
 
providers/google/src/airflow/providers/google/marketing_platform/operators/campaign_manager.py::GoogleCampaignManagerDeleteReportOperator
 
providers/microsoft/azure/src/airflow/providers/microsoft/azure/transfers/gcs_to_wasb.py::GCSToAzureBlobStorageOperator
 
providers/microsoft/psrp/src/airflow/providers/microsoft/psrp/operators/psrp.py::PsrpOperator
-providers/standard/src/airflow/providers/standard/operators/bash.py::BashOperator

Reply via email to