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"),