项目文件夹

文件
wehub-resource-sync a0c8464e58
Build Package / build (ubuntu-latest) (push) Failing after 1s
CodeQL / Analyze (python) (push) Failing after 1s
Core Typecheck / core-typecheck (push) Failing after 1s
Linting / lint (push) Failing after 1s
llama-dev tests / test-llama-dev (push) Failing after 1s
Publish Sub-Package to PyPI if Needed / publish_subpackage_if_needed (push) Has been skipped
Sync Docs to Developer Hub / sync-docs (push) Failing after 0s
Build Package / build (windows-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:26:52 +08:00

208 行
6.7 KiB
Python

import re
import subprocess
import sys
from enum import Enum
from pathlib import Path
import tomli
from packaging import specifiers, version
from packaging.version import Version
DEP_NAME_REGEX = re.compile(r"([^<>=\[\];\s]+)")
class BumpType(str, Enum):
MAJOR = "major"
MINOR = "minor"
PATCH = "patch"
def bump_version(current_version: str, bump_type: BumpType) -> str:
"""Bump a version string according to semver rules."""
v = Version(current_version)
# Parse the version components
release = v.release
major = release[0] if len(release) > 0 else 0
minor = release[1] if len(release) > 1 else 0
micro = release[2] if len(release) > 2 else 0
version_str = ""
if bump_type == BumpType.MAJOR:
version_str = f"{major + 1}.0.0"
elif bump_type == BumpType.MINOR:
version_str = f"{major}.{minor + 1}.0"
elif bump_type == BumpType.PATCH:
version_str = f"{major}.{minor}.{micro + 1}"
return version_str
def update_pyproject_version(package_path: Path, new_version: str) -> None:
"""Update the version in a pyproject.toml file."""
pyproject_path = package_path / "pyproject.toml"
# Read the file content
with open(pyproject_path, "r") as f:
content = f.read()
pattern = r'^version = "[^"]+"'
new_content = re.sub(
pattern, f'version = "{new_version}"', content, flags=re.MULTILINE
)
# Write the updated content back
with open(pyproject_path, "w") as f:
f.write(new_content)
def package_has_tests(package_path: Path) -> bool:
"""Returns whether a package folder contains a 'tests' subfolder."""
tests_folder = package_path / "tests"
return package_path.is_dir() and tests_folder.exists() and tests_folder.is_dir()
def is_llama_index_package(package_path: Path) -> bool:
"""Returns whether a folder contains a 'pyproject.toml' file."""
pyproject = package_path / "pyproject.toml"
return package_path.is_dir() and pyproject.exists() and pyproject.is_file()
def load_pyproject(package_path: Path) -> dict:
"""Thin wrapper around tomli.load()."""
pyproject_path = package_path / "pyproject.toml"
with open(pyproject_path, "rb") as f:
return tomli.load(f)
def find_integrations(root_path: Path, recursive=False) -> list[Path]:
"""Find all integrations packages in the repo."""
package_roots: list[Path] = []
integrations_root = root_path
if not recursive:
integrations_root = integrations_root / "llama-index-integrations"
for category_path in integrations_root.iterdir():
if not category_path.is_dir():
continue
if category_path.name == "storage":
# The "storage" category has sub-folders
package_roots += find_integrations(category_path, recursive=True)
continue
for package_name in category_path.iterdir():
if is_llama_index_package(package_name):
package_roots.append(package_name)
return package_roots
def find_utils(root_path: Path) -> list[Path]:
"""Find all llama-index-utils packages in the repo."""
package_roots: list[Path] = []
utils_root = root_path / "llama-index-utils"
for package_name in utils_root.iterdir():
if is_llama_index_package(package_name):
package_roots.append(package_name)
return package_roots
def find_all_packages(root_path: Path) -> list[Path]:
"""Returns a list of all the package folders in the monorepo."""
return [
root_path / "llama-index-core",
*find_integrations(root_path),
*find_utils(root_path),
root_path / "llama-index-instrumentation",
]
def get_changed_files(repo_root: Path, base_ref: str = "main") -> list[Path]:
"""Use git to get the list of files changed compared to the base branch."""
try:
cmd = ["git", "diff", "--name-only", f"{base_ref}...HEAD"]
result = subprocess.run(cmd, cwd=repo_root, text=True, capture_output=True)
if result.returncode != 0:
raise RuntimeError(f"Git command failed: {result.stderr}")
return [repo_root / Path(f) for f in result.stdout.splitlines() if f.strip()]
except Exception as e:
print(f"Exception occurred: {e!s}")
raise
def get_changed_packages(
changed_files: list[Path], all_packages: list[Path]
) -> set[Path]:
"""Get the list of package folders containing the path in 'changed_files'."""
changed_packages: set[Path] = set()
for file_path in changed_files:
# Find the package containing this file
for pkg_dir in all_packages:
if file_path.absolute().is_relative_to(pkg_dir.absolute()):
changed_packages.add(pkg_dir)
break
return changed_packages
def get_dep_names(pyproject_data: dict) -> set[str]:
"""Load dependencies from pyproject.toml."""
dependencies: set[str] = set()
for dep in pyproject_data["project"]["dependencies"]:
matches = DEP_NAME_REGEX.findall(dep)
if not matches:
continue
dependencies.add(matches[0])
return dependencies
def is_python_version_compatible(pyproject_data: dict) -> bool:
"""Check if the package is compatible with the current Python version using packaging."""
# Get current Python version
current_version = version.Version(
f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
)
# Get Python version requirements if they exist
requires_python = pyproject_data.get("project", {}).get("requires-python")
if requires_python is None:
# If no Python version is specified, assume it's compatible
return True
try:
# Parse the version specifier
spec = specifiers.SpecifierSet(requires_python)
# Check if the current version satisfies the specifier
return spec.contains(str(current_version))
except Exception as e:
# If there's any error in parsing, log it and assume compatibility
print(
f"Warning: Could not parse Python version specifier '{requires_python}': {e}"
)
return True
def get_dependants_packages(
changed_packages: set[Path], all_packages: list[Path]
) -> set[Path]:
"""Get packages containing the files in the changeset."""
changed_packages_names: set[str] = set()
for pkg_path in changed_packages:
pyproject_data = load_pyproject(pkg_path)
changed_packages_names.add(pyproject_data["project"]["name"])
dependants_packages: set[Path] = set()
for pkg_path in all_packages:
pyproject_data = load_pyproject(pkg_path)
for dep_name in get_dep_names(pyproject_data):
if dep_name in changed_packages_names:
dependants_packages.add(pkg_path)
return dependants_packages