|
1 | 1 | #!/usr/bin/env python3
|
2 | 2 |
|
3 | 3 | """
|
4 |
| -This script serves for generating a matrix of jobs that should |
5 |
| -be executed on CI. |
| 4 | +This script contains CI functionality. |
| 5 | +It can be used to generate a matrix of jobs that should |
| 6 | +be executed on CI, or run a specific CI job locally. |
6 | 7 |
|
7 |
| -It reads job definitions from `src/ci/github-actions/jobs.yml` |
8 |
| -and filters them based on the event that happened on CI. |
| 8 | +It reads job definitions from `src/ci/github-actions/jobs.yml`. |
9 | 9 | """
|
10 | 10 |
|
| 11 | +import argparse |
11 | 12 | import dataclasses
|
12 | 13 | import json
|
13 | 14 | import logging
|
14 | 15 | import os
|
15 | 16 | import re
|
| 17 | +import subprocess |
16 | 18 | import typing
|
17 | 19 | from pathlib import Path
|
18 | 20 | from typing import List, Dict, Any, Optional
|
@@ -181,30 +183,76 @@ def format_run_type(run_type: WorkflowRunType) -> str:
|
181 | 183 | raise AssertionError()
|
182 | 184 |
|
183 | 185 |
|
| 186 | +def run_workflow_locally(job_data: Dict[str, Any], job_name: str): |
| 187 | + DOCKER_DIR = Path(__file__).absolute().parent.parent / "docker" |
| 188 | + |
| 189 | + jobs = list(job_data["auto"]) |
| 190 | + jobs.extend(job_data["pr"]) |
| 191 | + |
| 192 | + jobs = [job for job in jobs if job.get("image") == job_name] |
| 193 | + if len(jobs) == 0: |
| 194 | + raise Exception(f"Job `{job_name}` not found") |
| 195 | + job = jobs[0] |
| 196 | + if "ubuntu" not in job["os"]: |
| 197 | + raise Exception("Only Linux jobs can be executed locally") |
| 198 | + |
| 199 | + image = job.get("env", {}).get("IMAGE", job["image"]) |
| 200 | + custom_env = {} |
| 201 | + custom_env["DEPLOY"] = "1" |
| 202 | + custom_env.update({k: str(v) for (k, v) in job.get("env", {}).items()}) |
| 203 | + |
| 204 | + args = [ |
| 205 | + str(DOCKER_DIR / "run.sh"), |
| 206 | + image |
| 207 | + ] |
| 208 | + env_formatted = [f"{k}={v}" for (k, v) in sorted(custom_env.items())] |
| 209 | + print(f"Executing `{' '.join(env_formatted)} {' '.join(args)}`") |
| 210 | + |
| 211 | + env = os.environ.copy() |
| 212 | + env.update(custom_env) |
| 213 | + subprocess.run(args, env=env) |
| 214 | + |
| 215 | + |
184 | 216 | if __name__ == "__main__":
|
185 | 217 | logging.basicConfig(level=logging.INFO)
|
186 | 218 |
|
187 | 219 | with open(JOBS_YAML_PATH) as f:
|
188 | 220 | data = yaml.safe_load(f)
|
189 | 221 |
|
190 |
| - github_ctx = get_github_ctx() |
| 222 | + parser = argparse.ArgumentParser( |
| 223 | + prog="ci.py", |
| 224 | + description="Generate or run CI workflows" |
| 225 | + ) |
| 226 | + generate_matrix = argparse.ArgumentParser() |
| 227 | + subparsers = parser.add_subparsers(help="Command to execute", dest="command", required=True) |
| 228 | + subparsers.add_parser("calculate-job-matrix") |
| 229 | + run_parser = subparsers.add_parser("run-local") |
| 230 | + run_parser.add_argument("job_name", help="CI job that should be executed") |
| 231 | + args = parser.parse_args() |
191 | 232 |
|
192 |
| - run_type = find_run_type(github_ctx) |
193 |
| - logging.info(f"Job type: {run_type}") |
| 233 | + if args.command == "calculate-job-matrix": |
| 234 | + github_ctx = get_github_ctx() |
194 | 235 |
|
195 |
| - with open(CI_DIR / "channel") as f: |
196 |
| - channel = f.read().strip() |
| 236 | + run_type = find_run_type(github_ctx) |
| 237 | + logging.info(f"Job type: {run_type}") |
197 | 238 |
|
198 |
| - jobs = [] |
199 |
| - if run_type is not None: |
200 |
| - jobs = calculate_jobs(run_type, data) |
201 |
| - jobs = skip_jobs(jobs, channel) |
| 239 | + with open(CI_DIR / "channel") as f: |
| 240 | + channel = f.read().strip() |
202 | 241 |
|
203 |
| - if not jobs: |
204 |
| - raise Exception("Scheduled job list is empty, this is an error") |
| 242 | + jobs = [] |
| 243 | + if run_type is not None: |
| 244 | + jobs = calculate_jobs(run_type, data) |
| 245 | + jobs = skip_jobs(jobs, channel) |
205 | 246 |
|
206 |
| - run_type = format_run_type(run_type) |
| 247 | + if not jobs: |
| 248 | + raise Exception("Scheduled job list is empty, this is an error") |
207 | 249 |
|
208 |
| - logging.info(f"Output:\n{yaml.dump(dict(jobs=jobs, run_type=run_type), indent=4)}") |
209 |
| - print(f"jobs={json.dumps(jobs)}") |
210 |
| - print(f"run_type={run_type}") |
| 250 | + run_type = format_run_type(run_type) |
| 251 | + |
| 252 | + logging.info(f"Output:\n{yaml.dump(dict(jobs=jobs, run_type=run_type), indent=4)}") |
| 253 | + print(f"jobs={json.dumps(jobs)}") |
| 254 | + print(f"run_type={run_type}") |
| 255 | + elif args.command == "run-local": |
| 256 | + run_workflow_locally(data, args.job_name) |
| 257 | + else: |
| 258 | + raise Exception(f"Unknown command {args.command}") |
0 commit comments