mirror of
https://github.com/apache/airflow.git
synced 2026-11-04 16:01:19 +00:00
CI no longer runs Python 3.10, so the tomli fallbacks for tomllib, the typing_extensions imports of Self, Unpack and assert_never, the re-implementation of asyncio.Runner in the Task SDK and the version gated test skips no longer have a version to serve.
202 lines
7.1 KiB
Python
Executable File
202 lines
7.1 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
# 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.
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import shlex
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
if __name__ not in ("__main__", "__mp_main__"):
|
|
raise SystemExit(
|
|
"This file is intended to be executed as an executable program. You cannot use it as a module."
|
|
f"To run this script, run the ./{__file__} command"
|
|
)
|
|
|
|
|
|
def clean_up_airflow_home(airflow_home: Path):
|
|
if airflow_home.exists():
|
|
print(f"Removing {airflow_home}")
|
|
shutil.rmtree(airflow_home, ignore_errors=True)
|
|
|
|
|
|
def check_if_in_virtualenv() -> bool:
|
|
return hasattr(sys, "real_prefix") or (hasattr(sys, "base_prefix") and sys.base_prefix != sys.prefix)
|
|
|
|
|
|
def check_for_package_extras() -> str:
|
|
"""
|
|
check if the user provided any extra packages to install.
|
|
defaults to package 'dev'.
|
|
"""
|
|
if len(sys.argv) > 1:
|
|
if len(sys.argv) > 2:
|
|
print('Provide extras as 1 argument like: "dev,google,snowflake"')
|
|
sys.exit(1)
|
|
return sys.argv[1]
|
|
return "dev"
|
|
|
|
|
|
def get_dependency_groups(pyproject_toml_path: Path) -> list[str]:
|
|
"""
|
|
Get the dependency groups from pyproject.toml
|
|
"""
|
|
import tomllib
|
|
|
|
airflow_core_toml_dict = tomllib.loads(pyproject_toml_path.read_text())
|
|
return airflow_core_toml_dict["dependency-groups"].keys()
|
|
|
|
|
|
def uv_install_requirements(airflow_pyproject_toml_file: Path) -> int:
|
|
"""
|
|
install the requirements of the current python version.
|
|
return 0 if success, anything else is an error.
|
|
"""
|
|
|
|
extras = check_for_package_extras()
|
|
print(
|
|
f"""
|
|
Installing requirements.
|
|
|
|
Airflow is installed with "{extras}" extra.
|
|
|
|
----------------------------------------------------------------------------------------
|
|
|
|
IMPORTANT NOTE ABOUT EXTRAS !!!
|
|
|
|
You can specify extras as single coma-separated parameter to install. For example
|
|
|
|
* dev - to have all development dependencies required to test core.
|
|
* docs - to install dependencies required for documentation building
|
|
* google,amazon,microsoft.azure - to install dependencies needed at runtime by specified providers
|
|
* all - to have all provider dependencies
|
|
|
|
Note that "all" installs all possible dependencies and we have > 600 of them,
|
|
which might not be possible to install cleanly on your host because of lack of
|
|
system packages. It's easier to install extras one-by-one as needed.
|
|
|
|
----------------------------------------------------------------------------------------
|
|
|
|
"""
|
|
)
|
|
dependency_groups = get_dependency_groups(airflow_pyproject_toml_file)
|
|
extra_param = [
|
|
flag
|
|
for extra in extras.split(",")
|
|
for flag in (["--group", extra] if extra in dependency_groups else ["--extra", extra])
|
|
]
|
|
uv_install_command = ["uv", "sync"] + extra_param
|
|
quoted_command = " ".join([shlex.quote(parameter) for parameter in uv_install_command])
|
|
print()
|
|
print(f"Running command: \n {quoted_command}\n")
|
|
e = subprocess.run(uv_install_command, check=False)
|
|
return e.returncode
|
|
|
|
|
|
def get_python_version() -> str:
|
|
"""
|
|
return the version of python we are running.
|
|
"""
|
|
major = sys.version_info[0]
|
|
minor = sys.version_info[1]
|
|
micro = sys.version_info[2]
|
|
return f"{major}.{minor}.{micro}"
|
|
|
|
|
|
def main():
|
|
"""
|
|
Setup local virtual environment.
|
|
"""
|
|
airflow_home_dir = Path(os.environ.get("AIRFLOW_HOME", Path.home() / "airflow"))
|
|
airflow_sources = Path(__file__).resolve().parents[2]
|
|
|
|
if not check_if_in_virtualenv():
|
|
version = get_python_version()
|
|
e = subprocess.run(["uv", "venv", "--python", version], check=False)
|
|
if e.returncode != 0:
|
|
print(f"There was a problem with 'uv venv'. Error code: {e.returncode}")
|
|
|
|
print("Initializing environment...")
|
|
print(f"This will remove the folder {airflow_home_dir} and reset all the databases!")
|
|
response = input("Are you sure? (y/N/q)")
|
|
if response != "y":
|
|
sys.exit(2)
|
|
|
|
print(f"\nWiping and recreating {airflow_home_dir}")
|
|
|
|
if airflow_home_dir == airflow_sources:
|
|
print("AIRFLOW_HOME and Source code are in the same path")
|
|
print(
|
|
f"When running this script it will delete all files in path {airflow_home_dir} "
|
|
"to clear dynamic files like config/logs/db"
|
|
)
|
|
print("Please move the airflow source code elsewhere to avoid deletion")
|
|
|
|
sys.exit(3)
|
|
|
|
clean_up_airflow_home(airflow_home_dir)
|
|
|
|
airflow_pyproject_toml_file = airflow_sources / "pyproject.toml"
|
|
return_code = uv_install_requirements(airflow_pyproject_toml_file)
|
|
|
|
if return_code != 0:
|
|
print(
|
|
"To solve persisting issues with the installation, you might need the "
|
|
"prerequisites installed on your system.\n "
|
|
"Try running the command below and rerun virtualenv installation\n"
|
|
)
|
|
|
|
os_type = sys.platform
|
|
if os_type == "darwin":
|
|
print("brew install sqlite mysql postgresql openssl pkg-config")
|
|
print('export LDFLAGS="-L/usr/local/opt/openssl/lib"')
|
|
print('export CPPFLAGS="-I/usr/local/opt/openssl/include"')
|
|
else:
|
|
print(
|
|
"sudo apt install build-essential python3-dev libsqlite3-dev openssl "
|
|
"sqlite3 default-libmysqlclient-dev libmysqlclient-dev postgresql pkg-config"
|
|
)
|
|
sys.exit(4)
|
|
|
|
print("\nResetting AIRFLOW sqlite database...")
|
|
env = os.environ.copy()
|
|
env["AIRFLOW__CORE__LOAD_EXAMPLES"] = "False"
|
|
env["AIRFLOW__CORE__UNIT_TEST_MODE"] = "False"
|
|
env["AIRFLOW__DATABASE__SQL_ALCHEMY_POOL_ENABLED"] = "False"
|
|
env["AIRFLOW__CORE__DAGS_FOLDER"] = f"{airflow_sources}/empty"
|
|
env["AIRFLOW__CORE__PLUGINS_FOLDER"] = f"{airflow_sources}/empty"
|
|
subprocess.run(["uv", "run", "airflow", "db", "reset", "--yes"], check=False, env=env)
|
|
|
|
print("\nResetting AIRFLOW sqlite unit test database...")
|
|
env = os.environ.copy()
|
|
env["AIRFLOW__CORE__LOAD_EXAMPLES"] = "True"
|
|
env["AIRFLOW__CORE__UNIT_TEST_MODE"] = "False"
|
|
env["AIRFLOW__DATABASE__SQL_ALCHEMY_POOL_ENABLED"] = "False"
|
|
env["AIRFLOW__CORE__DAGS_FOLDER"] = f"{airflow_sources}/empty"
|
|
env["AIRFLOW__CORE__PLUGINS_FOLDER"] = f"{airflow_sources}/empty"
|
|
subprocess.run(["uv", "run", "airflow", "db", "reset", "--yes"], check=False, env=env)
|
|
|
|
print("\nInitialization of environment complete! Go ahead and develop Airflow!")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|