|
1 | 1 | # Copyright (c) Microsoft. All rights reserved. |
2 | 2 |
|
3 | | -"""Run a task only in packages that have changed files.""" |
| 3 | +"""Run task(s) only in packages that have changed files, in parallel by default.""" |
4 | 4 |
|
5 | 5 | import argparse |
6 | | -import glob |
7 | | -import sys |
8 | 6 | from pathlib import Path |
9 | 7 |
|
10 | | -import tomli |
11 | | -from poethepoet.app import PoeThePoet |
12 | 8 | from rich import print |
13 | | - |
14 | | - |
15 | | -def discover_projects(workspace_pyproject_file: Path) -> list[Path]: |
16 | | - with workspace_pyproject_file.open("rb") as f: |
17 | | - data = tomli.load(f) |
18 | | - |
19 | | - projects = data["tool"]["uv"]["workspace"]["members"] |
20 | | - exclude = data["tool"]["uv"]["workspace"].get("exclude", []) |
21 | | - |
22 | | - all_projects: list[Path] = [] |
23 | | - for project in projects: |
24 | | - if "*" in project: |
25 | | - globbed = glob.glob(str(project), root_dir=workspace_pyproject_file.parent) |
26 | | - globbed_paths = [Path(p) for p in globbed] |
27 | | - all_projects.extend(globbed_paths) |
28 | | - else: |
29 | | - all_projects.append(Path(project)) |
30 | | - |
31 | | - for project in exclude: |
32 | | - if "*" in project: |
33 | | - globbed = glob.glob(str(project), root_dir=workspace_pyproject_file.parent) |
34 | | - globbed_paths = [Path(p) for p in globbed] |
35 | | - all_projects = [p for p in all_projects if p not in globbed_paths] |
36 | | - else: |
37 | | - all_projects = [p for p in all_projects if p != Path(project)] |
38 | | - |
39 | | - return all_projects |
40 | | - |
41 | | - |
42 | | -def extract_poe_tasks(file: Path) -> set[str]: |
43 | | - with file.open("rb") as f: |
44 | | - data = tomli.load(f) |
45 | | - |
46 | | - tasks = set(data.get("tool", {}).get("poe", {}).get("tasks", {}).keys()) |
47 | | - |
48 | | - # Check if there is an include too |
49 | | - include: str | None = data.get("tool", {}).get("poe", {}).get("include", None) |
50 | | - if include: |
51 | | - include_file = file.parent / include |
52 | | - if include_file.exists(): |
53 | | - tasks = tasks.union(extract_poe_tasks(include_file)) |
54 | | - |
55 | | - return tasks |
| 9 | +from task_runner import build_work_items, discover_projects, run_tasks |
56 | 10 |
|
57 | 11 |
|
58 | 12 | def get_changed_packages(projects: list[Path], changed_files: list[str], workspace_root: Path) -> set[Path]: |
@@ -95,38 +49,32 @@ def get_changed_packages(projects: list[Path], changed_files: list[str], workspa |
95 | 49 |
|
96 | 50 |
|
97 | 51 | def main() -> None: |
98 | | - parser = argparse.ArgumentParser(description="Run a task only in packages with changed files.") |
99 | | - parser.add_argument("task", help="The task name to run") |
100 | | - parser.add_argument("files", nargs="*", help="Changed files to determine which packages to run") |
| 52 | + parser = argparse.ArgumentParser(description="Run task(s) in changed packages, in parallel by default.") |
| 53 | + parser.add_argument("tasks", nargs="+", help="Task name(s) to run") |
| 54 | + parser.add_argument("--files", nargs="*", default=None, help="Changed files to determine which packages to run") |
| 55 | + parser.add_argument("--seq", action="store_true", help="Run sequentially instead of in parallel") |
101 | 56 | args = parser.parse_args() |
102 | 57 |
|
103 | 58 | pyproject_file = Path(__file__).parent.parent / "pyproject.toml" |
104 | 59 | workspace_root = pyproject_file.parent |
105 | 60 | projects = discover_projects(pyproject_file) |
106 | 61 |
|
107 | | - # If no files specified, run in all packages (default behavior) |
| 62 | + # Determine which packages to check |
108 | 63 | if not args.files or args.files == ["."]: |
109 | | - print(f"[yellow]No specific files provided, running {args.task} in all packages[/yellow]") |
110 | | - changed_packages = set(projects) |
| 64 | + task_list = ", ".join(args.tasks) |
| 65 | + print(f"[yellow]No specific files provided, running {task_list} in all packages[/yellow]") |
| 66 | + target_packages = sorted(set(projects)) |
111 | 67 | else: |
112 | 68 | changed_packages = get_changed_packages(projects, args.files, workspace_root) |
113 | 69 | if changed_packages: |
114 | 70 | print(f"[cyan]Detected changes in packages: {', '.join(str(p) for p in sorted(changed_packages))}[/cyan]") |
115 | 71 | else: |
116 | | - print(f"[yellow]No changes detected in any package, skipping {args.task}[/yellow]") |
| 72 | + print(f"[yellow]No changes detected in any package, skipping[/yellow]") |
117 | 73 | return |
| 74 | + target_packages = sorted(changed_packages) |
118 | 75 |
|
119 | | - # Run the task in changed packages |
120 | | - for project in sorted(changed_packages): |
121 | | - tasks = extract_poe_tasks(project / "pyproject.toml") |
122 | | - if args.task in tasks: |
123 | | - print(f"Running task {args.task} in {project}") |
124 | | - app = PoeThePoet(cwd=project) |
125 | | - result = app(cli_args=[args.task]) |
126 | | - if result: |
127 | | - sys.exit(result) |
128 | | - else: |
129 | | - print(f"Task {args.task} not found in {project}") |
| 76 | + work_items = build_work_items(target_packages, args.tasks) |
| 77 | + run_tasks(work_items, workspace_root, sequential=args.seq) |
130 | 78 |
|
131 | 79 |
|
132 | 80 | if __name__ == "__main__": |
|
0 commit comments