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

eladkal 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 e1807a1d49e Auto-skip dev-only tooling changes and release-preparation 
commits in provider documentation prep (#71839)
e1807a1d49e is described below

commit e1807a1d49eff7efbd1979fa8b4ee2ab0f6dd31e
Author: Elad Kalif <[email protected]>
AuthorDate: Wed Aug 19 18:57:51 2026 +0300

    Auto-skip dev-only tooling changes and release-preparation commits in 
provider documentation prep (#71839)
---
 .../prepare_providers/provider_documentation.py    | 49 +++++++++++++++-------
 dev/breeze/tests/test_provider_documentation.py    | 31 ++++++++++++++
 2 files changed, 66 insertions(+), 14 deletions(-)

diff --git 
a/dev/breeze/src/airflow_breeze/prepare_providers/provider_documentation.py 
b/dev/breeze/src/airflow_breeze/prepare_providers/provider_documentation.py
index 0196a990c0c..ed1b1ee5f3a 100644
--- a/dev/breeze/src/airflow_breeze/prepare_providers/provider_documentation.py
+++ b/dev/breeze/src/airflow_breeze/prepare_providers/provider_documentation.py
@@ -205,24 +205,29 @@ def classification_result(provider_id, changed_files):
     def is_doc(f):
         return re.match(r"^providers/.+/docs/", f) and f.endswith(".rst")
 
-    def is_test_or_example(f):
-        return re.match(r"^providers/.+/tests/", f) or re.match(
-            r"^providers/.+/src/airflow/providers/.+/example_dags/", f
+    def is_not_shipped(f):
+        # tests, example DAGs and dev/ (local-only tooling such as simulators, 
generators, etc.) are
+        # never part of the sdist (see the provider's `[tool.flit.sdist] 
include` list), so changes
+        # confined to these paths have no PyPI-facing impact.
+        return (
+            re.match(r"^providers/.+/tests/", f)
+            or 
re.match(r"^providers/.+/src/airflow/providers/.+/example_dags/", f)
+            or re.match(r"^providers/.+/dev/", f)
         )
 
     all_docs = all(is_doc(f) for f in changed_files)
-    all_test_or_example = all(is_test_or_example(f) for f in changed_files)
+    all_not_shipped = all(is_not_shipped(f) for f in changed_files)
 
     has_docs = any(is_doc(f) for f in changed_files)
-    has_test_or_example = any(is_test_or_example(f) for f in changed_files)
+    has_not_shipped = any(is_not_shipped(f) for f in changed_files)
 
-    has_real_code = any(not (is_doc(f) or is_test_or_example(f)) for f in 
changed_files)
+    has_real_code = any(not (is_doc(f) or is_not_shipped(f)) for f in 
changed_files)
 
     if all_docs:
         return "documentation"
-    if all_test_or_example:
+    if all_not_shipped:
         return "test_or_example_only"
-    if not has_real_code and (has_docs or has_test_or_example):
+    if not has_real_code and (has_docs or has_not_shipped):
         return "documentation"
     return "other"
 
@@ -232,7 +237,7 @@ def classify_provider_pr_files(provider_id: str, 
commit_hash: str) -> str:
     Classify a provider commit based on changed files.
 
     - Returns 'documentation' if any provider doc files are present.
-    - Returns 'test_or_example_only' if only test/example DAGs changed.
+    - Returns 'test_or_example_only' if only test/example DAGs/dev-only 
tooling changed.
     - Returns 'other' otherwise.
     """
     try:
@@ -268,6 +273,7 @@ _DETERMINISTIC_CLASSIFICATION_NAMES = {
 }
 
 _BUMP_SUBJECT_RE = re.compile(r"^\s*bump\b", re.IGNORECASE)
+_RELEASE_PREP_SUBJECT_RE = re.compile(r"^\s*Prepare providers release 
\d{4}-\d{2}-\d{2}\b", re.IGNORECASE)
 
 
 def classify_change_deterministically(provider_id: str, change: Change) -> 
tuple[str, str]:
@@ -277,9 +283,10 @@ def classify_change_deterministically(provider_id: str, 
change: Change) -> tuple
     ``documentation``, ``skip``, ``misc`` (decided here) or ``needs_llm`` (no
     high-confidence rule matched - an LLM/agent must assess the type of 
change).
 
-    Intentionally conservative: only changed-files heuristics and a ``Bump``
-    dependency-bump subject are trusted. ``Fix``/``Add`` subjects are NOT
-    auto-classified as bugfix/feature, since they are wrong too often to be 
safe.
+    Intentionally conservative: only changed-files heuristics, a ``Prepare 
providers
+    release YYYY-MM-DD`` subject, and a ``Bump`` dependency-bump subject are 
trusted.
+    ``Fix``/``Add`` subjects are NOT auto-classified as bugfix/feature, since 
they are
+    wrong too often to be safe.
     """
     files_class = classify_provider_pr_files(provider_id, change.full_hash)
     if files_class == "documentation":
@@ -287,7 +294,13 @@ def classify_change_deterministically(provider_id: str, 
change: Change) -> tuple
             "only provider documentation (*.rst) files changed"
         )
     if files_class == "test_or_example_only":
-        return _DETERMINISTIC_CLASSIFICATION_NAMES[TypeOfChange.SKIP], ("only 
tests / example DAGs changed")
+        return _DETERMINISTIC_CLASSIFICATION_NAMES[TypeOfChange.SKIP], (
+            "only tests / example DAGs / dev-only tooling changed"
+        )
+    if _RELEASE_PREP_SUBJECT_RE.match(change.message):
+        return _DETERMINISTIC_CLASSIFICATION_NAMES[TypeOfChange.SKIP], (
+            "release-preparation commit (subject matches 'Prepare providers 
release YYYY-MM-DD')"
+        )
     if _BUMP_SUBJECT_RE.match(change.message):
         return _DETERMINISTIC_CLASSIFICATION_NAMES[TypeOfChange.MISC], (
             "dependency bump (subject starts with 'Bump')"
@@ -900,7 +913,15 @@ def update_release_notes(
                     type_of_change = TypeOfChange.DOCUMENTATION
                 elif classification == "test_or_example_only":
                     console_print(
-                        f"[green]Automatically classifying change as SKIPPED 
since it only contains test/example changes:[/]\n"
+                        f"[green]Automatically classifying change as SKIPPED 
since it only contains "
+                        f"test/example/dev-only tooling changes:[/]\n"
+                        f"[blue]{formatted_message}[/]"
+                    )
+                    type_of_change = TypeOfChange.SKIP
+                elif _RELEASE_PREP_SUBJECT_RE.match(change.message):
+                    console_print(
+                        f"[green]Automatically classifying change as SKIPPED 
since it is a "
+                        f"release-preparation commit:[/]\n"
                         f"[blue]{formatted_message}[/]"
                     )
                     type_of_change = TypeOfChange.SKIP
diff --git a/dev/breeze/tests/test_provider_documentation.py 
b/dev/breeze/tests/test_provider_documentation.py
index b1048452071..555e467d825 100644
--- a/dev/breeze/tests/test_provider_documentation.py
+++ b/dev/breeze/tests/test_provider_documentation.py
@@ -487,6 +487,30 @@ def test_get_most_impactful_change(changes, expected):
         ),
         pytest.param("slack", ["airflow/utils/db.py"], "other", 
id="non_provider_file"),
         pytest.param("slack", [], "other", id="empty_commit"),
+        pytest.param(
+            "informatica",
+            
["providers/informatica/dev/informatica_simulator/requirements.txt"],
+            "test_or_example_only",
+            id="only_dev_tooling",
+        ),
+        pytest.param(
+            "slack",
+            [
+                "providers/slack/tests/test_slack.py",
+                "providers/slack/dev/some_tool.py",
+            ],
+            "test_or_example_only",
+            id="tests_and_dev_tooling",
+        ),
+        pytest.param(
+            "slack",
+            [
+                "providers/slack/src/airflow/providers/slack/hooks/slack.py",
+                "providers/slack/dev/some_tool.py",
+            ],
+            "other",
+            id="dev_tooling_and_real_code",
+        ),
     ],
 )
 def test_classify_provider_pr_files_logic(provider_id, changed_files, 
expected):
@@ -513,6 +537,13 @@ def _make_change(subject: str) -> Change:
         pytest.param("documentation", "Fix typo in docs", "documentation", 
id="doc_only"),
         pytest.param("documentation", "Bump aiohttp", "documentation", 
id="doc_only_beats_bump"),
         pytest.param("test_or_example_only", "Add a flaky test", "skip", 
id="test_only"),
+        # subject-based deterministic rule: release-preparation commits
+        pytest.param(
+            "other", "Prepare providers release 2026-07-22 (#70256)", "skip", 
id="release_prep_skip"
+        ),
+        pytest.param(
+            "other", "prepare providers release 2026-01-01", "skip", 
id="release_prep_lowercase_skip"
+        ),
         # subject-based deterministic rule: dependency bumps
         pytest.param("other", "Bump aiohttp regarding dependabot warning", 
"misc", id="bump_misc"),
         pytest.param("other", "bump the deps group across 1 directory", 
"misc", id="lowercase_bump_misc"),

Reply via email to