|
3 | 3 | import os |
4 | 4 | import dataclasses |
5 | 5 | import json |
| 6 | +import requests |
6 | 7 |
|
7 | 8 | from enum import Enum |
8 | | -from typing import Any, Dict, List, Final, Set, Union |
| 9 | +from typing import Any, Dict, List, Final, Set, Union, Optional |
9 | 10 |
|
10 | 11 | MANAGED_OWNER: Final[str] = "kernel-patches" |
11 | 12 | MANAGED_REPOS: Final[Set[str]] = { |
@@ -66,22 +67,14 @@ class BuildConfig: |
66 | 67 | run_veristat: bool = False |
67 | 68 | parallel_tests: bool = False |
68 | 69 | build_release: bool = False |
| 70 | + build_runs_on: Optional[List[str]] = dataclasses.field(default_factory=lambda: [DEFAULT_RUNNER]) |
69 | 71 |
|
70 | 72 | @property |
71 | 73 | def runs_on(self) -> List[str]: |
72 | 74 | if is_managed_repo(): |
73 | 75 | return DEFAULT_SELF_HOSTED_RUNNER_TAGS + [self.arch.value] |
74 | 76 | return [DEFAULT_RUNNER] |
75 | 77 |
|
76 | | - @property |
77 | | - def build_runs_on(self) -> List[str]: |
78 | | - if is_managed_repo(): |
79 | | - # Build s390x on x86_64 |
80 | | - return DEFAULT_SELF_HOSTED_RUNNER_TAGS + [ |
81 | | - self.arch.value == "s390x" and Arch.X86_64.value or self.arch.value, |
82 | | - ] |
83 | | - return [DEFAULT_RUNNER] |
84 | | - |
85 | 78 | @property |
86 | 79 | def tests(self) -> Dict[str, Any]: |
87 | 80 | tests_list = [ |
@@ -158,38 +151,115 @@ def generate_test_config(test: str) -> Dict[str, Union[str, int]]: |
158 | 151 | return config |
159 | 152 |
|
160 | 153 |
|
| 154 | +def query_runners() -> Optional[List[Dict[str, Any]]]: |
| 155 | + token = os.environ["GITHUB_TOKEN"] |
| 156 | + headers = { |
| 157 | + "Authorization": f"token {token}", |
| 158 | + "Accept": "application/vnd.github.v3+json" |
| 159 | + } |
| 160 | + owner = os.environ["GITHUB_REPOSITORY_OWNER"] |
| 161 | + url = f"https://api.github.com/orgs/{owner}/actions/runners" |
| 162 | + # GitHub returns 30 runners per page, fetch all |
| 163 | + all_runners = [] |
| 164 | + try: |
| 165 | + while url: |
| 166 | + response = requests.get(url, headers=headers) |
| 167 | + if response.status_code != 200: |
| 168 | + return None |
| 169 | + data = response.json() |
| 170 | + all_runners.extend(data.get('runners', [])) |
| 171 | + # Check for next page URL in Link header |
| 172 | + url = None |
| 173 | + if 'Link' in response.headers: |
| 174 | + links = requests.utils.parse_header_links(response.headers['Link']) |
| 175 | + for link in links: |
| 176 | + if link['rel'] == 'next': |
| 177 | + url = link['url'] |
| 178 | + break |
| 179 | + return all_runners |
| 180 | + except Exception as e: |
| 181 | + print(f"Warning: Failed to query runner status: {e}") |
| 182 | + return None |
| 183 | + |
| 184 | + |
| 185 | +def is_x86_64_runner(runner: Dict[str, Any]) -> bool: |
| 186 | + labels = [label['name'] for label in runner['labels']] |
| 187 | + for label in DEFAULT_SELF_HOSTED_RUNNER_TAGS: |
| 188 | + if label not in labels: |
| 189 | + return False |
| 190 | + return 'x86_64' in labels |
| 191 | + |
| 192 | + |
| 193 | +def x86_64_runners_too_busy() -> bool: |
| 194 | + |
| 195 | + print(f"is_managed_repo(): {is_managed_repo()}") |
| 196 | + |
| 197 | + if not is_managed_repo(): |
| 198 | + return False |
| 199 | + |
| 200 | + runners = query_runners() |
| 201 | + if not runners: |
| 202 | + print("Warning: failed to query runner status from GitHub") |
| 203 | + return False |
| 204 | + |
| 205 | + x86_64_runners = [r for r in runners if is_x86_64_runner(r)] |
| 206 | + n_busy = 0 |
| 207 | + n_idle = 0 |
| 208 | + n_offline = 0 |
| 209 | + for runner in x86_64_runners: |
| 210 | + if runner['status'] == 'online': |
| 211 | + if runner['busy']: |
| 212 | + n_busy += 1 |
| 213 | + else: |
| 214 | + n_idle += 1 |
| 215 | + else: |
| 216 | + n_offline += 1 |
| 217 | + too_busy = n_idle == 0 or (n_busy / (n_idle + n_busy)) > 0.8 |
| 218 | + |
| 219 | + print(f"GitHub says we have {len(x86_64_runners)} x86_64 runners") |
| 220 | + print(f"Busy: {n_busy}, Idle: {n_idle}, Offline: {n_offline}") |
| 221 | + print(f"x86_64 runners too busy: {too_busy}") |
| 222 | + |
| 223 | + return too_busy |
| 224 | + |
| 225 | + |
161 | 226 | if __name__ == "__main__": |
| 227 | + |
| 228 | + if x86_64_runners_too_busy(): |
| 229 | + # this value is checked for in kernel-build.yml |
| 230 | + x86_64_builder = ['codebuild'] |
| 231 | + else: |
| 232 | + x86_64_builder = DEFAULT_SELF_HOSTED_RUNNER_TAGS + [Arch.X86_64.value] |
| 233 | + |
162 | 234 | matrix = [ |
163 | 235 | BuildConfig( |
164 | 236 | arch=Arch.X86_64, |
165 | 237 | toolchain=Toolchain(compiler=Compiler.GCC, version=DEFAULT_LLVM_VERSION), |
166 | 238 | run_veristat=True, |
167 | 239 | parallel_tests=True, |
| 240 | + build_runs_on=x86_64_builder, |
168 | 241 | ), |
169 | 242 | BuildConfig( |
170 | 243 | arch=Arch.X86_64, |
171 | 244 | toolchain=Toolchain(compiler=Compiler.LLVM, version=DEFAULT_LLVM_VERSION), |
172 | 245 | build_release=True, |
| 246 | + build_runs_on=x86_64_builder, |
173 | 247 | ), |
174 | 248 | BuildConfig( |
175 | 249 | arch=Arch.X86_64, |
176 | 250 | toolchain=Toolchain(compiler=Compiler.LLVM, version=18), |
177 | 251 | build_release=True, |
| 252 | + build_runs_on=x86_64_builder, |
178 | 253 | ), |
179 | 254 | BuildConfig( |
180 | 255 | arch=Arch.AARCH64, |
181 | 256 | toolchain=Toolchain(compiler=Compiler.GCC, version=DEFAULT_LLVM_VERSION), |
| 257 | + build_runs_on=DEFAULT_SELF_HOSTED_RUNNER_TAGS + [Arch.AARCH64.value], |
182 | 258 | ), |
183 | | - # BuildConfig( |
184 | | - # arch=Arch.AARCH64, |
185 | | - # toolchain=Toolchain( |
186 | | - # compiler=Compiler.LLVM, |
187 | | - # version=DEFAULT_LLVM_VERSION |
188 | | - # ), |
189 | | - # ), |
190 | 259 | BuildConfig( |
191 | 260 | arch=Arch.S390X, |
192 | 261 | toolchain=Toolchain(compiler=Compiler.GCC, version=DEFAULT_LLVM_VERSION), |
| 262 | + build_runs_on=x86_64_builder, |
193 | 263 | ), |
194 | 264 | ] |
195 | 265 |
|
|
0 commit comments