2021-02-16 16:21:49 +00:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
#
|
|
|
|
# Copyright (C) 2021 Red Hat, Inc.
|
|
|
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
|
|
|
|
|
|
|
import argparse
|
2021-03-12 16:55:08 +00:00
|
|
|
import os
|
2021-02-16 16:21:49 +00:00
|
|
|
import pathlib
|
2021-03-12 16:52:50 +00:00
|
|
|
import pty
|
2021-03-12 16:48:47 +00:00
|
|
|
import shutil
|
|
|
|
import subprocess
|
|
|
|
import sys
|
2021-02-16 16:21:49 +00:00
|
|
|
|
|
|
|
|
|
|
|
class Parser:
|
|
|
|
def __init__(self):
|
2021-03-12 16:55:08 +00:00
|
|
|
# Options that are common to all actions that use containers
|
|
|
|
containerparser = argparse.ArgumentParser(add_help=False)
|
|
|
|
containerparser.add_argument(
|
|
|
|
"target",
|
|
|
|
help="perform action on target OS",
|
|
|
|
)
|
|
|
|
containerparser.add_argument(
|
|
|
|
"--engine",
|
|
|
|
choices=["auto", "podman", "docker"],
|
|
|
|
default="auto",
|
|
|
|
help="container engine to use",
|
|
|
|
)
|
|
|
|
containerparser.add_argument(
|
|
|
|
"--login",
|
|
|
|
default=os.getlogin(), # exempt from syntax-check
|
|
|
|
help="login to use inside the container",
|
|
|
|
)
|
|
|
|
containerparser.add_argument(
|
|
|
|
"--image-prefix",
|
|
|
|
default="registry.gitlab.com/libvirt/libvirt/ci-",
|
|
|
|
help="use container images from non-default location",
|
|
|
|
)
|
|
|
|
containerparser.add_argument(
|
|
|
|
"--image-tag",
|
|
|
|
default=":latest",
|
|
|
|
help="use container images with non-default tags",
|
|
|
|
)
|
|
|
|
|
2021-03-12 17:00:52 +00:00
|
|
|
# Options that are common to all actions that call the
|
|
|
|
# project's build system
|
|
|
|
mesonparser = argparse.ArgumentParser(add_help=False)
|
|
|
|
mesonparser.add_argument(
|
|
|
|
"--meson-args",
|
|
|
|
default="",
|
|
|
|
help="additional arguments passed to meson "
|
|
|
|
"(eg --meson-args='-Dopt1=enabled -Dopt2=disabled')",
|
|
|
|
)
|
|
|
|
mesonparser.add_argument(
|
|
|
|
"--ninja-args",
|
|
|
|
default="",
|
|
|
|
help="additional arguments passed to ninja",
|
|
|
|
)
|
|
|
|
|
2021-03-12 16:48:47 +00:00
|
|
|
# Options that are common to all actions that use lcitool
|
|
|
|
lcitoolparser = argparse.ArgumentParser(add_help=False)
|
|
|
|
lcitoolparser.add_argument(
|
|
|
|
"--lcitool",
|
|
|
|
metavar="PATH",
|
|
|
|
default="lcitool",
|
|
|
|
help="path to lcitool binary",
|
|
|
|
)
|
|
|
|
|
2021-02-16 16:21:49 +00:00
|
|
|
# Main parser
|
|
|
|
self.parser = argparse.ArgumentParser()
|
|
|
|
subparsers = self.parser.add_subparsers(
|
|
|
|
dest="action",
|
|
|
|
metavar="ACTION",
|
|
|
|
)
|
|
|
|
subparsers.required = True
|
|
|
|
|
2021-03-12 17:00:52 +00:00
|
|
|
# build action
|
|
|
|
buildparser = subparsers.add_parser(
|
|
|
|
"build",
|
|
|
|
help="run a build in a container",
|
|
|
|
parents=[containerparser, mesonparser],
|
|
|
|
)
|
|
|
|
buildparser.set_defaults(func=Application.action_build)
|
|
|
|
|
2021-03-12 17:01:43 +00:00
|
|
|
# test action
|
|
|
|
testparser = subparsers.add_parser(
|
|
|
|
"test",
|
|
|
|
help="run a build in a container (including tests)",
|
|
|
|
parents=[containerparser, mesonparser],
|
|
|
|
)
|
|
|
|
testparser.set_defaults(func=Application.action_test)
|
|
|
|
|
2021-03-12 16:55:08 +00:00
|
|
|
# shell action
|
|
|
|
shellparser = subparsers.add_parser(
|
|
|
|
"shell",
|
|
|
|
help="start a shell in a container",
|
|
|
|
parents=[containerparser],
|
|
|
|
)
|
|
|
|
shellparser.set_defaults(func=Application.action_shell)
|
|
|
|
|
2021-03-12 16:52:50 +00:00
|
|
|
# list-images action
|
|
|
|
listimagesparser = subparsers.add_parser(
|
|
|
|
"list-images",
|
|
|
|
help="list known container images",
|
|
|
|
)
|
|
|
|
listimagesparser.set_defaults(func=Application.action_list_images)
|
|
|
|
|
2021-03-12 16:48:47 +00:00
|
|
|
# refresh action
|
|
|
|
refreshparser = subparsers.add_parser(
|
|
|
|
"refresh",
|
|
|
|
help="refresh data generated with lcitool",
|
|
|
|
parents=[lcitoolparser],
|
|
|
|
)
|
|
|
|
refreshparser.set_defaults(func=Application.action_refresh)
|
|
|
|
|
2021-02-16 16:21:49 +00:00
|
|
|
def parse(self):
|
|
|
|
return self.parser.parse_args()
|
|
|
|
|
|
|
|
|
|
|
|
class Application:
|
|
|
|
def __init__(self):
|
|
|
|
self.basedir = pathlib.Path(__file__).resolve().parent
|
|
|
|
self.args = Parser().parse()
|
|
|
|
|
2021-03-12 16:48:47 +00:00
|
|
|
if self.args.action == "refresh":
|
|
|
|
if not shutil.which(self.args.lcitool):
|
|
|
|
sys.exit("error: 'lcitool' not installed")
|
|
|
|
|
2021-03-12 16:52:50 +00:00
|
|
|
def make_run(self, target):
|
|
|
|
args = [
|
|
|
|
"-C",
|
|
|
|
self.basedir,
|
|
|
|
target,
|
|
|
|
]
|
|
|
|
|
2021-03-12 17:01:43 +00:00
|
|
|
if self.args.action in ["build", "test", "shell"]:
|
2021-03-12 16:55:08 +00:00
|
|
|
args.extend([
|
|
|
|
f"CI_ENGINE={self.args.engine}",
|
|
|
|
f"CI_USER_LOGIN={self.args.login}",
|
|
|
|
f"CI_IMAGE_PREFIX={self.args.image_prefix}",
|
|
|
|
f"CI_IMAGE_TAG={self.args.image_tag}",
|
|
|
|
])
|
|
|
|
|
2021-03-12 17:01:43 +00:00
|
|
|
if self.args.action in ["build", "test"]:
|
2021-03-12 17:00:52 +00:00
|
|
|
args.extend([
|
|
|
|
f"CI_MESON_ARGS={self.args.meson_args}",
|
|
|
|
f"CI_NINJA_ARGS={self.args.ninja_args}",
|
|
|
|
])
|
|
|
|
|
2021-03-12 16:52:50 +00:00
|
|
|
if pty.spawn(["make"] + args) != 0:
|
|
|
|
sys.exit("error: 'make' failed")
|
|
|
|
|
2021-03-12 16:48:47 +00:00
|
|
|
def lcitool_run(self, args):
|
|
|
|
output = subprocess.check_output([self.args.lcitool] + args)
|
|
|
|
return output.decode("utf-8")
|
|
|
|
|
|
|
|
def lcitool_get_hosts(self):
|
|
|
|
output = self.lcitool_run(["hosts"])
|
|
|
|
return output.splitlines()
|
|
|
|
|
|
|
|
def generate_dockerfile(self, host, cross=None):
|
|
|
|
args = ["dockerfile", host, "libvirt"]
|
|
|
|
outdir = self.basedir.joinpath("containers")
|
|
|
|
outfile = f"ci-{host}.Dockerfile"
|
|
|
|
|
|
|
|
if cross:
|
|
|
|
args.extend(["--cross", cross])
|
|
|
|
outfile = f"ci-{host}-cross-{cross}.Dockerfile"
|
|
|
|
|
|
|
|
outpath = outdir.joinpath(outfile)
|
|
|
|
print(outpath)
|
|
|
|
|
|
|
|
output = self.lcitool_run(args)
|
|
|
|
with open(outpath, "w") as f:
|
|
|
|
f.write(output)
|
|
|
|
|
|
|
|
def generate_vars(self, host):
|
|
|
|
args = ["variables", host, "libvirt"]
|
|
|
|
outdir = self.basedir.joinpath("cirrus")
|
|
|
|
outfile = f"{host}.vars"
|
|
|
|
|
|
|
|
outpath = outdir.joinpath(outfile)
|
|
|
|
print(outpath)
|
|
|
|
|
|
|
|
output = self.lcitool_run(args)
|
|
|
|
with open(outpath, "w") as f:
|
|
|
|
f.write(output)
|
|
|
|
|
|
|
|
def refresh_containers(self):
|
|
|
|
debian_cross = [
|
|
|
|
"aarch64",
|
|
|
|
"armv6l",
|
|
|
|
"armv7l",
|
|
|
|
"i686",
|
|
|
|
"mips",
|
|
|
|
"mips64el",
|
|
|
|
"mipsel",
|
|
|
|
"ppc64le",
|
|
|
|
"s390x",
|
|
|
|
]
|
|
|
|
fedora_cross = [
|
|
|
|
"mingw32",
|
|
|
|
"mingw64",
|
|
|
|
]
|
|
|
|
|
|
|
|
for host in self.lcitool_get_hosts():
|
|
|
|
if host.startswith("freebsd-") or host.startswith("macos-"):
|
|
|
|
continue
|
|
|
|
|
|
|
|
self.generate_dockerfile(host)
|
|
|
|
|
|
|
|
if host == "fedora-rawhide":
|
|
|
|
for cross in fedora_cross:
|
|
|
|
self.generate_dockerfile(host, cross)
|
|
|
|
|
|
|
|
if host.startswith("debian-"):
|
|
|
|
for cross in debian_cross:
|
|
|
|
if host == "debian-sid" and cross == "mips":
|
|
|
|
continue
|
|
|
|
self.generate_dockerfile(host, cross)
|
|
|
|
|
|
|
|
def refresh_cirrus(self):
|
|
|
|
for host in self.lcitool_get_hosts():
|
|
|
|
if not (host.startswith("freebsd-") or host.startswith("macos-")):
|
|
|
|
continue
|
|
|
|
|
|
|
|
self.generate_vars(host)
|
|
|
|
|
2021-03-12 17:00:52 +00:00
|
|
|
def action_build(self):
|
|
|
|
self.make_run(f"ci-build@{self.args.target}")
|
|
|
|
|
2021-03-12 17:01:43 +00:00
|
|
|
def action_test(self):
|
|
|
|
self.make_run(f"ci-test@{self.args.target}")
|
|
|
|
|
2021-03-12 16:55:08 +00:00
|
|
|
def action_shell(self):
|
|
|
|
self.make_run(f"ci-shell@{self.args.target}")
|
|
|
|
|
2021-03-12 16:52:50 +00:00
|
|
|
def action_list_images(self):
|
|
|
|
self.make_run(f"ci-list-images")
|
|
|
|
|
2021-03-12 16:48:47 +00:00
|
|
|
def action_refresh(self):
|
|
|
|
self.refresh_containers()
|
|
|
|
self.refresh_cirrus()
|
|
|
|
|
2021-02-16 16:21:49 +00:00
|
|
|
def run(self):
|
|
|
|
self.args.func(self)
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
Application().run()
|