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 9b3ed326f1c Add cooldown parameter to constraints-version-check 
command (#64844)
9b3ed326f1c is described below

commit 9b3ed326f1ca46ed8d835342e8d9a2f41f188c13
Author: Jarek Potiuk <[email protected]>
AuthorDate: Tue Apr 7 17:35:19 2026 +0200

    Add cooldown parameter to constraints-version-check command (#64844)
    
    When checking for latest package versions, very recent releases may
    not yet be stable or widely mirrored. This adds a --cooldown-days
    parameter (default: 4 days) that excludes package versions released
    within the cooldown period when determining the "latest" version.
---
 ...elease-management_constraints-version-check.svg | 32 ++++++++++------
 ...elease-management_constraints-version-check.txt |  2 +-
 .../commands/release_management_commands.py        |  9 +++++
 .../commands/release_management_commands_config.py |  1 +
 .../utils/constraints_version_check.py             | 43 ++++++++++++++++++++--
 5 files changed, 71 insertions(+), 16 deletions(-)

diff --git 
a/dev/breeze/doc/images/output_release-management_constraints-version-check.svg 
b/dev/breeze/doc/images/output_release-management_constraints-version-check.svg
index df0f1265600..5bc64a75459 100644
--- 
a/dev/breeze/doc/images/output_release-management_constraints-version-check.svg
+++ 
b/dev/breeze/doc/images/output_release-management_constraints-version-check.svg
@@ -1,4 +1,4 @@
-<svg class="rich-terminal" viewBox="0 0 1482 733.1999999999999" 
xmlns="http://www.w3.org/2000/svg";>
+<svg class="rich-terminal" viewBox="0 0 1482 782.0" 
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-constraints-version-check-clip-terminal">
-      <rect x="0" y="0" width="1463.0" height="682.1999999999999" />
+      <rect x="0" y="0" width="1463.0" height="731.0" />
     </clipPath>
     <clipPath id="breeze-release-management-constraints-version-check-line-0">
     <rect x="0" y="1.5" width="1464" height="24.65"/>
@@ -126,9 +126,15 @@
 <clipPath id="breeze-release-management-constraints-version-check-line-26">
     <rect x="0" y="635.9" width="1464" height="24.65"/>
             </clipPath>
+<clipPath id="breeze-release-management-constraints-version-check-line-27">
+    <rect x="0" y="660.3" width="1464" height="24.65"/>
+            </clipPath>
+<clipPath id="breeze-release-management-constraints-version-check-line-28">
+    <rect x="0" y="684.7" 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="731.2" rx="8"/><text 
class="breeze-release-management-constraints-version-check-title" 
fill="#c5c8c6" text-anchor="middle" x="740" 
y="27">Command:&#160;release-management&#160;constraints-version-check</text>
+    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" 
x="1" y="1" width="1480" height="780" rx="8"/><text 
class="breeze-release-management-constraints-version-check-title" 
fill="#c5c8c6" text-anchor="middle" x="740" 
y="27">Command:&#160;release-management&#160;constraints-version-check</text>
             <g transform="translate(26,22)">
             <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
             <circle cx="22" cy="0" r="7" fill="#febc2e"/>
@@ -157,15 +163,17 @@
 </text><text class="breeze-release-management-constraints-version-check-r5" 
x="0" y="410.4" textLength="12.2" 
clip-path="url(#breeze-release-management-constraints-version-check-line-16)">│</text><text
 class="breeze-release-management-constraints-version-check-r7" x="414.8" 
y="410.4" textLength="390.4" 
clip-path="url(#breeze-release-management-constraints-version-check-line-16)">(full|diff-all|diff-constraints)</text><text
 class="breeze-release-management-constraints-version-check-r5" x= [...]
 </text><text class="breeze-release-management-constraints-version-check-r5" 
x="0" y="434.8" textLength="12.2" 
clip-path="url(#breeze-release-management-constraints-version-check-line-17)">│</text><text
 class="breeze-release-management-constraints-version-check-r4" x="24.4" 
y="434.8" textLength="366" 
clip-path="url(#breeze-release-management-constraints-version-check-line-17)">--package&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#
 [...]
 </text><text class="breeze-release-management-constraints-version-check-r5" 
x="0" y="459.2" textLength="12.2" 
clip-path="url(#breeze-release-management-constraints-version-check-line-18)">│</text><text
 class="breeze-release-management-constraints-version-check-r4" x="24.4" 
y="459.2" textLength="158.6" 
clip-path="url(#breeze-release-management-constraints-version-check-line-18)">--explain-why</text><text
 class="breeze-release-management-constraints-version-check-r1" x="183" 
y="459.2" text [...]
-</text><text class="breeze-release-management-constraints-version-check-r5" 
x="0" y="483.6" textLength="1464" 
clip-path="url(#breeze-release-management-constraints-version-check-line-19)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-release-management-constraints-version-check-r1" x="1464" 
y="483.6" textLength="12.2" 
clip-path="url(#breeze-release-management-constraints-version-check-line-19)">
-</text><text class="breeze-release-management-constraints-version-check-r5" 
x="0" y="508" textLength="24.4" 
clip-path="url(#breeze-release-management-constraints-version-check-line-20)">╭─</text><text
 class="breeze-release-management-constraints-version-check-r5" x="24.4" 
y="508" textLength="195.2" 
clip-path="url(#breeze-release-management-constraints-version-check-line-20)">&#160;Build&#160;options.&#160;</text><text
 class="breeze-release-management-constraints-version-check-r5" x="219. [...]
-</text><text class="breeze-release-management-constraints-version-check-r5" 
x="0" y="532.4" textLength="12.2" 
clip-path="url(#breeze-release-management-constraints-version-check-line-21)">│</text><text
 class="breeze-release-management-constraints-version-check-r4" x="24.4" 
y="532.4" textLength="109.8" 
clip-path="url(#breeze-release-management-constraints-version-check-line-21)">--builder</text><text
 class="breeze-release-management-constraints-version-check-r1" x="158.6" 
y="532.4" textLe [...]
-</text><text class="breeze-release-management-constraints-version-check-r5" 
x="0" y="556.8" textLength="1464" 
clip-path="url(#breeze-release-management-constraints-version-check-line-22)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-release-management-constraints-version-check-r1" x="1464" 
y="556.8" textLength="12.2" 
clip-path="url(#breeze-release-management-constraints-version-check-line-22)">
-</text><text class="breeze-release-management-constraints-version-check-r5" 
x="0" y="581.2" textLength="24.4" 
clip-path="url(#breeze-release-management-constraints-version-check-line-23)">╭─</text><text
 class="breeze-release-management-constraints-version-check-r5" x="24.4" 
y="581.2" textLength="195.2" 
clip-path="url(#breeze-release-management-constraints-version-check-line-23)">&#160;Common&#160;options&#160;</text><text
 class="breeze-release-management-constraints-version-check-r5" x=" [...]
-</text><text class="breeze-release-management-constraints-version-check-r5" 
x="0" y="605.6" textLength="12.2" 
clip-path="url(#breeze-release-management-constraints-version-check-line-24)">│</text><text
 class="breeze-release-management-constraints-version-check-r4" x="24.4" 
y="605.6" textLength="109.8" 
clip-path="url(#breeze-release-management-constraints-version-check-line-24)">--verbose</text><text
 class="breeze-release-management-constraints-version-check-r6" x="158.6" 
y="605.6" textLe [...]
-</text><text class="breeze-release-management-constraints-version-check-r5" 
x="0" y="630" textLength="12.2" 
clip-path="url(#breeze-release-management-constraints-version-check-line-25)">│</text><text
 class="breeze-release-management-constraints-version-check-r4" x="24.4" 
y="630" textLength="109.8" 
clip-path="url(#breeze-release-management-constraints-version-check-line-25)">--dry-run</text><text
 class="breeze-release-management-constraints-version-check-r6" x="158.6" 
y="630" textLength=" [...]
-</text><text class="breeze-release-management-constraints-version-check-r5" 
x="0" y="654.4" textLength="12.2" 
clip-path="url(#breeze-release-management-constraints-version-check-line-26)">│</text><text
 class="breeze-release-management-constraints-version-check-r4" x="24.4" 
y="654.4" textLength="109.8" 
clip-path="url(#breeze-release-management-constraints-version-check-line-26)">--help&#160;&#160;&#160;</text><text
 class="breeze-release-management-constraints-version-check-r6" x="158.6" y 
[...]
-</text><text class="breeze-release-management-constraints-version-check-r5" 
x="0" y="678.8" textLength="1464" 
clip-path="url(#breeze-release-management-constraints-version-check-line-27)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-release-management-constraints-version-check-r1" x="1464" 
y="678.8" textLength="12.2" 
clip-path="url(#breeze-release-management-constraints-version-check-line-27)">
+</text><text class="breeze-release-management-constraints-version-check-r5" 
x="0" y="483.6" textLength="12.2" 
clip-path="url(#breeze-release-management-constraints-version-check-line-19)">│</text><text
 class="breeze-release-management-constraints-version-check-r4" x="24.4" 
y="483.6" textLength="366" 
clip-path="url(#breeze-release-management-constraints-version-check-line-19)">--cooldown-days&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;</text><
 [...]
+</text><text class="breeze-release-management-constraints-version-check-r5" 
x="0" y="508" textLength="12.2" 
clip-path="url(#breeze-release-management-constraints-version-check-line-20)">│</text><text
 class="breeze-release-management-constraints-version-check-r5" x="414.8" 
y="508" textLength="24.4" 
clip-path="url(#breeze-release-management-constraints-version-check-line-20)">4]</text><text
 class="breeze-release-management-constraints-version-check-r7" x="451.4" 
y="508" textLength="109.8"  [...]
+</text><text class="breeze-release-management-constraints-version-check-r5" 
x="0" y="532.4" textLength="1464" 
clip-path="url(#breeze-release-management-constraints-version-check-line-21)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-release-management-constraints-version-check-r1" x="1464" 
y="532.4" textLength="12.2" 
clip-path="url(#breeze-release-management-constraints-version-check-line-21)">
+</text><text class="breeze-release-management-constraints-version-check-r5" 
x="0" y="556.8" textLength="24.4" 
clip-path="url(#breeze-release-management-constraints-version-check-line-22)">╭─</text><text
 class="breeze-release-management-constraints-version-check-r5" x="24.4" 
y="556.8" textLength="195.2" 
clip-path="url(#breeze-release-management-constraints-version-check-line-22)">&#160;Build&#160;options.&#160;</text><text
 class="breeze-release-management-constraints-version-check-r5" x=" [...]
+</text><text class="breeze-release-management-constraints-version-check-r5" 
x="0" y="581.2" textLength="12.2" 
clip-path="url(#breeze-release-management-constraints-version-check-line-23)">│</text><text
 class="breeze-release-management-constraints-version-check-r4" x="24.4" 
y="581.2" textLength="109.8" 
clip-path="url(#breeze-release-management-constraints-version-check-line-23)">--builder</text><text
 class="breeze-release-management-constraints-version-check-r1" x="158.6" 
y="581.2" textLe [...]
+</text><text class="breeze-release-management-constraints-version-check-r5" 
x="0" y="605.6" textLength="1464" 
clip-path="url(#breeze-release-management-constraints-version-check-line-24)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-release-management-constraints-version-check-r1" x="1464" 
y="605.6" textLength="12.2" 
clip-path="url(#breeze-release-management-constraints-version-check-line-24)">
+</text><text class="breeze-release-management-constraints-version-check-r5" 
x="0" y="630" textLength="24.4" 
clip-path="url(#breeze-release-management-constraints-version-check-line-25)">╭─</text><text
 class="breeze-release-management-constraints-version-check-r5" x="24.4" 
y="630" textLength="195.2" 
clip-path="url(#breeze-release-management-constraints-version-check-line-25)">&#160;Common&#160;options&#160;</text><text
 class="breeze-release-management-constraints-version-check-r5" x="219. [...]
+</text><text class="breeze-release-management-constraints-version-check-r5" 
x="0" y="654.4" textLength="12.2" 
clip-path="url(#breeze-release-management-constraints-version-check-line-26)">│</text><text
 class="breeze-release-management-constraints-version-check-r4" x="24.4" 
y="654.4" textLength="109.8" 
clip-path="url(#breeze-release-management-constraints-version-check-line-26)">--verbose</text><text
 class="breeze-release-management-constraints-version-check-r6" x="158.6" 
y="654.4" textLe [...]
+</text><text class="breeze-release-management-constraints-version-check-r5" 
x="0" y="678.8" textLength="12.2" 
clip-path="url(#breeze-release-management-constraints-version-check-line-27)">│</text><text
 class="breeze-release-management-constraints-version-check-r4" x="24.4" 
y="678.8" textLength="109.8" 
clip-path="url(#breeze-release-management-constraints-version-check-line-27)">--dry-run</text><text
 class="breeze-release-management-constraints-version-check-r6" x="158.6" 
y="678.8" textLe [...]
+</text><text class="breeze-release-management-constraints-version-check-r5" 
x="0" y="703.2" textLength="12.2" 
clip-path="url(#breeze-release-management-constraints-version-check-line-28)">│</text><text
 class="breeze-release-management-constraints-version-check-r4" x="24.4" 
y="703.2" textLength="109.8" 
clip-path="url(#breeze-release-management-constraints-version-check-line-28)">--help&#160;&#160;&#160;</text><text
 class="breeze-release-management-constraints-version-check-r6" x="158.6" y 
[...]
+</text><text class="breeze-release-management-constraints-version-check-r5" 
x="0" y="727.6" textLength="1464" 
clip-path="url(#breeze-release-management-constraints-version-check-line-29)">╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</text><text
 class="breeze-release-management-constraints-version-check-r1" x="1464" 
y="727.6" textLength="12.2" 
clip-path="url(#breeze-release-management-constraints-version-check-line-29)">
 </text>
     </g>
     </g>
diff --git 
a/dev/breeze/doc/images/output_release-management_constraints-version-check.txt 
b/dev/breeze/doc/images/output_release-management_constraints-version-check.txt
index 5f4e9134458..5f03ceac2f5 100644
--- 
a/dev/breeze/doc/images/output_release-management_constraints-version-check.txt
+++ 
b/dev/breeze/doc/images/output_release-management_constraints-version-check.txt
@@ -1 +1 @@
-69595e717626710cce603db050e76a21
+9997441e5cb12ba360db1756028718d3
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 893f4167ebb..ca24ba0daf3 100644
--- a/dev/breeze/src/airflow_breeze/commands/release_management_commands.py
+++ b/dev/breeze/src/airflow_breeze/commands/release_management_commands.py
@@ -4253,6 +4253,13 @@ def publish_docs_to_s3(
     default=False,
     help="Show explanations for outdated packages.",
 )
[email protected](
+    "--cooldown-days",
+    type=int,
+    default=4,
+    show_default=True,
+    help="Ignore package versions released within this many days (cooldown 
period).",
+)
 @option_github_token
 @option_github_repository
 @option_verbose
