runner.py (7283B)
1 # This Source Code Form is subject to the terms of the Mozilla Public 2 # License, v. 2.0. If a copy of the MPL was not distributed with this 3 # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 """ Script that launches profiles creation. 5 """ 6 import os 7 import shutil 8 import asyncio 9 10 import mozversion 11 12 from condprof.creator import ProfileCreator 13 from condprof.desktop import DesktopEnv 14 from condprof.android import AndroidEnv 15 from condprof.changelog import Changelog 16 from condprof.scenarii import scenarii 17 from condprof.util import logger, get_current_platform, extract_from_dmg 18 from condprof.customization import get_customizations, find_customization 19 from condprof.client import read_changelog, ProfileNotFoundError 20 21 22 class Runner: 23 def __init__( 24 self, 25 profile, 26 firefox, 27 geckodriver, 28 archive, 29 device_name, 30 strict, 31 force_new, 32 visible, 33 skip_logs=False, 34 remote_test_root="/sdcard/test_root/", 35 ): 36 self.force_new = force_new 37 self.profile = profile 38 self.geckodriver = geckodriver 39 self.archive = archive 40 self.device_name = device_name 41 self.strict = strict 42 self.visible = visible 43 self.skip_logs = skip_logs 44 self.remote_test_root = remote_test_root 45 self.env = {} 46 # unpacking a dmg 47 # XXX do something similar if we get an apk (but later) 48 # XXX we want to do 49 # adb install -r target.apk 50 # and get the installed app name 51 if firefox is not None and firefox.endswith("dmg"): 52 target = os.path.join(os.path.dirname(firefox), "firefox.app") 53 extract_from_dmg(firefox, target) 54 firefox = os.path.join(target, "Contents", "MacOS", "firefox") 55 self.firefox = firefox 56 self.android = self.firefox is not None and self.firefox.startswith( 57 "org.mozilla" 58 ) 59 60 def prepare(self, scenario, customization): 61 self.scenario = scenario 62 self.customization = customization 63 64 # early checks to avoid extra work 65 if self.customization != "all": 66 if find_customization(self.customization) is None: 67 raise IOError("Cannot find customization %r" % self.customization) 68 69 if self.scenario != "all" and self.scenario not in scenarii: 70 raise IOError("Cannot find scenario %r" % self.scenario) 71 72 if not self.android and self.firefox is not None: 73 logger.info("Verifying Desktop Firefox binary") 74 # we want to verify we do have a firefox binary 75 # XXX so lame 76 if not os.path.exists(self.firefox): 77 if "MOZ_FETCHES_DIR" in os.environ: 78 target = os.path.join(os.environ["MOZ_FETCHES_DIR"], self.firefox) 79 if os.path.exists(target): 80 self.firefox = target 81 82 if not os.path.exists(self.firefox): 83 raise IOError("Cannot find %s" % self.firefox) 84 85 mozversion.get_version(self.firefox) 86 87 logger.info(os.environ) 88 if self.archive: 89 self.archive = os.path.abspath(self.archive) 90 logger.info("Archives directory is %s" % self.archive) 91 if not os.path.exists(self.archive): 92 os.makedirs(self.archive, exist_ok=True) 93 94 if shutil.which(self.geckodriver) is None and not os.path.exists( 95 self.geckodriver 96 ): 97 raise IOError("Cannot find %s" % self.geckodriver) 98 99 if not self.skip_logs: 100 try: 101 if self.android: 102 plat = "%s-%s" % ( 103 self.device_name, 104 self.firefox.split("org.mozilla.")[-1], 105 ) 106 else: 107 plat = get_current_platform() 108 self.changelog = read_changelog(plat) 109 logger.info("Got the changelog from TaskCluster") 110 except ProfileNotFoundError: 111 logger.info("changelog not found on TaskCluster, creating a local one.") 112 self.changelog = Changelog(self.archive) 113 else: 114 self.changelog = [] 115 116 def _create_env(self): 117 if self.android: 118 klass = AndroidEnv 119 else: 120 klass = DesktopEnv 121 122 return klass( 123 self.profile, self.firefox, self.geckodriver, self.archive, self.device_name 124 ) 125 126 def display_error(self, scenario, customization): 127 logger.error("%s x %s failed." % (scenario, customization), exc_info=True) 128 # TODO: this might avoid the exceptions that slip through in automation 129 # if self.strict: 130 # raise 131 132 async def one_run(self, scenario, customization): 133 """Runs one single conditioned profile. 134 135 Create an instance of the environment and run the ProfileCreator. 136 """ 137 self.env = self._create_env() 138 return await ProfileCreator( 139 scenario, 140 customization, 141 self.archive, 142 self.changelog, 143 self.force_new, 144 self.env, 145 skip_logs=self.skip_logs, 146 remote_test_root=self.remote_test_root, 147 ).run(not self.visible) 148 149 async def run_all(self): 150 """Runs the conditioned profile builders""" 151 if self.scenario != "all": 152 selected_scenario = [self.scenario] 153 else: 154 selected_scenario = scenarii.keys() 155 156 # this is the loop that generates all combinations of profile 157 # for the current platform when "all" is selected 158 res = [] 159 failures = 0 160 for scenario in selected_scenario: 161 if self.customization != "all": 162 try: 163 res.append(await self.one_run(scenario, self.customization)) 164 except Exception: 165 failures += 1 166 self.display_error(scenario, self.customization) 167 else: 168 for customization in get_customizations(): 169 logger.info("Customization %s" % customization) 170 try: 171 res.append(await self.one_run(scenario, customization)) 172 except Exception: 173 failures += 1 174 self.display_error(scenario, customization) 175 176 return failures, [one_res for one_res in res if one_res] 177 178 def save(self): 179 self.changelog.save(self.archive) 180 181 182 def run( 183 archive, 184 firefox=None, 185 scenario="all", 186 profile=None, 187 customization="all", 188 visible=False, 189 archives_dir="/tmp/archives", 190 force_new=False, 191 strict=True, 192 geckodriver="geckodriver", 193 device_name=None, 194 ): 195 runner = Runner( 196 profile, firefox, geckodriver, archive, device_name, strict, force_new, visible 197 ) 198 199 runner.prepare(scenario, customization) 200 loop = asyncio.get_event_loop() 201 202 try: 203 failures, results = loop.run_until_complete(runner.run_all()) 204 logger.info("Saving changelog in %s" % archive) 205 runner.save() 206 if failures > 0: 207 raise Exception("At least one scenario failed") 208 except Exception as e: 209 raise e 210 finally: 211 loop.close()