Newer
Older

Ralf Jung
committed
#!/usr/bin/python3
import sys, os, subprocess

Ralf Jung
committed
from datetime import datetime, timezone
################################################################################
# This script lets you autoamtically trigger some operations on the Iris CI to
# do further test/analysis on a branch (usually an MR).
# Set the GITLAB_TOKEN environment variable to a GitLab access token.
# Set at least one of IRIS_REV or STDPP_REV to control which branches of these
# projects to build against (defaults to default git branch). IRIS_REPO and

Ralf Jung
committed
# STDPP_REPO can be used to take branches from forks. Setting IRIS to
# "user:branch" will use the given branch on that user's fork of Iris, and
# similar for STDPP.
#
# Supported commands:
# - `./iris-bot build [$filter]`: Builds all reverse dependencies against the
# given branches. The optional `filter` argument only builds projects whose
# names contains that string.
# - `./iris-bot time $project`: Measure the impact of this branch on the build
# time of the given reverse dependency. Only Iris branches are supported for
# now.
################################################################################
PROJECTS = {
'lambda-rust': { 'name': 'lambda-rust', 'branch': 'master', 'timing': True },
'lambda-rust-weak': { 'name': 'lambda-rust', 'branch': 'masters/weak_mem' }, # covers GPFSL and ORC11
'examples': { 'name': 'examples', 'branch': 'master', 'timing': True },
'iron': { 'name': 'iron', 'branch': 'master', 'timing': True },

Ralf Jung
committed
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
'reloc': { 'name': 'reloc', 'branch': 'master' },
'spygame': { 'name': 'spygame', 'branch': 'master' },
'time-credits': { 'name': 'time-credits', 'branch': 'master' },
'actris': { 'name': 'actris', 'branch': 'master' },
'tutorial-popl20': { 'name': 'tutorial-popl20', 'branch': 'master' },
'tutorial-popl21': { 'name': 'tutorial-popl21', 'branch': 'master' },
}
if not "GITLAB_TOKEN" in os.environ:
print("You need to set the GITLAB_TOKEN environment variable to a GitLab access token.")
print("You can create such tokens at <https://gitlab.mpi-sws.org/profile/personal_access_tokens>.")
print("Make sure you grant access to the 'api' scope.")
sys.exit(1)
GITLAB_TOKEN = os.environ["GITLAB_TOKEN"]
# Pre-processing for branch variables of dependency projects: you can set
# 'PROJECT' to 'user:branch', or set 'PROJECT_REPO' and 'PROJECT_REV'
# automatically.
BUILD_BRANCHES = {}
for project in ['stdpp', 'iris', 'orc11', 'gpfsl']:
var = project.upper()
if var in os.environ:
(repo, rev) = os.environ[var].split(':')
repo = repo + "/" + project
else:
repo = os.environ.get(var+"_REPO", "iris/"+project)
rev = os.environ.get(var+"_REV")
if rev is not None:
BUILD_BRANCHES[project] = (repo, rev)
if not "iris" in BUILD_BRANCHES:
print("Please set IRIS_REV, STDPP_REV, ORC11_REV and GPFSL_REV environment variables to the branch/tag/commit of the respective project that you want to use.")
print("Only IRIS_REV is mandatory, the rest defaults to the default git branch.")
sys.exit(1)
# Useful helpers
def trigger_build(project, branch, vars):
id = "iris%2F{}".format(project)
url = "https://gitlab.mpi-sws.org/api/v4/projects/{}/pipeline".format(id)
json = {
'ref': branch,
'variables': [{ 'key': key, 'value': val } for (key, val) in vars.items()],
}
r = requests.post(url, headers={'PRIVATE-TOKEN': GITLAB_TOKEN}, json=json)
r.raise_for_status()

Ralf Jung
committed
# The commands

Ralf Jung
committed
# Convert BUILD_BRANCHES into suitable dictionary
vars = {}
for project in BUILD_BRANCHES.keys():
(repo, rev) = BUILD_BRANCHES[project]
var = project.upper()
vars[var+"_REPO"] = repo
vars[var+"_REV"] = rev
# Loop over all projects, and trigger build.
for (name, project) in PROJECTS.items():

