scripts/ci/airflow_version_check.py (70 lines of code) (raw):

#! /usr/bin/env python # 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. # /// script # requires-python = ">=3.9" # dependencies = [ # "packaging>=23.2", # "requests>=2.28.1", # "rich>=13.3.5", # ] # /// from __future__ import annotations import re import sys from pathlib import Path import requests from packaging.version import Version from rich.console import Console console = Console(color_system="standard", stderr=True, width=400) def check_airflow_version(airflow_version: Version) -> tuple[str, bool]: """ Check if the given version is a valid Airflow version and latest. airflow_version: The Airflow version to check. returns: tuple containing the version and a boolean indicating if it's latest. """ latest = False url = "https://pypi.org/pypi/apache-airflow/json" max_versions_shown = 30 try: response = requests.get(url) response.raise_for_status() data = response.json() latest_version = Version(data["info"]["version"]) valid_versions = list(reversed(data["releases"].keys()))[:max_versions_shown] if str(airflow_version) not in valid_versions: console.print(f"[red]Version {airflow_version} is not a valid Airflow version") console.print( f"Available versions: (first available {max_versions_shown} versions):", valid_versions ) sys.exit(1) if airflow_version == latest_version: latest = True # find requires-python = "~=VERSION" in pyproject.toml file of airflow pyproject_toml_conntent = (Path(__file__).parents[2] / "pyproject.toml").read_text() matched_version = re.search('requires-python = "~=([0-9]+.[0-9]+)', pyproject_toml_conntent) if matched_version: min_version = matched_version.group(1) else: console.print("[red]Error: requires-python version not found in pyproject.toml") sys.exit(1) constraints_url = ( f"https://raw.githubusercontent.com/apache/airflow/" f"constraints-{airflow_version}/constraints-{min_version}.txt" ) console.print(f"[bright_blue]Checking constraints file: {constraints_url}") response = requests.head(constraints_url) if response.status_code == 404: console.print( f"[red]Error: Constraints file not found for version {airflow_version}. " f"Please set appropriate tag." ) sys.exit(1) response.raise_for_status() console.print(f"[green]Constraints file found for version {airflow_version}, Python {min_version}") return str(airflow_version), latest except Exception as e: console.print(f"[red]Error fetching latest version: {e}") sys.exit(1) def normalize_version(version: str) -> Version: try: return Version(version) except Exception as e: console.print(f"[red]Error normalizing version: {e}") sys.exit(1) def print_both_outputs(output: str): print(output) console.print(output) if __name__ == "__main__": if len(sys.argv) < 2: console.print("[yellow]Usage: python normalize_airflow_version.py <version>") sys.exit(1) version = sys.argv[1] parsed_version = normalize_version(version) actual_version, is_latest = check_airflow_version(parsed_version) print_both_outputs(f"airflowVersion={actual_version}") skip_latest = "false" if is_latest else "true" print_both_outputs(f"skipLatest={skip_latest}")