libvirt/ci/helper

264 lines
7.9 KiB
Plaintext
Raw Normal View History

#!/usr/bin/env python3
#
# Copyright (C) 2021 Red Hat, Inc.
# SPDX-License-Identifier: LGPL-2.1-or-later
import argparse
import os
import pathlib
import pty
import shutil
import subprocess
import sys
class Parser:
def __init__(self):
# 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",
)
# 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",
)
# 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",
)
# Main parser
self.parser = argparse.ArgumentParser()
subparsers = self.parser.add_subparsers(
dest="action",
metavar="ACTION",
)
subparsers.required = True
# build action
buildparser = subparsers.add_parser(
"build",
help="run a build in a container",
parents=[containerparser, mesonparser],
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
buildparser.set_defaults(func=Application.action_build)
# test action
testparser = subparsers.add_parser(
"test",
help="run a build in a container (including tests)",
parents=[containerparser, mesonparser],
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
testparser.set_defaults(func=Application.action_test)
# shell action
shellparser = subparsers.add_parser(
"shell",
help="start a shell in a container",
parents=[containerparser],
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
shellparser.set_defaults(func=Application.action_shell)
# list-images action
listimagesparser = subparsers.add_parser(
"list-images",
help="list known container images",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
listimagesparser.set_defaults(func=Application.action_list_images)
# refresh action
refreshparser = subparsers.add_parser(
"refresh",
help="refresh data generated with lcitool",
parents=[lcitoolparser],
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
refreshparser.add_argument(
"--quiet",
action="store_true",
default=False,
help="refresh data silently"
)
refreshparser.set_defaults(func=Application.action_refresh)
def parse(self):
return self.parser.parse_args()
class Application:
def __init__(self):
self.basedir = pathlib.Path(__file__).resolve().parent
self.args = Parser().parse()
if self.args.action == "refresh":
if not shutil.which(self.args.lcitool):
sys.exit("error: 'lcitool' not installed")
def make_run(self, target):
args = [
"-C",
self.basedir,
target,
]
if self.args.action in ["build", "test", "shell"]:
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}",
])
if self.args.action in ["build", "test"]:
args.extend([
f"CI_MESON_ARGS={self.args.meson_args}",
f"CI_NINJA_ARGS={self.args.ninja_args}",
])
if pty.spawn(["make"] + args) != 0:
sys.exit("error: 'make' failed")
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)
if not self.args.quiet:
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)
if not self.args.quiet:
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)
def action_build(self):
self.make_run(f"ci-build@{self.args.target}")
def action_test(self):
self.make_run(f"ci-test@{self.args.target}")
def action_shell(self):
self.make_run(f"ci-shell@{self.args.target}")
def action_list_images(self):
self.make_run(f"ci-list-images")
def action_refresh(self):
self.refresh_containers()
self.refresh_cirrus()
def run(self):
self.args.func(self)
if __name__ == "__main__":
Application().run()