Browse Source

runner specs

pull/12/head
Daniel Gyulai 3 years ago
parent
commit
52bd836209
  1. 8
      alice-ci/src/alice/cli.py
  2. 4
      alice-ci/src/alice/runnerfactory.py
  3. 1
      alice-ci/src/alice/runners/__init__.py
  4. 3
      alice-ci/src/alice/runners/pythonrunner.py
  5. 23
      alice-ci/src/alice/utils.py
  6. 25
      docs/runners.md

8
alice-ci/src/alice/cli.py

@ -26,19 +26,19 @@ def parse_jobs(args):
print(f"[Alice] Env vars from CLI: {envs}") print(f"[Alice] Env vars from CLI: {envs}")
jobParser = ConfigParser(args.input, factory, gen_env(args.env), args.verbose) jobParser = ConfigParser(args.input, factory, gen_env(args.env), args.verbose)
print("Begin pipeline steps...") print("[Alice] Begin pipeline steps")
for step in args.steps: for step in args.steps:
if step in jobParser.jobs: if step in jobParser.jobs:
status = jobParser.execute_job(step) status = jobParser.execute_job(step)
print(f"[Step] {step}: {status}") print(f"[Alice][Step] {step}: {status}")
else: else:
print(f"Step {step} not found in {args.input}") raise ConfigException(f"Step {step} not found in {args.input}")
exit(1) exit(1)
except ConfigException as e: except ConfigException as e:
print(f"Configuration error-> {e}") print(f"Configuration error-> {e}")
exit(1) exit(1)
except NonZeroRetcode: except NonZeroRetcode:
print("FAILED") print("[Alice] FAILED")
exit(1) exit(1)
except RunnerError as e: except RunnerError as e:
print(f"RunnerError-> {e}") print(f"RunnerError-> {e}")

4
alice-ci/src/alice/runnerfactory.py

@ -1,5 +1,3 @@
from os import getcwd
from alice.runners.pythonrunner import PythonRunner from alice.runners.pythonrunner import PythonRunner
from alice.exceptions import ConfigException from alice.exceptions import ConfigException
@ -10,7 +8,6 @@ class Factory():
self.runnertypes = self.__load_runners() self.runnertypes = self.__load_runners()
self.runner_configs = {} self.runner_configs = {}
self.runners = {} self.runners = {}
self.workdir = getcwd()
self.globals = {} self.globals = {}
def __load_runners(self): def __load_runners(self):
@ -40,7 +37,6 @@ class Factory():
if (self.verbose): if (self.verbose):
print(f"[Alice] Initializing runner: {runnertype}") print(f"[Alice] Initializing runner: {runnertype}")
params = { params = {
"workdir": self.workdir,
"verbose": self.verbose "verbose": self.verbose
} }
self.runners[runnertype] = self.runnertypes[runnertype](params, self.globals) self.runners[runnertype] = self.runnertypes[runnertype](params, self.globals)

1
alice-ci/src/alice/runners/__init__.py

@ -1 +1,2 @@
# flake8: noqa F401
from alice.runners.pythonrunner import PythonRunner from alice.runners.pythonrunner import PythonRunner

3
alice-ci/src/alice/runners/pythonrunner.py

@ -11,7 +11,7 @@ class PythonRunner():
self.verbose = params["verbose"] self.verbose = params["verbose"]
if self.verbose: if self.verbose:
print("[PythonRunner] Initializing") print("[PythonRunner] Initializing")
self.workdir = params["workdir"] self.workdir = user_defaults["workdir"]
self.virtual_dir = os.path.abspath(os.path.join(self.workdir, "venv")) self.virtual_dir = os.path.abspath(os.path.join(self.workdir, "venv"))
self.config = user_defaults self.config = user_defaults
@ -24,6 +24,7 @@ class PythonRunner():
self.vpython = os.path.join(self.virtual_dir, "bin", "python3") self.vpython = os.path.join(self.virtual_dir, "bin", "python3")
if not os.path.exists(self.vpython): if not os.path.exists(self.vpython):
print("[PythonRunner] Initializing venv")
with subprocess.Popen([sys.executable, "-m", "virtualenv", self.virtual_dir], with subprocess.Popen([sys.executable, "-m", "virtualenv", self.virtual_dir],
stdout=subprocess.PIPE, stderr=subprocess.PIPE) as p: stdout=subprocess.PIPE, stderr=subprocess.PIPE) as p:
p.wait() p.wait()

23
alice-ci/src/alice/utils.py

