frontend.py (4557B)
1 # mypy: allow-untyped-defs 2 3 import argparse 4 import logging 5 import os 6 import re 7 import subprocess 8 import sys 9 10 here = os.path.abspath(os.path.dirname(__file__)) 11 wpt_root = os.path.abspath(os.path.join(here, os.pardir, os.pardir)) 12 13 logger = logging.getLogger() 14 15 16 def build(tag="wpt:local", *args, **kwargs): 17 subprocess.check_call(["docker", 18 "build", 19 "--pull", 20 "--tag", tag, 21 here]) 22 23 24 def parser_push(): 25 parser = argparse.ArgumentParser() 26 parser.add_argument("--tag", 27 help="Tag to use (default is taken from .taskcluster.yml)") 28 parser.add_argument("--force", action="store_true", 29 help="Ignore warnings and push anyway") 30 return parser 31 32 33 def walk_yaml(root, target): 34 rv = [] 35 if isinstance(root, list): 36 for value in root: 37 if isinstance(value, (dict, list)): 38 rv.extend(walk_yaml(value, target)) 39 elif isinstance(root, dict): 40 for key, value in root.items(): 41 if isinstance(value, (dict, list)): 42 rv.extend(walk_yaml(value, target)) 43 elif key == target: 44 rv.append(value) 45 return rv 46 47 48 def read_image_name(): 49 import yaml 50 with open(os.path.join(wpt_root, ".taskcluster.yml")) as f: 51 taskcluster_data = yaml.safe_load(f) 52 taskcluster_values = set(walk_yaml(taskcluster_data, "image")) 53 with open(os.path.join(wpt_root, "tools", "ci", "tc", "tasks", "test.yml")) as f: 54 test_data = yaml.safe_load(f) 55 tests_value = test_data["components"]["wpt-base"]["image"] 56 return taskcluster_values, tests_value 57 58 59 def tag_exists(tag): 60 retcode = subprocess.call(["docker", "manifest", "inspect", tag]) 61 # The command succeeds if the tag exists. 62 return retcode != 0 63 64 65 def push(venv, tag=None, force=False, *args, **kwargs): 66 taskcluster_tags, tests_tag = read_image_name() 67 68 taskcluster_tag = taskcluster_tags.pop() 69 70 error_log = logger.warning if force else logger.error 71 if len(taskcluster_tags) != 0 or tests_tag != taskcluster_tag: 72 error_log("Image names in .taskcluster.yml and tools/ci/tc/tasks/test.yml " 73 "don't match.") 74 if not force: 75 sys.exit(1) 76 if tag is not None and tag != taskcluster_tag: 77 error_log("Supplied tag doesn't match .taskcluster.yml or " 78 "tools/ci/tc/tasks/test.yml; remember to update before pushing") 79 if not force: 80 sys.exit(1) 81 if tag is None: 82 logger.info("Using tag %s from .taskcluster.yml" % taskcluster_tag) 83 tag = taskcluster_tag 84 85 tag_re = re.compile(r"ghcr.io/web-platform-tests/wpt:\d+") 86 if not tag_re.match(tag): 87 error_log("Tag doesn't match expected format ghcr.io/web-platform-tests/wpt:x") 88 if not force: 89 sys.exit(1) 90 91 if tag_exists(tag): 92 # No override for this case 93 logger.critical("Tag %s already exists" % tag) 94 sys.exit(1) 95 96 build(tag) 97 subprocess.check_call(["docker", 98 "push", 99 tag]) 100 101 102 def parser_run(): 103 parser = argparse.ArgumentParser() 104 parser.add_argument("--rebuild", action="store_true", help="Force rebuild of image") 105 parser.add_argument("--checkout", 106 help="Revision to checkout in the image. " 107 "If this is not supplied we mount the wpt checkout on the host as " 108 "/home/test/web-platform-tests/") 109 parser.add_argument("--privileged", action="store_true", 110 help="Run the image in priviledged mode (required for emulators)") 111 parser.add_argument("--tag", default="wpt:local", 112 help="Docker image tag to use (default wpt:local)") 113 return parser 114 115 116 def run(*args, **kwargs): 117 if kwargs["rebuild"]: 118 build() 119 120 args = ["docker", "run"] 121 args.extend(["--security-opt", "seccomp:%s" % 122 os.path.join(wpt_root, "tools", "docker", "seccomp.json")]) 123 if kwargs["privileged"]: 124 args.append("--privileged") 125 args.extend(["--device", "/dev/kvm"]) 126 if kwargs["checkout"]: 127 args.extend(["--env", "REF==%s" % kwargs["checkout"]]) 128 else: 129 args.extend(["--mount", 130 "type=bind,source=%s,target=/home/test/web-platform-tests" % wpt_root]) 131 args.extend(["-it", kwargs["tag"]]) 132 133 proc = subprocess.Popen(args) 134 proc.wait() 135 return proc.returncode