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

shahar1 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 d452d2769df Only force the full test matrix on API changes when the 
contract changes (#68060)
d452d2769df is described below

commit d452d2769dfe395f02c8fa48630de2c082319b6f
Author: Shahar Epstein <[email protected]>
AuthorDate: Fri Jun 5 19:17:03 2026 +0300

    Only force the full test matrix on API changes when the contract changes 
(#68060)
    
    Any change under the API directory (airflow-core/src/airflow/api_fastapi/,
    the legacy api/, or their test dirs) forced full-tests-needed=true — the 
whole
    ~135-job matrix including every provider's tests. The API tree is large and
    churns constantly, so this fired on ~1 in 8 PRs and was, by measurement, the
    single largest source of unnecessary full-matrix runs.
    
    But only the API *contract* changing ripples broadly: the generated OpenAPI
    spec (consumed by the UI codegen and the generated clients). Plain API
    source/test edits that leave the committed spec untouched do not — and a 
prek
    hook regenerates and verifies the spec, so an unchanged spec reliably means 
an
    unchanged contract.
    
    Narrow the full-tests trigger from API_FILES to API_CODEGEN_FILES (the
    generated spec / client generator). API source edits still run the `API` 
test
    type and the `fab` provider via run_api_tests; they just no longer drag in 
the
    full provider matrix. Spec changes still force the full matrix.
    
    In a 30-day sample (750 merged PRs) this spares ~63 full-matrix runs while
    preserving full coverage on the 12 PRs that changed the contract.
    
    Co-authored-by: Claude Opus 4.8 <[email protected]>
---
 .../src/airflow_breeze/utils/selective_checks.py   |  15 +-
 dev/breeze/tests/test_selective_checks.py          | 159 ++++++++++++++++-----
 2 files changed, 135 insertions(+), 39 deletions(-)

diff --git a/dev/breeze/src/airflow_breeze/utils/selective_checks.py 
b/dev/breeze/src/airflow_breeze/utils/selective_checks.py
index d7e931469ca..373cf7c8e39 100644
--- a/dev/breeze/src/airflow_breeze/utils/selective_checks.py
+++ b/dev/breeze/src/airflow_breeze/utils/selective_checks.py
@@ -693,10 +693,21 @@ class SelectiveChecks:
             console_print("[warning]Running full set of tests because env 
files changed[/]")
             return True
         if self._matching_files(
-            FileGroupForCi.API_FILES,
+            FileGroupForCi.API_CODEGEN_FILES,
             CI_FILE_GROUP_MATCHES,
         ):
-            console_print("[warning]Running full set of tests because api 
files changed[/]")
+            # Only the API *contract* changing (the generated OpenAPI spec, or 
the
+            # client generator) ripples broadly — to the UI codegen, the 
generated
+            # clients, and every consumer — so it warrants the full matrix. 
Plain
+            # API source/test edits that leave the committed spec untouched do 
not:
+            # a prek hook regenerates and verifies the spec, so an unchanged 
spec
+            # reliably means an unchanged contract. Those edits still run the 
`API`
+            # test type and the `fab` provider (via `run_api_tests`); they 
just no
+            # longer drag in the whole provider matrix.
+            console_print(
+                "[warning]Running full set of tests because the API contract "
+                "(generated OpenAPI spec / client generator) changed[/]"
+            )
             return True
         if self._matching_files(
             FileGroupForCi.GIT_PROVIDER_FILES,
diff --git a/dev/breeze/tests/test_selective_checks.py 
b/dev/breeze/tests/test_selective_checks.py
index 57d01fe7917..cf2b7b99344 100644
--- a/dev/breeze/tests/test_selective_checks.py
+++ b/dev/breeze/tests/test_selective_checks.py
@@ -146,6 +146,22 @@ ALL_SKIPPED_COMMITS_IF_NO_UI_AND_HELM_TESTS = (
     "ts-compile-lint-simple-auth-manager-ui,ts-compile-lint-ui,update-uv-lock"
 )
 
+# API source/test change with NO OpenAPI spec change: the full matrix is no 
longer
+# forced. airflow-core Python changed (so mypy-airflow-core + flynt run); no
+# provider.yaml, helm, or UI files changed, so those checks stay skipped.
+ALL_SKIPPED_COMMITS_IF_ONLY_API_SOURCE_CHANGED = (
+    "check-provider-yaml-valid,identity,lint-helm-chart,"
+    "mypy-airflow-ctl,mypy-airflow-ctl-tests,mypy-airflow-e2e-tests,"
+    
"mypy-dev,mypy-devel-common,mypy-docker-tests,mypy-helm-tests,mypy-kubernetes-tests,"
+    "mypy-scripts,"
+    
"mypy-shared-configuration,mypy-shared-dagnode,mypy-shared-listeners,mypy-shared-logging,"
+    
"mypy-shared-module_loading,mypy-shared-observability,mypy-shared-plugins_manager,"
+    
"mypy-shared-providers_discovery,mypy-shared-secrets_backend,mypy-shared-secrets_masker,"
+    
"mypy-shared-serialization,mypy-shared-state,mypy-shared-template_rendering,mypy-shared-timezones,"
+    "mypy-task-sdk,mypy-task-sdk-integration-tests,"
+    "ts-compile-lint-simple-auth-manager-ui,ts-compile-lint-ui,update-uv-lock"
+)
+
 ALL_SKIPPED_COMMITS_IF_NO_PROVIDERS_AND_UI = (
     "check-provider-yaml-valid,identity,"
     
"mypy-airflow-core,mypy-airflow-ctl,mypy-airflow-ctl-tests,mypy-airflow-e2e-tests,"
@@ -314,72 +330,114 @@ def assert_outputs_are_printed(expected_outputs: 
dict[str, str], stderr: str):
             pytest.param(
                 ("airflow-core/src/airflow/api/file.py",),
                 {
-                    "selected-providers-list-as-string": "",
+                    "full-tests-needed": "false",
+                    "selected-providers-list-as-string": "common.compat fab",
                     "all-python-versions": 
f"['{DEFAULT_PYTHON_MAJOR_MINOR_VERSION}']",
                     "all-python-versions-list-as-string": 
DEFAULT_PYTHON_MAJOR_MINOR_VERSION,
                     "python-versions": 
f"['{DEFAULT_PYTHON_MAJOR_MINOR_VERSION}']",
                     "python-versions-list-as-string": 
DEFAULT_PYTHON_MAJOR_MINOR_VERSION,
                     "ci-image-build": "true",
-                    "prod-image-build": "true",
-                    "run-helm-tests": "true",
+                    "prod-image-build": "false",
+                    "run-helm-tests": "false",
                     "run-unit-tests": "true",
+                    "run-amazon-tests": "false",
+                    "run-api-tests": "true",
                     "docs-build": "true",
-                    "skip-prek-hooks": 
ALL_SKIPPED_COMMITS_BY_DEFAULT_ON_ALL_TESTS_NEEDED,
+                    "skip-prek-hooks": 
ALL_SKIPPED_COMMITS_IF_ONLY_API_SOURCE_CHANGED,
                     "upgrade-to-newer-dependencies": "false",
-                    "core-test-types-list-as-strings-in-json": 
ALL_CI_SELECTIVE_TEST_TYPES_AS_JSON,
-                    "providers-test-types-list-as-strings-in-json": 
ALL_PROVIDERS_SELECTIVE_TEST_TYPES_AS_JSON,
-                    "individual-providers-test-types-list-as-strings-in-json": 
LIST_OF_ALL_PROVIDER_TESTS_AS_JSON,
-                    "run-mypy-providers": "true",
+                    "core-test-types-list-as-strings-in-json": json.dumps(
+                        [{"description": "API...Always", "test_types": "API 
Always"}]
+                    ),
+                    "providers-test-types-list-as-strings-in-json": json.dumps(
+                        [{"description": "common.compat,fab", "test_types": 
"Providers[common.compat,fab]"}]
+                    ),
+                    "individual-providers-test-types-list-as-strings-in-json": 
json.dumps(
+                        [
+                            {
+                                "description": "common.compat...fab",
+                                "test_types": "Providers[common.compat] 
Providers[fab]",
+                            }
+                        ]
+                    ),
+                    "run-mypy-providers": "false",
                 },
-                id="All tests should be run when API file changed",
+                id="API source change (no spec) runs API + fab only, not the 
full matrix",
             )
         ),
         (
             pytest.param(
                 ("airflow-core/src/airflow/api_fastapi/file.py",),
                 {
+                    "full-tests-needed": "false",
+                    "selected-providers-list-as-string": "common.compat fab",
                     "all-python-versions": 
f"['{DEFAULT_PYTHON_MAJOR_MINOR_VERSION}']",
                     "all-python-versions-list-as-string": 
DEFAULT_PYTHON_MAJOR_MINOR_VERSION,
                     "python-versions": 
f"['{DEFAULT_PYTHON_MAJOR_MINOR_VERSION}']",
                     "python-versions-list-as-string": 
DEFAULT_PYTHON_MAJOR_MINOR_VERSION,
                     "ci-image-build": "true",
-                    "prod-image-build": "true",
-                    "run-helm-tests": "true",
+                    "prod-image-build": "false",
+                    "run-helm-tests": "false",
                     "run-unit-tests": "true",
-                    "run-amazon-tests": "true",
+                    "run-amazon-tests": "false",
+                    "run-api-tests": "true",
                     "docs-build": "true",
-                    "skip-prek-hooks": 
ALL_SKIPPED_COMMITS_BY_DEFAULT_ON_ALL_TESTS_NEEDED,
+                    "skip-prek-hooks": 
ALL_SKIPPED_COMMITS_IF_ONLY_API_SOURCE_CHANGED,
                     "upgrade-to-newer-dependencies": "false",
-                    "core-test-types-list-as-strings-in-json": 
ALL_CI_SELECTIVE_TEST_TYPES_AS_JSON,
-                    "providers-test-types-list-as-strings-in-json": 
ALL_PROVIDERS_SELECTIVE_TEST_TYPES_AS_JSON,
-                    "individual-providers-test-types-list-as-strings-in-json": 
LIST_OF_ALL_PROVIDER_TESTS_AS_JSON,
-                    "run-mypy-providers": "true",
+                    "core-test-types-list-as-strings-in-json": json.dumps(
+                        [{"description": "API...Always", "test_types": "API 
Always"}]
+                    ),
+                    "providers-test-types-list-as-strings-in-json": json.dumps(
+                        [{"description": "common.compat,fab", "test_types": 
"Providers[common.compat,fab]"}]
+                    ),
+                    "individual-providers-test-types-list-as-strings-in-json": 
json.dumps(
+                        [
+                            {
+                                "description": "common.compat...fab",
+                                "test_types": "Providers[common.compat] 
Providers[fab]",
+                            }
+                        ]
+                    ),
+                    "run-mypy-providers": "false",
                 },
-                id="All tests should be run when fastapi files change",
+                id="fastapi source change (no spec) runs API + fab only, not 
the full matrix",
             )
         ),
         (
             pytest.param(
                 ("airflow-core/tests/unit/api/file.py",),
                 {
+                    "full-tests-needed": "false",
+                    "selected-providers-list-as-string": "common.compat fab",
                     "all-python-versions": 
f"['{DEFAULT_PYTHON_MAJOR_MINOR_VERSION}']",
                     "all-python-versions-list-as-string": 
DEFAULT_PYTHON_MAJOR_MINOR_VERSION,
                     "python-versions": 
f"['{DEFAULT_PYTHON_MAJOR_MINOR_VERSION}']",
                     "python-versions-list-as-string": 
DEFAULT_PYTHON_MAJOR_MINOR_VERSION,
                     "ci-image-build": "true",
-                    "prod-image-build": "true",
-                    "run-helm-tests": "true",
+                    "prod-image-build": "false",
+                    "run-helm-tests": "false",
                     "run-unit-tests": "true",
-                    "run-amazon-tests": "true",
-                    "docs-build": "true",
-                    "skip-prek-hooks": 
ALL_SKIPPED_COMMITS_BY_DEFAULT_ON_ALL_TESTS_NEEDED,
+                    "run-amazon-tests": "false",
+                    "run-api-tests": "true",
+                    "docs-build": "false",
+                    "skip-prek-hooks": 
ALL_SKIPPED_COMMITS_IF_ONLY_API_SOURCE_CHANGED,
                     "upgrade-to-newer-dependencies": "false",
-                    "core-test-types-list-as-strings-in-json": 
ALL_CI_SELECTIVE_TEST_TYPES_AS_JSON,
-                    "providers-test-types-list-as-strings-in-json": 
ALL_PROVIDERS_SELECTIVE_TEST_TYPES_AS_JSON,
-                    "individual-providers-test-types-list-as-strings-in-json": 
LIST_OF_ALL_PROVIDER_TESTS_AS_JSON,
-                    "run-mypy-providers": "true",
+                    "core-test-types-list-as-strings-in-json": json.dumps(
+                        [{"description": "API...Always", "test_types": "API 
Always"}]
+                    ),
+                    "providers-test-types-list-as-strings-in-json": json.dumps(
+                        [{"description": "common.compat,fab", "test_types": 
"Providers[common.compat,fab]"}]
+                    ),
+                    "individual-providers-test-types-list-as-strings-in-json": 
json.dumps(
+                        [
+                            {
+                                "description": "common.compat...fab",
+                                "test_types": "Providers[common.compat] 
Providers[fab]",
+                            }
+                        ]
+                    ),
+                    "run-mypy-providers": "false",
                 },
-                id="All tests should run when API test files change",
+                id="API test change (no spec) runs API + fab only, not the 
full matrix",
             )
         ),
         (
@@ -429,25 +487,49 @@ def assert_outputs_are_printed(expected_outputs: 
dict[str, str], stderr: str):
                     "providers/postgres/tests/unit/postgres/file.py",
                 ),
                 {
-                    "selected-providers-list-as-string": "",
+                    "full-tests-needed": "false",
+                    "selected-providers-list-as-string": "amazon common.compat 
common.sql fab google "
+                    "microsoft.azure openlineage pgvector postgres",
                     "all-python-versions": 
f"['{DEFAULT_PYTHON_MAJOR_MINOR_VERSION}']",
                     "all-python-versions-list-as-string": 
DEFAULT_PYTHON_MAJOR_MINOR_VERSION,
                     "python-versions": 
f"['{DEFAULT_PYTHON_MAJOR_MINOR_VERSION}']",
                     "python-versions-list-as-string": 
DEFAULT_PYTHON_MAJOR_MINOR_VERSION,
                     "ci-image-build": "true",
-                    "prod-image-build": "true",
-                    "run-helm-tests": "true",
+                    "prod-image-build": "false",
+                    "run-helm-tests": "false",
                     "run-unit-tests": "true",
                     "run-amazon-tests": "true",
+                    "run-api-tests": "true",
                     "docs-build": "true",
-                    "skip-prek-hooks": 
ALL_SKIPPED_COMMITS_BY_DEFAULT_ON_ALL_TESTS_NEEDED,
+                    "skip-prek-hooks": (
+                        "identity,lint-helm-chart,"
+                        
"mypy-airflow-ctl,mypy-airflow-ctl-tests,mypy-airflow-e2e-tests,"
+                        
"mypy-dev,mypy-devel-common,mypy-docker-tests,mypy-helm-tests,mypy-kubernetes-tests,"
+                        "mypy-scripts,"
+                        
"mypy-shared-configuration,mypy-shared-dagnode,mypy-shared-listeners,mypy-shared-logging,"
+                        
"mypy-shared-module_loading,mypy-shared-observability,mypy-shared-plugins_manager,"
+                        
"mypy-shared-providers_discovery,mypy-shared-secrets_backend,mypy-shared-secrets_masker,"
+                        
"mypy-shared-serialization,mypy-shared-state,mypy-shared-template_rendering,"
+                        
"mypy-shared-timezones,mypy-task-sdk,mypy-task-sdk-integration-tests,"
+                        
"ts-compile-lint-simple-auth-manager-ui,ts-compile-lint-ui,update-uv-lock"
+                    ),
                     "upgrade-to-newer-dependencies": "false",
-                    "core-test-types-list-as-strings-in-json": 
ALL_CI_SELECTIVE_TEST_TYPES_AS_JSON,
-                    "providers-test-types-list-as-strings-in-json": 
ALL_PROVIDERS_SELECTIVE_TEST_TYPES_AS_JSON,
-                    "individual-providers-test-types-list-as-strings-in-json": 
LIST_OF_ALL_PROVIDER_TESTS_AS_JSON,
+                    "core-test-types-list-as-strings-in-json": json.dumps(
+                        [{"description": "API...Always", "test_types": "API 
Always"}]
+                    ),
+                    "providers-test-types-list-as-strings-in-json": json.dumps(
+                        [
+                            {
+                                "description": "amazon...google",
+                                "test_types": "Providers[amazon] "
+                                
"Providers[common.compat,common.sql,fab,microsoft.azure,openlineage,pgvector,postgres]
 "
+                                "Providers[google]",
+                            }
+                        ]
+                    ),
                     "run-mypy-providers": "true",
                 },
-                id="All tests and docs should run on API change",
+                id="API source + provider change runs API + affected 
providers, not the full matrix",
             )
         ),
         (
@@ -2320,6 +2402,9 @@ def test_expected_output_push(
         pytest.param(
             
("airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml",),
             {
+                # The OpenAPI spec IS the API contract — changing it ripples 
to the UI
+                # codegen and generated clients, so it still forces the full 
matrix.
+                "full-tests-needed": "true",
                 "selected-providers-list-as-string": "",
                 "all-python-versions": 
f"['{DEFAULT_PYTHON_MAJOR_MINOR_VERSION}']",
                 "all-python-versions-list-as-string": 
DEFAULT_PYTHON_MAJOR_MINOR_VERSION,
@@ -2334,7 +2419,7 @@ def test_expected_output_push(
                 "core-test-types-list-as-strings-in-json": 
ALL_CI_SELECTIVE_TEST_TYPES_AS_JSON,
                 "run-mypy-providers": "true",
             },
-            id="pre commit ts-compile-format-lint should not be ignored if 
openapi spec changed.",
+            id="OpenAPI spec change still forces the full matrix",
         ),
         pytest.param(
             (

Reply via email to