@ -1,4 +1,4 @@
import os from os import getcwd, path, environ
import subprocess import subprocess
import yaml import yaml
@ -6,23 +6,23 @@ from alice.exceptions import ConfigException
class ConfigParser: class ConfigParser:
def __init__(self, file_path, factory, env_vars, verbose=False) -> None: def __init__(self, file_path, factory, cli_env_vars, verbose=False) -> None:
self.verbose = verbose self.verbose = verbose
with open(file_path) as f: with open(file_path) as f:
self.config = yaml.safe_load(f) self.config = yaml.safe_load(f)
self.factory = factory self.factory = factory
self.factory.set_globals(self.__gen_globals(env_vars)) self.factory.set_globals(self.__gen_globals(cli_env_vars))
if "runners" in self.config: if "runners" in self.config:
self.factory.update_runners(self.config["runners"]) self.factory.update_runners(self.config["runners"])
self.jobs = self.__get_jobs() self.jobs = self.__get_jobs()
# Initialize env and workdir if not present in global # Initialize env and workdir if not present in global
def __gen_globals(self, cli_vars): def __gen_globals(self, cli_vars):
env_vars = os.environ.copy() env_vars = environ.copy()
env_vars.update(cli_vars) env_vars.update(cli_vars)
globals = { globals = {
"env": env_vars, "env": env_vars,
"workdir": None "workdir": getcwd()
} }
if "runners" in self.config: if "runners" in self.config:
if "global" in self.config["runners"]: if "global" in self.config["runners"]:
@ -55,20 +55,20 @@ class ConfigParser:
try: try:
target = changes["branch"] target = changes["branch"]
paths = [] paths = []
for path in changes["paths"]: for _path in changes["paths"]:
paths.append(os.path.abspath(path)) paths.append(path.abspath(_path))
# TODO: Error handling # TODO: Error handling
command = ["git", "diff", "--name-only", target] command = ["git", "diff", "--name-only", target]
with subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as p: with subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as p:
p.wait() p.wait()
for line in p.stdout: for line in p.stdout:
change_path = os.path.abspath(line.decode("UTF-8").strip()) change_path = path.abspath(line.decode("UTF-8").strip())
for path in paths: for _path in paths:
spec_path = os.path.abspath(path) spec_path = path.abspath(_path)
if change_path.startswith(spec_path): if change_path.startswith(spec_path):
if self.verbose: if self.verbose:
print(f"[Alice] Modified file: {change_path}") print(f"[Alice] Modified file: {change_path}")
print(f"[Alice] Path match: {path}") print(f"[Alice] Path match: {_path}")
return True return True
except KeyError: except KeyError:
raise ConfigException(f"Invalid 'changes' config: {changes}") raise ConfigException(f"Invalid 'changes' config: {changes}")
@ -86,4 +86,3 @@ class ConfigParser:
return "SUCCESS" return "SUCCESS"
else: else:
return "SKIP, no change detected" return "SKIP, no change detected"

25
docs/runners.md

@ -9,22 +9,37 @@ Runners are responsible to execute a list of commands in a set environment defin
## Import schema ## Import schema
What you need to do to make Alice recognise and import your custom Runners
TODO TODO
## Runner API ## Runner API
Each runner has to support the following functions: Each runner has to support the following functions:
### Constructor(params, user_defaults) ### __init__(params, user_defaults)
* params: dict of runtime variables for the program itself * params: dict of runtime variables for the program itself.
* user_defaults: raw data from the CI file's global dict, augmented with an "env" dict, which contains environment variables from the host sytem, the CLI params and the pipeline global config, and....? * user_defaults: raw data from the CI file's global dict, augmented with an "env" dict, which contains environment variables from the host sytem, the CLI params and the pipeline global config, and the "workdir" value, which is the absolute path of the directory that the runner shall recognize as the current working directory.
TODO: Clean up workdir as a concept, who should hold it, if any,and how shall it be passed to runners, if necessary. It is - workdir can be assigned at CI yaml level as global
#### Params
Currently the only param used is the dict is "verbose", whichis a boolean. The intended purpose is to show debug output if set to True.
#### Workdir
workdir can be assigned at CI yaml level as global
Order: Order:
By default: os.cwd() By default: os.cwd()
if overwritten in global if overwritten in global
------------------------------- Below this level is the runner's responsibility ------------------------------- Below this level is the runner's responsibility
if owerwritten in runner if owerwritten in runner config
if overwritten in job if overwritten in job
Runner shall receive the current working directory, unless stated otherwise in global config Runner shall receive the current working directory, unless stated otherwise in global config
### update_config(config)
The function takes the raw data from the parsed yaml under runners.(runnername). Handling its own config is the sole responsibility of the runner. This function may be called at any point of the running lifecycle, so the runner has to support changing its own configuration.
### run(job_spec)
This function executes one job attributed ith the type of the runner called. As the only hard requirement for Alice is the "type" field in a job (or the optional "changes"), everything else is handled by the runner.
Loading…
Cancel
Save