@@ -4263,6 +4270,7 @@ def version_check(
     diff_mode,
     package: tuple[str],
     explain_why: bool,
+    cooldown_days: int,
     github_token: str,
     github_repository: str,
     builder: str,
@@ -4288,6 +4296,7 @@ def version_check(
         explain_why=explain_why,
         github_token=github_token,
         github_repository=github_repository,
+        cooldown_days=cooldown_days,
     )
 
 
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 e6a1fbfa9bf..6381bdadb31 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
@@ -515,6 +515,7 @@ RELEASE_MANAGEMENT_PARAMETERS: dict[str, list[dict[str, str 
| list[str]]]] = {
                 "--diff-mode",
                 "--package",
                 "--explain-why",
+                "--cooldown-days",
             ],
         },
         {
diff --git a/dev/breeze/src/airflow_breeze/utils/constraints_version_check.py 
b/dev/breeze/src/airflow_breeze/utils/constraints_version_check.py
index aea56854f85..9114b2d1089 100755
--- a/dev/breeze/src/airflow_breeze/utils/constraints_version_check.py
+++ b/dev/breeze/src/airflow_breeze/utils/constraints_version_check.py
@@ -24,7 +24,7 @@ import sys
 import tempfile
 import urllib.request
 from contextlib import contextmanager
-from datetime import datetime
+from datetime import datetime, timedelta
 from pathlib import Path
 from typing import TYPE_CHECKING, Any
 from urllib.error import HTTPError, URLError
@@ -168,6 +168,38 @@ def should_show_package(releases, latest_version, 
constraints_date, mode, is_lat
     return True
 
 
+def get_latest_version_with_cooldown(releases: dict[str, Any], cooldown_days: 
int) -> str | None:
+    """Find the latest non-prerelease version whose release date is outside 
the cooldown period.
+
+    Returns the version string, or None if no version qualifies.
+    """
+    from packaging import version
+
+    cutoff = datetime.now() - timedelta(days=cooldown_days)
+    candidates: list[tuple[version.Version, str]] = []
+    for v, release_files in releases.items():
+        if not release_files:
+            continue
+        try:
+            parsed_v = version.parse(v)
+        except version.InvalidVersion:
+            continue
+        if parsed_v.is_prerelease or parsed_v.is_devrelease:
+            continue
+        try:
+            upload_time = datetime.fromisoformat(
+                release_files[0]["upload_time_iso_8601"].replace("Z", "+00:00")
+            ).replace(tzinfo=None)
+        except (KeyError, IndexError, ValueError):
+            continue
+        if upload_time <= cutoff:
+            candidates.append((parsed_v, v))
+    if not candidates:
+        return None
+    candidates.sort(key=lambda x: x[0], reverse=True)
+    return candidates[0][1]
+
+
 def get_first_newer_release_date_str(releases, current_version):
     from packaging import version
 
@@ -210,9 +242,11 @@ def constraints_version_check(
     explain_why: bool = False,
     github_token: str | None = None,
     github_repository: str | None = None,
+    cooldown_days: int = 4,
 ):
     console_print(f"[bold cyan]Python version:[/] [white]{python}[/]")
-    console_print(f"[bold cyan]Constraints mode:[/] 
[white]{airflow_constraints_mode}[/]\n")
+    console_print(f"[bold cyan]Constraints mode:[/] 
[white]{airflow_constraints_mode}[/]")
+    console_print(f"[bold cyan]Cooldown period:[/] [white]{cooldown_days} 
days[/]\n")
     with tempfile.TemporaryDirectory() as temp_dir:
         constraints_file = Path(temp_dir) / "constraints.txt"
         download_constraints_file(
@@ -248,6 +282,7 @@ def constraints_version_check(
         python_version=python,
         airflow_constraints_mode=airflow_constraints_mode,
         github_repository=github_repository,
+        cooldown_days=cooldown_days,
     )
 
     print_table_footer(
@@ -386,6 +421,7 @@ def process_packages(
     python_version: str,
     airflow_constraints_mode: str,
     github_repository: str | None,
+    cooldown_days: int = 4,
 ) -> tuple[int, int, list[str], dict[str, int]]:
     @contextmanager
     def preserve_pyproject_file(pyproject_path: Path):
@@ -417,8 +453,9 @@ def process_packages(
     for pkg, pinned_version in packages:
         try:
             data = fetch_pypi_data(pkg)
-            latest_version = data["info"]["version"]
             releases = data["releases"]
+            latest_version_with_cooldown = 
get_latest_version_with_cooldown(releases, cooldown_days)
+            latest_version = latest_version_with_cooldown or 
data["info"]["version"]
             latest_release_date = get_release_dates(releases, latest_version)
             constraint_release_date = get_release_dates(releases, 
pinned_version)
             is_latest_version = pinned_version == latest_version

Reply via email to