Skip to content

Commit ce05c9f

Browse files
committed
Add a command to run a given Linux CI job locally
1 parent 55865c8 commit ce05c9f

File tree

1 file changed

+67
-19
lines changed
  • src/ci/github-actions

1 file changed

+67
-19
lines changed

src/ci/github-actions/ci.py

+67-19
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
#!/usr/bin/env python3
22

33
"""
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.
67
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`.
99
"""
1010

11+
import argparse
1112
import dataclasses
1213
import json
1314
import logging
1415
import os
1516
import re
17+
import subprocess
1618
import typing
1719
from pathlib import Path
1820
from typing import List, Dict, Any, Optional
@@ -181,30 +183,76 @@ def format_run_type(run_type: WorkflowRunType) -> str:
181183
raise AssertionError()
182184

183185

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+
184216
if __name__ == "__main__":
185217
logging.basicConfig(level=logging.INFO)
186218

187219
with open(JOBS_YAML_PATH) as f:
188220
data = yaml.safe_load(f)
189221

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()
191232

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()
194235

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}")
197238

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()
202241

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)
205246

206-
run_type = format_run_type(run_type)
247+
if not jobs:
248+
raise Exception("Scheduled job list is empty, this is an error")
207249

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

Comments
 (0)