Skip to content

Commit 7310e12

Browse files
authored
Testing new ci/cd infra (#55)
Update the CI/CD pipelines * move from generic `alps-uenv` repository to DockerHub `uenv` registry on JFrog * use oras to manage images and associate artifacts with images * parameterize the CI/CD pipeline
1 parent 7d5b6f7 commit 7310e12

35 files changed

+937
-176
lines changed

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,17 @@ log*
22
stackinator
33
recipes/*/*core/mirrors.yaml
44
cache.yaml
5+
/pipeline.yml
56

67
# vim temp files
78
.*.sw[pnoq]
89

910
# documentation working files
1011
site
1112
env
13+
14+
# oras binary, downloaded during build
15+
oras
16+
17+
ci/.pyenv
18+
__pycache__

ci/ci.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
stages:
2+
- configure
3+
- run
4+
5+
pipeline-configure:
6+
stage: configure
7+
tags: [clariden-login-baremetal]
8+
script:
9+
- ./ci/configure-pipeline
10+
artifacts:
11+
paths:
12+
- pipeline.yml
13+
14+
generated-pipeline:
15+
stage: run
16+
needs: [pipeline-configure]
17+
variables:
18+
CSCS_CI_MW_URL: $CSCS_CI_MW_URL
19+
trigger:
20+
include:
21+
- artifact: pipeline.yml
22+
job: pipeline-configure
23+
strategy: depend

ci/common.yml

Lines changed: 0 additions & 67 deletions
This file was deleted.

ci/config/ci.py

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
#!/usr/bin/env python3
2+
3+
import jinja2
4+
import jsonschema
5+
import os
6+
import pathlib
7+
import sys
8+
import yaml
9+
10+
prefix = pathlib.Path(__file__).parent.resolve()
11+
root_path = prefix.parent.resolve().parent.resolve()
12+
recipe_path = root_path / "recipes"
13+
14+
sys.path = [prefix.as_posix()] + sys.path
15+
16+
import configuration
17+
import util
18+
19+
class EnvError(Exception):
20+
"""EnvError when invalid environment variables are provided
21+
"""
22+
23+
def __init__(self, message):
24+
self.message = f"Environment: {message}"
25+
super().__init__(self.message)
26+
27+
def readenv(config):
28+
"""
29+
returns a dictionary with the following fields:
30+
{
31+
'system': 'clariden',
32+
'uarch': 'a100',
33+
'uenv': 'gromacs',
34+
'version': '2023',
35+
'recipe': configuration.Version
36+
}
37+
38+
based on the values of the environment variables:
39+
- system
40+
- uarch
41+
- uenv
42+
"""
43+
44+
system = os.getenv("system", default=None)
45+
uarch = os.getenv("uarch", default=None)
46+
target = os.getenv("uenv", default=None)
47+
48+
if system is None:
49+
raise EnvError("'system' environment variable not set")
50+
if uarch is None:
51+
raise EnvError("'uarch' environment variable not set")
52+
if target is None:
53+
raise EnvError("'uenv' environment variable not set")
54+
55+
# check that system+uarch are valid
56+
if uarch not in configuration.valid_uarch:
57+
raise EnvError(f"uarch={uarch} is not one of the available options {configuration.valid_uarch}")
58+
valid, msg = config.is_valid_target(system, uarch)
59+
if not valid:
60+
raise EnvError(f"{msg}")
61+
62+
try:
63+
uenv, version = target.split(':')
64+
except:
65+
raise EnvError(f"invalid format of uenv={target}, expected 'name:version'")
66+
67+
# TODO: check that the recipe-version also supports uarch
68+
recipe = config.recipe(uenv, version, uarch)
69+
if recipe is None:
70+
raise EnvError(f"the recipe 'name:version' is not available")
71+
72+
return {
73+
"system": system,
74+
"uarch": uarch,
75+
"uenv": uenv,
76+
"version": version,
77+
"recipe": recipe,
78+
}
79+
80+
if __name__ == "__main__":
81+
### TODO ###
82+
# read CLI arguments
83+
# - output path for the pipeline.yml file (required)
84+
# - path of the configuration file (required)
85+
# - JOB_ID (if needed?)
86+
if os.getenv("UENVCITEST", default=None) is not None:
87+
os.environ["system"] = "clariden"
88+
os.environ["uarch"] = "a100"
89+
os.environ["uenv"] = "gromacs:2023"
90+
91+
# read and validate the configuration
92+
print(recipe_path)
93+
try:
94+
config = configuration.Config(prefix / "../../config.yaml", recipe_path)
95+
except jsonschema.exceptions.ValidationError as e:
96+
print()
97+
where = e.json_path.replace("$.","").replace(".", ":")
98+
print(f"{util.colorize('[error] ', 'red')}config.yaml:{where}")
99+
print(f" {e.message}")
100+
exit(1)
101+
except configuration.ConfigError as e:
102+
print()
103+
print(f"{util.colorize('[error] ', 'red')}{e.message}")
104+
exit(1)
105+
106+
# read environment variables that describe image(s) to build in this run
107+
try:
108+
env = readenv(config)
109+
except EnvError as e:
110+
print()
111+
print(f"{util.colorize('[error] ', 'red')}{e.message}")
112+
exit(1)
113+
114+
print(f"--- request -----------------------------------------")
115+
for k,i in env.items():
116+
print(f"{k:20s}: {i}")
117+
118+
print(f"\n--- job template ------------------------------------")
119+
job = config.job_template(env)
120+
for k,i in job.items():
121+
print(f"{k:20s}: {i}")
122+
123+
#
124+
# write the pipeline.yml using jinja template
125+
#
126+
127+
# load the jinja templating environment
128+
template_path = prefix / "templates"
129+
jinja_env = jinja2.Environment(
130+
loader=jinja2.FileSystemLoader(template_path),
131+
trim_blocks=True,
132+
lstrip_blocks=True,
133+
)
134+
135+
# generate top level makefiles
136+
pipeline_template = jinja_env.get_template("pipeline.yml")
137+
138+
with (root_path / "pipeline.yml").open("w") as f:
139+
f.write(pipeline_template.render(jobs=[job]))
140+
141+

0 commit comments

Comments
 (0)