Ralf Jung
committed
print("Triggering build for {}...".format(name))
pipeline_url = trigger_build(project['name'], project['branch'], vars)['web_url']

Ralf Jung
committed
print(" Pipeline running at {}".format(pipeline_url))

Ralf Jung
committed
# Make sure only 'iris' variables are set.
# One could imagine generalizing to "either Iris or std++", but then if the
# ad-hoc timing jobs honor STDPP_REV, how do we make it so that particular
# deterministic std++ versions are used for Iris timing? This does not
# currently seem worth the effort / hacks.

Ralf Jung
committed
for project in BUILD_BRANCHES.keys():
if project != 'iris':
print("'time' command only supports Iris branches")
sys.exit(1)
(iris_repo, iris_rev) = BUILD_BRANCHES['iris']
# Get project to test and ensure it supports timing
project_name = args.project
if project_name not in PROJECTS:
print("ERROR: no such project: {}".format(project_name))

Ralf Jung
committed
sys.exit(1)
project = PROJECTS[project_name]
if not project.get('timing'):
print("ERROR: {} does not support timing".format(project_name))
sys.exit(1)
# Obtain a unique ID for this experiment
id = datetime.now(timezone.utc).strftime("%Y%m%d-%H%M%S")
# Determine the branch commit to build
subprocess.run(["git", "fetch", "-q", "https://gitlab.mpi-sws.org/{}".format(iris_repo), iris_rev], check=True)
test_commit = subprocess.run(["git", "rev-parse", "FETCH_HEAD"], check=True, stdout=subprocess.PIPE).stdout.decode().strip()
# Determine the base commit in master
subprocess.run(["git", "fetch", "-q", "origin", "master"], check=True)

Ralf Jung
committed
base_commit = subprocess.run(["git", "merge-base", test_commit, "origin/master"], check=True, stdout=subprocess.PIPE).stdout.decode().strip()
# Trigger the builds
print("Triggering timing builds for {} with Iris base commit {} and test commit {} using ad-hoc ID {}...".format(project_name, base_commit[:8], test_commit[:8], id))

Ralf Jung
committed
vars = {
'IRIS_REPO': iris_repo,
'IRIS_REV': base_commit,
'TIMING_AD_HOC_ID': id+"-base",
}
base_pipeline = trigger_build(project['name'], project['branch'], vars)
print(" Base pipeline running at {}".format(base_pipeline['web_url']))

Ralf Jung
committed
vars = {
'IRIS_REPO': iris_repo,
'IRIS_REV': test_commit,
'TIMING_AD_HOC_ID': id+"-test",
}
test_pipeline = trigger_build(project['name'], project['branch'], vars)
print(" Test pipeline running at {}".format(test_pipeline['web_url']))
else:
test_pipeline = trigger_build(project['name'], args.test_rev, vars)
print(" Test pipeline (on non-standard branch {}) running at {}".format(args.test_rev, test_pipeline['web_url']))
print(" Once done, timing comparison will be available at https://coq-speed.mpi-sws.org/d/1QE_dqjiz/coq-compare?orgId=1&var-project={}&var-branch1=@hoc&var-commit1={}&var-config1={}&var-branch2=@hoc&var-commit2={}&var-config2={}".format(project['name'], base_pipeline['sha'], id+"-base", test_pipeline['sha'], id+"-test"))

Ralf Jung
committed
# Dispatch
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Iris CI utility')
subparsers = parser.add_subparsers(required=True, title='iris-bot command to execute', description='see "$command -h" for help', metavar="$command")
parser_build = subparsers.add_parser('build', help='Build many reverse dependencies against an Iris branch')
parser_build.set_defaults(func=build)
parser_build.add_argument('filter', nargs='?', default='', help='(optional) restrict build to projects matching the filter')
parser_time = subparsers.add_parser('time', help='Time one reverse dependency against an Iris branch')
parser_time.add_argument("project", help="the project to measure the time of")
parser_time.add_argument("--test-rev", help="use different revision on project for the test build (in case the project requires changes to still build)")