Repository: apache/airflow
Pull request: Automatically classify test changes as SKIPPED while preparing providers
<!--
 Licensed to the Apache Software Foundation (ASF) under one
 or more contributor license agreements.  See the NOTICE file
 distributed with this work for additional information
 regarding copyright ownership.  The ASF licenses this file
 to you under the Apache License, Version 2.0 (the
 "License"); you may not use this file except in compliance
 with the License.  You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing,
 software distributed under the License is distributed on an
 "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License.
 -->

<!--
Thank you for contributing! Please make sure that your code changes
are covered with tests. And in case of new features or big changes
remember to adjust the documentation.

Feel free to ping committers for the review!

In case of an existing issue, reference it using one of the following:

closes: #ISSUE
related: #ISSUE

How to write a good git commit message:
http://chris.beams.io/posts/git-commit/
-->

closes: https://github.com/apache/airflow/issues/51132

While preparing release for providers, it is a waste of time to classify PRs that touch "tests" as SKIPPED when it can be done via automation. There were couple of ideas like:

- I think this one we can automate this with labels, probably when PR gets opened scan the pr in gh workflow and if changes are only under something like */tests/*  then add label to PR skip-changelog? i believe this will skip from the release process


But this would require some effort and can mess up PRs and it be a slow github workflow running for every pr, so i chose a way where we instead I use github token and call the Github API for descr that PR and check if it has modifications only to test files.


<!-- Please keep an empty line above the dashes. -->
---
**^ Add meaningful description above**
Read the **[Pull Request Guidelines](https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#pull-request-guidelines)** for more information.
In case of fundamental code changes, an Airflow Improvement Proposal ([AIP](https://cwiki.apache.org/confluence/display/AIRFLOW/Airflow+Improvement+Proposals)) is needed.
In case of a new dependency, check compliance with the [ASF 3rd Party License Policy](https://www.apache.org/legal/resolved.html#category-x).
In case of backwards incompatible changes please leave a note in a newsfragment file, named `{pr_number}.significant.rst` or `{issue_number}.significant.rst`, in [airflow-core/newsfragments](https://github.com/apache/airflow/tree/main/airflow-core/newsfragments).


Problem statement:
Exclude changes on tests from provider release tagging proccess
### Body

PRs that modify only tests can be tagged automatically as skipped no need to ask release manager to tag them


### Committer

- [x] I acknowledge that I am a maintainer/committer of the Apache Airflow project.

Hints from issue discussion:
@eladkal I am self assigning this one. Will get to it as soon as possible!
I think this one we can automate this with labels, probably when PR gets opened scan the pr in gh workflow and if changes are only under something like `*/tests/* ` then add label to PR skip-changelog? i believe this will skip from the release process  
Yep, kind of my idea too. Or we can even parse it while doing the release process as we do not want a slow github workflow running for every pr? 
I don't think labels are required to solve this one?
Yeah they arent. But if we can do that, our release management scripts do not need to have any logic to do it, filtering by files per PR will be a slow process.
> Yeah they arent. But if we can do that, our release management scripts do not need to have any logic to do it, filtering by files per PR will be a slow process.

I rather not do it with labels. In core that works because all PRs must be label and that is not an easy effort.
ACK. I have an alternative proposal in mind. Will get to it soon

Commit message: Automatically classify test changes as SKIPPED while preparing providers

Patch to review:
diff --git a/dev/breeze/doc/images/output_release-management_prepare-provider-documentation.svg b/dev/breeze/doc/images/output_release-management_prepare-provider-documentation.svg
index 4e15b1bb918a0..77be5433f7aef 100644
--- a/dev/breeze/doc/images/output_release-management_prepare-provider-documentation.svg
+++ b/dev/breeze/doc/images/output_release-management_prepare-provider-documentation.svg
@@ -1,4 +1,4 @@
-<svg class="rich-terminal" viewBox="0 0 1482 1099.2" xmlns="http://www.w3.org/2000/svg">
+<svg class="rich-terminal" viewBox="0 0 1482 1123.6" xmlns="http://www.w3.org/2000/svg">
     <!-- Generated with Rich https://www.textualize.io -->
     <style>
 
@@ -43,7 +43,7 @@
 
     <defs>
     <clipPath id="breeze-release-management-prepare-provider-documentation-clip-terminal">
-      <rect x="0" y="0" width="1463.0" height="1048.2" />
+      <rect x="0" y="0" width="1463.0" height="1072.6" />
     </clipPath>
     <clipPath id="breeze-release-management-prepare-provider-documentation-line-0">
     <rect x="0" y="1.5" width="1464" height="24.65"/>
@@ -171,9 +171,12 @@
 <clipPath id="breeze-release-management-prepare-provider-documentation-line-41">
     <rect x="0" y="1001.9" width="1464" height="24.65"/>
             </clipPath>
+<clipPath id="breeze-release-management-prepare-provider-documentation-line-42">
+    <rect x="0" y="1026.3" width="1464" height="24.65"/>
+            </clipPath>
     </defs>
 
-    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="1480" height="1097.2" rx="8"/><text class="breeze-release-management-prepare-provider-documentation-title" fill="#c5c8c6" text-anchor="middle" x="740" y="27">Command:&#160;release-management&#160;prepare-provider-documentation</text>
+    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="1480" height="1121.6" rx="8"/><text class="breeze-release-management-prepare-provider-documentation-title" fill="#c5c8c6" text-anchor="middle" x="740" y="27">Command:&#160;release-management&#160;prepare-provider-documentation</text>
             <g transform="translate(26,22)">
             <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
             <circle cx="22" cy="0" r="7" fill="#febc2e"/>
@@ -219,13 +222,14 @@
 </text><text class="breeze-release-management-prepare-provider-documentation-r5" x="0" y="825.2" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-33)">│</text><text class="breeze-release-management-prepare-provider-documentation-r4" x="24.4" y="825.2" textLength="195.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-33)">--skip-changelog</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="451.4" y="825.2" textLength="988.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-33)">Skip&#160;changelog&#160;generation.&#160;This&#160;is&#160;used&#160;in&#160;pre-commit&#160;that&#160;updates&#160;build-files&#160;&#160;&#160;</text><text class="breeze-release-management-prepare-provider-documentation-r5" x="1451.8" y="825.2" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-33)">│</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="1464" y="825.2" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-33)">
 </text><text class="breeze-release-management-prepare-provider-documentation-r5" x="0" y="849.6" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-34)">│</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="451.4" y="849.6" textLength="988.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-34)">only.&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><text class="breeze-release-management-prepare-provider-documentation-r5" x="1451.8" y="849.6" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-34)">│</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="1464" y="849.6" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-34)">
 </text><text class="breeze-release-management-prepare-provider-documentation-r5" x="0" y="874" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-35)">│</text><text class="breeze-release-management-prepare-provider-documentation-r4" x="24.4" y="874" textLength="158.6" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-35)">--skip-readme</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="451.4" y="874" textLength="988.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-35)">Skip&#160;readme&#160;generation.&#160;This&#160;is&#160;used&#160;in&#160;pre-commit&#160;that&#160;updates&#160;build-files&#160;only.</text><text class="breeze-release-management-prepare-provider-documentation-r5" x="1451.8" y="874" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-35)">│</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="1464" y="874" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-35)">
-</text><text class="breeze-release-management-prepare-provider-documentation-r5" x="0" y="898.4" textLength="1464" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-36)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="1464" y="898.4" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-36)">
-</text><text class="breeze-release-management-prepare-provider-documentation-r5" x="0" y="922.8" textLength="24.4" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-37)">╭─</text><text class="breeze-release-management-prepare-provider-documentation-r5" x="24.4" y="922.8" textLength="195.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-37)">&#160;Common&#160;options&#160;</text><text class="breeze-release-management-prepare-provider-documentation-r5" x="219.6" y="922.8" textLength="1220" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-37)">────────────────────────────────────────────────────────────────────────────────────────────────────</text><text class="breeze-release-management-prepare-provider-documentation-r5" x="1439.6" y="922.8" textLength="24.4" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-37)">─╮</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="1464" y="922.8" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-37)">
-</text><text class="breeze-release-management-prepare-provider-documentation-r5" x="0" y="947.2" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-38)">│</text><text class="breeze-release-management-prepare-provider-documentation-r4" x="24.4" y="947.2" textLength="97.6" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-38)">--answer</text><text class="breeze-release-management-prepare-provider-documentation-r7" x="158.6" y="947.2" textLength="24.4" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-38)">-a</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="207.4" y="947.2" textLength="317.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-38)">Force&#160;answer&#160;to&#160;questions.</text><text class="breeze-release-management-prepare-provider-documentation-r6" x="536.8" y="947.2" textLength="353.8" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-38)">(y&#160;|&#160;n&#160;|&#160;q&#160;|&#160;yes&#160;|&#160;no&#160;|&#160;quit)</text><text class="breeze-release-management-prepare-provider-documentation-r5" x="1451.8" y="947.2" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-38)">│</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="1464" y="947.2" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-38)">
-</text><text class="breeze-release-management-prepare-provider-documentation-r5" x="0" y="971.6" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-39)">│</text><text class="breeze-release-management-prepare-provider-documentation-r4" x="24.4" y="971.6" textLength="109.8" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-39)">--dry-run</text><text class="breeze-release-management-prepare-provider-documentation-r7" x="158.6" y="971.6" textLength="24.4" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-39)">-D</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="207.4" y="971.6" textLength="719.8" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-39)">If&#160;dry-run&#160;is&#160;set,&#160;commands&#160;are&#160;only&#160;printed,&#160;not&#160;executed.</text><text class="breeze-release-management-prepare-provider-documentation-r5" x="1451.8" y="971.6" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-39)">│</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="1464" y="971.6" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-39)">
-</text><text class="breeze-release-management-prepare-provider-documentation-r5" x="0" y="996" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-40)">│</text><text class="breeze-release-management-prepare-provider-documentation-r4" x="24.4" y="996" textLength="109.8" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-40)">--verbose</text><text class="breeze-release-management-prepare-provider-documentation-r7" x="158.6" y="996" textLength="24.4" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-40)">-v</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="207.4" y="996" textLength="585.6" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-40)">Print&#160;verbose&#160;information&#160;about&#160;performed&#160;steps.</text><text class="breeze-release-management-prepare-provider-documentation-r5" x="1451.8" y="996" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-40)">│</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="1464" y="996" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-40)">
-</text><text class="breeze-release-management-prepare-provider-documentation-r5" x="0" y="1020.4" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-41)">│</text><text class="breeze-release-management-prepare-provider-documentation-r4" x="24.4" y="1020.4" textLength="73.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-41)">--help</text><text class="breeze-release-management-prepare-provider-documentation-r7" x="158.6" y="1020.4" textLength="24.4" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-41)">-h</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="207.4" y="1020.4" textLength="329.4" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-41)">Show&#160;this&#160;message&#160;and&#160;exit.</text><text class="breeze-release-management-prepare-provider-documentation-r5" x="1451.8" y="1020.4" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-41)">│</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="1464" y="1020.4" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-41)">
-</text><text class="breeze-release-management-prepare-provider-documentation-r5" x="0" y="1044.8" textLength="1464" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-42)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="1464" y="1044.8" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-42)">
+</text><text class="breeze-release-management-prepare-provider-documentation-r5" x="0" y="898.4" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-36)">│</text><text class="breeze-release-management-prepare-provider-documentation-r4" x="24.4" y="898.4" textLength="170.8" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-36)">--github-token</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="451.4" y="898.4" textLength="451.4" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-36)">The&#160;token&#160;to&#160;use&#160;to&#160;access&#160;GitHub&#160;API</text><text class="breeze-release-management-prepare-provider-documentation-r6" x="915" y="898.4" textLength="73.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-36)">(TEXT)</text><text class="breeze-release-management-prepare-provider-documentation-r5" x="1451.8" y="898.4" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-36)">│</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="1464" y="898.4" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-36)">
+</text><text class="breeze-release-management-prepare-provider-documentation-r5" x="0" y="922.8" textLength="1464" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-37)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="1464" y="922.8" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-37)">
+</text><text class="breeze-release-management-prepare-provider-documentation-r5" x="0" y="947.2" textLength="24.4" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-38)">╭─</text><text class="breeze-release-management-prepare-provider-documentation-r5" x="24.4" y="947.2" textLength="195.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-38)">&#160;Common&#160;options&#160;</text><text class="breeze-release-management-prepare-provider-documentation-r5" x="219.6" y="947.2" textLength="1220" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-38)">────────────────────────────────────────────────────────────────────────────────────────────────────</text><text class="breeze-release-management-prepare-provider-documentation-r5" x="1439.6" y="947.2" textLength="24.4" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-38)">─╮</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="1464" y="947.2" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-38)">
+</text><text class="breeze-release-management-prepare-provider-documentation-r5" x="0" y="971.6" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-39)">│</text><text class="breeze-release-management-prepare-provider-documentation-r4" x="24.4" y="971.6" textLength="97.6" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-39)">--answer</text><text class="breeze-release-management-prepare-provider-documentation-r7" x="158.6" y="971.6" textLength="24.4" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-39)">-a</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="207.4" y="971.6" textLength="317.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-39)">Force&#160;answer&#160;to&#160;questions.</text><text class="breeze-release-management-prepare-provider-documentation-r6" x="536.8" y="971.6" textLength="353.8" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-39)">(y&#160;|&#160;n&#160;|&#160;q&#160;|&#160;yes&#160;|&#160;no&#160;|&#160;quit)</text><text class="breeze-release-management-prepare-provider-documentation-r5" x="1451.8" y="971.6" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-39)">│</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="1464" y="971.6" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-39)">
+</text><text class="breeze-release-management-prepare-provider-documentation-r5" x="0" y="996" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-40)">│</text><text class="breeze-release-management-prepare-provider-documentation-r4" x="24.4" y="996" textLength="109.8" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-40)">--dry-run</text><text class="breeze-release-management-prepare-provider-documentation-r7" x="158.6" y="996" textLength="24.4" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-40)">-D</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="207.4" y="996" textLength="719.8" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-40)">If&#160;dry-run&#160;is&#160;set,&#160;commands&#160;are&#160;only&#160;printed,&#160;not&#160;executed.</text><text class="breeze-release-management-prepare-provider-documentation-r5" x="1451.8" y="996" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-40)">│</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="1464" y="996" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-40)">
+</text><text class="breeze-release-management-prepare-provider-documentation-r5" x="0" y="1020.4" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-41)">│</text><text class="breeze-release-management-prepare-provider-documentation-r4" x="24.4" y="1020.4" textLength="109.8" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-41)">--verbose</text><text class="breeze-release-management-prepare-provider-documentation-r7" x="158.6" y="1020.4" textLength="24.4" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-41)">-v</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="207.4" y="1020.4" textLength="585.6" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-41)">Print&#160;verbose&#160;information&#160;about&#160;performed&#160;steps.</text><text class="breeze-release-management-prepare-provider-documentation-r5" x="1451.8" y="1020.4" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-41)">│</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="1464" y="1020.4" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-41)">
+</text><text class="breeze-release-management-prepare-provider-documentation-r5" x="0" y="1044.8" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-42)">│</text><text class="breeze-release-management-prepare-provider-documentation-r4" x="24.4" y="1044.8" textLength="73.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-42)">--help</text><text class="breeze-release-management-prepare-provider-documentation-r7" x="158.6" y="1044.8" textLength="24.4" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-42)">-h</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="207.4" y="1044.8" textLength="329.4" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-42)">Show&#160;this&#160;message&#160;and&#160;exit.</text><text class="breeze-release-management-prepare-provider-documentation-r5" x="1451.8" y="1044.8" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-42)">│</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="1464" y="1044.8" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-42)">
+</text><text class="breeze-release-management-prepare-provider-documentation-r5" x="0" y="1069.2" textLength="1464" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-43)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text class="breeze-release-management-prepare-provider-documentation-r1" x="1464" y="1069.2" textLength="12.2" clip-path="url(#breeze-release-management-prepare-provider-documentation-line-43)">
 </text>
     </g>
     </g>
diff --git a/dev/breeze/doc/images/output_release-management_prepare-provider-documentation.txt b/dev/breeze/doc/images/output_release-management_prepare-provider-documentation.txt
index 11719f039a9de..69fd9fddc34c6 100644
--- a/dev/breeze/doc/images/output_release-management_prepare-provider-documentation.txt
+++ b/dev/breeze/doc/images/output_release-management_prepare-provider-documentation.txt
@@ -1 +1 @@
-f308e97af5a7f47147b006ed66af2c1c
+0a6fe9a9a734d0d869f14666193cc681
diff --git a/dev/breeze/src/airflow_breeze/commands/release_management_commands.py b/dev/breeze/src/airflow_breeze/commands/release_management_commands.py
index 023615c2f3aa1..1cb4d898ecd0d 100644
--- a/dev/breeze/src/airflow_breeze/commands/release_management_commands.py
+++ b/dev/breeze/src/airflow_breeze/commands/release_management_commands.py
@@ -799,6 +799,11 @@ def provider_action_summary(description: str, message_type: MessageType, package
     help="Skip readme generation. This is used in pre-commit that updates build-files only.",
 )
 @option_verbose
+@click.option(
+    "--github-token",
+    help="The token to use to access GitHub API",
+    envvar="GITHUB_TOKEN",
+)
 def prepare_provider_documentation(
     base_branch: str,
     github_repository: str,
@@ -811,6 +816,7 @@ def prepare_provider_documentation(
     skip_git_fetch: bool,
     skip_changelog: bool,
     skip_readme: bool,
+    github_token: str | None,
 ):
     from airflow_breeze.prepare_providers.provider_documentation import (
         PrepareReleaseDocsChangesOnlyException,
@@ -863,6 +869,7 @@ def prepare_provider_documentation(
                             regenerate_missing_docs=reapply_templates_only,
                             non_interactive=non_interactive,
                             only_min_version_update=only_min_version_update,
+                            github_token=github_token,
                         )
                     )
                 update_min_airflow_version_and_build_files(
diff --git a/dev/breeze/src/airflow_breeze/commands/release_management_commands_config.py b/dev/breeze/src/airflow_breeze/commands/release_management_commands_config.py
index 1bd02e34da765..d5ee7e5635b6b 100644
--- a/dev/breeze/src/airflow_breeze/commands/release_management_commands_config.py
+++ b/dev/breeze/src/airflow_breeze/commands/release_management_commands_config.py
@@ -259,6 +259,7 @@
                 "--skip-git-fetch",
                 "--skip-changelog",
                 "--skip-readme",
+                "--github-token",
             ],
         }
     ],
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 844b3c1790f93..1037149f5c716 100644
--- a/dev/breeze/src/airflow_breeze/prepare_providers/provider_documentation.py
+++ b/dev/breeze/src/airflow_breeze/prepare_providers/provider_documentation.py
@@ -32,6 +32,7 @@
 from time import time
 from typing import Any, NamedTuple
 
+import requests
 from packaging.version import Version, parse
 from rich.syntax import Syntax
 
@@ -715,6 +716,43 @@ def _update_commits_rst(
     )
 
 
+def _is_test_only_changes(pr_number: str, github_token: str | None = None) -> bool:
+    """
+    Check if a PR contains only test-related changes by querying GitHub API.
+    Only considers files in airflow/providers/{provider_name}/tests as test files.
+
+    :param pr_number: The PR number to check
+    :param github_token: GitHub token to use for authentication
+    :return: True if changes are only in test files, False otherwise
+    """
+    from urllib.parse import urljoin
+
+    if not pr_number:
+        return False
+
+    base_url = "https://api.github.com/repos/apache/airflow"
+    pr_files_url = f"/pulls/{pr_number}/files"
+
+    headers = {"Accept": "application/vnd.github.v3.raw"}
+    if os.environ.get("GITHUB_TOKEN"):
+        headers["Authorization"] = f"Bearer {github_token}"
+
+    try:
+        response = requests.get(urljoin(base_url, pr_files_url), headers=headers)
+        response.raise_for_status()
+        files = response.json()
+
+        # check if file is in a provider's test directory
+        for file in files:
+            file_path = file["filename"]
+            if not re.match(r"airflow/providers/[^/]+/tests/", file_path):
+                return False
+        return True
+    except (requests.RequestException, KeyError, ValueError):
+        # if we can't check the PR, assume it's not test-only
+        return False
+
+
 def update_release_notes(
     provider_id: str,
     reapply_templates_only: bool,
@@ -722,6 +760,7 @@ def update_release_notes(
     regenerate_missing_docs: bool,
     non_interactive: bool,
     only_min_version_update: bool,
+    github_token: str | None = None,
 ) -> tuple[bool, bool, bool]:
     """Updates generated files.
 
@@ -732,6 +771,8 @@ def update_release_notes(
     :param base_branch: base branch to check changes in apache remote for changes
     :param regenerate_missing_docs: whether to regenerate missing docs
     :param non_interactive: run in non-interactive mode (useful for CI)
+    :param only_min_version_update: whether to only update min version
+    :param github_token: GitHub token to use for authentication
     :return: tuple of three bools: (with_breaking_change, maybe_with_new_features, with_min_airflow_version_bump)
     """
     proceed, list_of_list_of_changes, changes_as_table = _get_all_changes_for_package(
@@ -781,12 +822,21 @@ def update_release_notes(
                 formatted_message = format_message_for_classification(
                     list_of_list_of_changes[0][table_iter].message_without_backticks
                 )
-                get_console().print(
-                    f"[green]Define the type of change for "
-                    f"`{formatted_message}`"
-                    f" by referring to the above table[/]"
-                )
-                type_of_change = _ask_the_user_for_the_type_of_changes(non_interactive=non_interactive)
+                change = list_of_list_of_changes[0][table_iter]
+
+                if change.pr and _is_test_only_changes(change.pr, github_token):
+                    get_console().print(
+                        f"[green]Automatically classifying change as SKIPPED since it only contains test changes:[/]\n"
+                        f"[blue]{formatted_message}[/]"
+                    )
+                    type_of_change = TypeOfChange.SKIP
+                else:
+                    get_console().print(
+                        f"[green]Define the type of change for "
+                        f"`{formatted_message}`"
+                        f" by referring to the above table[/]"
+                    )
+                    type_of_change = _ask_the_user_for_the_type_of_changes(non_interactive=non_interactive)
 
                 if type_of_change == TypeOfChange.MIN_AIRFLOW_VERSION_BUMP:
                     with_min_airflow_version_bump = True


Code review comments:
diff_hunk: @@ -715,13 +716,51 @@ def _update_commits_rst(
     )
 
 
+def _is_test_only_changes(pr_number: str, github_token: str | None = None) -> bool:
path: dev/breeze/src/airflow_breeze/prepare_providers/provider_documentation.py
text: @user1:
As discussed on slack - adding the need for GH token here is likely not a good idea for the interactive command. We can query local git repo without extra interaction with GH because RM already has all the commits locally - and we have all data in "Changes" (notably commit hash).

```
class Change(NamedTuple):
    full_hash: str
    short_hash: str
    date: str
    message: str
    message_without_backticks: str
    pr: int | None
```

Either by pygit api or even easier by `git command`:

```
git diff 4dfc907f6bdc86a4c9bb941f61accebdc5333b13..4dfc907f6bdc86a4c9bb941f61accebdc5333b13^ --name-only 
providers/fab/tests/conftest.py
providers/fab/tests/unit/fab/www/views/test_views_custom_user_views.py
```

No authentication needed. 


@author:
Good call. I will try along these lines, wasnt so sure about wheteher we pull it locally, turns out we do!