Some checks are pending
CodeQL / Analyze (csharp) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
dotnet-build-and-test / paths-filter (push) Waiting to run
dotnet-build-and-test / dotnet-build-and-test (Debug, windows-latest, net9.0) (push) Blocked by required conditions
dotnet-build-and-test / dotnet-build-and-test (Release, integration, true, ubuntu-latest, net10.0) (push) Blocked by required conditions
dotnet-build-and-test / dotnet-build-and-test (Release, integration, true, windows-latest, net472) (push) Blocked by required conditions
dotnet-build-and-test / dotnet-build-and-test (Release, ubuntu-latest, net8.0) (push) Blocked by required conditions
dotnet-build-and-test / dotnet-build-and-test-check (push) Blocked by required conditions
134 lines
4.7 KiB
Python
134 lines
4.7 KiB
Python
# Copyright (c) Microsoft. All rights reserved.
|
|
|
|
"""Run a task only in packages that have changed files."""
|
|
|
|
import argparse
|
|
import glob
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import tomli
|
|
from poethepoet.app import PoeThePoet
|
|
from rich import print
|
|
|
|
|
|
def discover_projects(workspace_pyproject_file: Path) -> list[Path]:
|
|
with workspace_pyproject_file.open("rb") as f:
|
|
data = tomli.load(f)
|
|
|
|
projects = data["tool"]["uv"]["workspace"]["members"]
|
|
exclude = data["tool"]["uv"]["workspace"].get("exclude", [])
|
|
|
|
all_projects: list[Path] = []
|
|
for project in projects:
|
|
if "*" in project:
|
|
globbed = glob.glob(str(project), root_dir=workspace_pyproject_file.parent)
|
|
globbed_paths = [Path(p) for p in globbed]
|
|
all_projects.extend(globbed_paths)
|
|
else:
|
|
all_projects.append(Path(project))
|
|
|
|
for project in exclude:
|
|
if "*" in project:
|
|
globbed = glob.glob(str(project), root_dir=workspace_pyproject_file.parent)
|
|
globbed_paths = [Path(p) for p in globbed]
|
|
all_projects = [p for p in all_projects if p not in globbed_paths]
|
|
else:
|
|
all_projects = [p for p in all_projects if p != Path(project)]
|
|
|
|
return all_projects
|
|
|
|
|
|
def extract_poe_tasks(file: Path) -> set[str]:
|
|
with file.open("rb") as f:
|
|
data = tomli.load(f)
|
|
|
|
tasks = set(data.get("tool", {}).get("poe", {}).get("tasks", {}).keys())
|
|
|
|
# Check if there is an include too
|
|
include: str | None = data.get("tool", {}).get("poe", {}).get("include", None)
|
|
if include:
|
|
include_file = file.parent / include
|
|
if include_file.exists():
|
|
tasks = tasks.union(extract_poe_tasks(include_file))
|
|
|
|
return tasks
|
|
|
|
|
|
def get_changed_packages(projects: list[Path], changed_files: list[str], workspace_root: Path) -> set[Path]:
|
|
"""Determine which packages have changed files."""
|
|
changed_packages: set[Path] = set()
|
|
core_package_changed = False
|
|
|
|
for file_path in changed_files:
|
|
# Strip 'python/' prefix if present (when git diff is run from repo root)
|
|
file_path_str = str(file_path)
|
|
if file_path_str.startswith("python/"):
|
|
file_path_str = file_path_str[7:] # Remove 'python/' prefix
|
|
|
|
# Convert to absolute path if relative
|
|
abs_path = Path(file_path_str)
|
|
if not abs_path.is_absolute():
|
|
abs_path = workspace_root / file_path_str
|
|
|
|
# Check which package this file belongs to
|
|
for project in projects:
|
|
project_abs = workspace_root / project
|
|
try:
|
|
# Check if the file is within this project directory
|
|
abs_path.relative_to(project_abs)
|
|
changed_packages.add(project)
|
|
# Check if the core package was changed
|
|
if project == Path("packages/core"):
|
|
core_package_changed = True
|
|
break
|
|
except ValueError:
|
|
# File is not in this project
|
|
continue
|
|
|
|
# If core package changed, check all packages
|
|
if core_package_changed:
|
|
print("[yellow]Core package changed - checking all packages[/yellow]")
|
|
return set(projects)
|
|
|
|
return changed_packages
|
|
|
|
|
|
def main() -> None:
|
|
parser = argparse.ArgumentParser(description="Run a task only in packages with changed files.")
|
|
parser.add_argument("task", help="The task name to run")
|
|
parser.add_argument("files", nargs="*", help="Changed files to determine which packages to run")
|
|
args = parser.parse_args()
|
|
|
|
pyproject_file = Path(__file__).parent / "pyproject.toml"
|
|
workspace_root = pyproject_file.parent
|
|
projects = discover_projects(pyproject_file)
|
|
|
|
# If no files specified, run in all packages (default behavior)
|
|
if not args.files or args.files == ["."]:
|
|
print(f"[yellow]No specific files provided, running {args.task} in all packages[/yellow]")
|
|
changed_packages = set(projects)
|
|
else:
|
|
changed_packages = get_changed_packages(projects, args.files, workspace_root)
|
|
if changed_packages:
|
|
print(f"[cyan]Detected changes in packages: {', '.join(str(p) for p in sorted(changed_packages))}[/cyan]")
|
|
else:
|
|
print(f"[yellow]No changes detected in any package, skipping {args.task}[/yellow]")
|
|
return
|
|
|
|
# Run the task in changed packages
|
|
for project in sorted(changed_packages):
|
|
tasks = extract_poe_tasks(project / "pyproject.toml")
|
|
if args.task in tasks:
|
|
print(f"Running task {args.task} in {project}")
|
|
app = PoeThePoet(cwd=project)
|
|
result = app(cli_args=[args.task])
|
|
if result:
|
|
sys.exit(result)
|
|
else:
|
|
print(f"Task {args.task} not found in {project}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|