forked from Chia-Network/chia-blockchain
-
Notifications
You must be signed in to change notification settings - Fork 0
/
build-job-matrix.py
130 lines (97 loc) · 4.13 KB
/
build-job-matrix.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
from __future__ import annotations
import argparse
import json
import logging
import types
from pathlib import Path
from typing import Any, Dict, List
import testconfig
root_path = Path(__file__).parent.resolve()
project_root_path = root_path.parent
def skip(path: Path) -> bool:
return any(part.startswith(("_", ".")) for part in path.parts)
def subdirs(per: str) -> List[Path]:
if per == "directory":
glob_pattern = "**/"
elif per == "file":
glob_pattern = "**/test_*.py"
else:
raise Exception(f"Unrecognized per: {per!r}")
paths = [path for path in root_path.rglob(glob_pattern) if not skip(path=path)]
if per == "directory":
filtered_paths = []
for path in paths:
relative_path = path.relative_to(root_path)
logging.info(f"Considering: {relative_path}")
if len([f for f in path.glob("test_*.py")]) == 0:
logging.info(f"Skipping {relative_path}: no tests collected")
continue
filtered_paths.append(path)
paths = filtered_paths
return sorted(paths)
def module_dict(module: types.ModuleType) -> Dict[str, Any]:
return {k: v for k, v in module.__dict__.items() if not k.startswith("_")}
def dir_config(dir: Path) -> Dict[str, Any]:
import importlib
module_name = ".".join([*dir.relative_to(root_path).parts, "config"])
try:
return module_dict(importlib.import_module(module_name))
except ModuleNotFoundError:
return {}
# Overwrite with directory specific values
def update_config(parent: Dict[str, Any], child: Dict[str, Any]) -> Dict[str, Any]:
if child is None:
return parent
conf = child
for k, v in parent.items():
if k not in child:
conf[k] = v
return conf
# args
arg_parser = argparse.ArgumentParser(description="Generate GitHub test matrix configuration")
arg_parser.add_argument("--per", type=str, choices=["directory", "file"], required=True)
arg_parser.add_argument("--verbose", "-v", action="store_true")
arg_parser.add_argument("--only", action="append", default=[])
arg_parser.add_argument("--duplicates", type=int, default=1)
args = arg_parser.parse_args()
if args.verbose:
logging.basicConfig(format="%(asctime)s:%(message)s", level=logging.DEBUG)
# main
if len(args.only) == 0:
test_paths = subdirs(per=args.per)
else:
test_paths = [root_path.joinpath(path) for path in args.only]
test_paths = [path for path in test_paths for _ in range(args.duplicates)]
configuration = []
for path in test_paths:
if path.is_dir():
test_files = sorted(path.glob("test_*.py"))
test_file_paths = [file.relative_to(project_root_path) for file in test_files]
paths_for_cli = " ".join(path.as_posix() for path in test_file_paths)
else:
paths_for_cli = path.relative_to(project_root_path).as_posix()
conf = update_config(module_dict(testconfig), dir_config(path))
# TODO: design a configurable system for this
process_count = {
"macos": {False: 0, True: 4}.get(conf["parallel"], conf["parallel"]),
"ubuntu": {False: 0, True: 4}.get(conf["parallel"], conf["parallel"]),
"windows": {False: 0, True: 2}.get(conf["parallel"], conf["parallel"]),
}
pytest_parallel_args = {os: f" -n {count}" for os, count in process_count.items()}
for_matrix = {
"check_resource_usage": conf["check_resource_usage"],
"enable_pytest_monitor": "-p monitor" if conf["check_resource_usage"] else "",
"job_timeout": conf["job_timeout"],
"pytest_parallel_args": pytest_parallel_args,
"checkout_blocks_and_plots": conf["checkout_blocks_and_plots"],
"install_timelord": conf["install_timelord"],
"test_files": paths_for_cli,
"name": ".".join(path.relative_to(root_path).with_suffix("").parts),
"legacy_keyring_required": conf.get("legacy_keyring_required", False),
}
for_matrix = dict(sorted(for_matrix.items()))
configuration.append(for_matrix)
configuration_json = json.dumps(configuration)
for line in json.dumps(configuration, indent=4).splitlines():
logging.info(line)
print(f"{configuration_json}")