def _run_python_code(build, extra_path, entry_point): python_runner = ensure_lib_available(build, 'python_runner.py') if sys.platform.startswith("win"): runner = ensure_lib_available(build, 'python_runner_win.exe') run_shell(runner, extra_path, entry_point, command_log_level=logging.INFO, check_for_interrupt=True) elif sys.platform.startswith("darwin"): runner = ensure_lib_available(build, 'python_runner_darwin') run_shell(runner, extra_path, entry_point, command_log_level=logging.INFO, check_for_interrupt=True, create_process_group=True) else: python = sys.executable run_shell(python, python_runner, extra_path, entry_point, command_log_level=logging.INFO, check_for_interrupt=True)
def run_firefox(build, build_type_dir): firefox_lib = ensure_lib_available(build, 'run-firefox.zip') try: _run_python_code(build, firefox_lib, 'firefox_runner.run') finally: _clean_firefox(build_type_dir)
def _create_apk_with_aapt(build, out_apk_name, path_info, package_name, lib_path, dev_dir): LOG.info('Creating APK with aapt') run_shell( path_info.aapt, 'p', # create APK package '-F', out_apk_name, # output name '-S', path.join(dev_dir, 'res'), # uncompressed resources folder '-M', path.join(dev_dir, 'AndroidManifest.xml'), # uncompressed xml manifest '-I', ensure_lib_available( build, 'android-platform.apk' ), # Android platform to "compile" resources against '-A', path.join(dev_dir, 'assets'), # Assets folder to include '-0', '', # Don't compress any assets - Important for content provider access to assets '--rename-manifest-package', package_name, # Package name '-f', # Force overwrite path.join(dev_dir, 'bin'), # Location of raw files (app binary) command_log_level=logging.DEBUG)
def run_idevice(self, build, device, provisioning_profile, certificate=None, certificate_path=None, certificate_password=None): possible_app_location = '{0}/ios/device-*/'.format( self.path_to_ios_build) LOG.debug('Looking for apps at {0}'.format(possible_app_location)) possible_apps = glob(possible_app_location) if not possible_apps: raise IOSError("Couldn't find iOS app to run on a device") path_to_app = possible_apps[0] LOG.debug("Signing {app}".format(app=path_to_app)) plist_str = self._grab_plist_from_binary_mess(build, provisioning_profile) plist_dict = self._parse_plist(plist_str) self.check_plist_dict(plist_dict, self.path_to_ios_build) self.provisioning_profile = plist_dict LOG.info("Plist OK") certificate = self._select_certificate(certificate) self.log_profile() if sys.platform.startswith('darwin'): with temp_file() as temp_file_path: self._create_entitlements_file(build, temp_file_path) self._sign_app( build=build, provisioning_profile=provisioning_profile, certificate=certificate, entitlements_file=temp_file_path, ) fruitstrap = [ ensure_lib_available(build, 'fruitstrap'), '-d', '-u', '-t', '10', '-g', '-i mi -q', '-b', path_to_app ] if device and device.lower() != 'device': # pacific device given fruitstrap.append('-i') fruitstrap.append(device) LOG.info('Installing app on device {device}: is it connected?'. format(device=device)) else: LOG.info('Installing app on device: is it connected?') partial_line = [''] def filter_and_combine(logline): if logline.startswith('[') or logline.startswith('-'): return logline.rstrip() elif logline.startswith('@'): partial_line[0] += logline[2:-2] if partial_line[0].endswith('\\r\\n'): try: return partial_line[0][:-4] finally: partial_line[0] = "" return False run_shell(*fruitstrap, fail_silently=False, command_log_level=logging.INFO, filter=filter_and_combine, check_for_interrupt=True) elif sys.platform.startswith('win'): with temp_file() as ipa_path: self.create_ipa_from_app( build=build, provisioning_profile=provisioning_profile, output_path_for_ipa=ipa_path, certificate_path=certificate_path, certificate_password=certificate_password, ) win_ios_install = [ ensure_lib_available(build, 'win-ios-install.exe') ] if device and device.lower() != 'device': # pacific device given win_ios_install.append(device) LOG.info( 'Installing app on device {device}: is it connected?'. format(device=device)) else: LOG.info('Installing app on device: is it connected?') win_ios_install.append(ipa_path) win_ios_install.append(_generate_package_name(build)) run_shell(*win_ios_install, fail_silently=False, command_log_level=logging.INFO, check_for_interrupt=True) else: if not which('ideviceinstaller'): raise Exception( "Can't find ideviceinstaller - is it installed and on your PATH?" ) with temp_file() as ipa_path: self.create_ipa_from_app( build=build, provisioning_profile=provisioning_profile, output_path_for_ipa=ipa_path, certificate_path=certificate_path, certificate_password=certificate_password, ) linux_ios_install = ['ideviceinstaller'] if device and device.lower() != 'device': # pacific device given linux_ios_install.append('-U') linux_ios_install.append(device) LOG.info( 'Installing app on device {device}: is it connected?'. format(device=device)) else: LOG.info('Installing app on device: is it connected?') linux_ios_install.append('-i') linux_ios_install.append(ipa_path) run_shell(*linux_ios_install, fail_silently=False, command_log_level=logging.INFO, check_for_interrupt=True) LOG.info( 'App installed, you will need to run the app on the device manually.' )
def _sign_app(self, build, provisioning_profile, entitlements_file, certificate=None, certificate_path=None, certificate_password=None): app_folder_name = self._locate_ios_app( error_message="Couldn't find iOS app in order to sign it") path_to_app = path.abspath( path.join(self.path_to_ios_build, 'ios', app_folder_name)) embedded_profile = 'embedded.mobileprovision' path_to_embedded_profile = path.abspath( path.join(path_to_app, embedded_profile)) path_to_pp = path.join(build.orig_wd, provisioning_profile) if not path.isfile(path_to_pp): self._missing_provisioning_profile(build, path_to_pp) try: os.remove(path_to_embedded_profile) except Exception: LOG.warning("Couldn't remove {profile}".format( profile=path_to_embedded_profile)) shutil.copy2(path_to_pp, path_to_embedded_profile) if not sys.platform.startswith('darwin'): if not certificate_path: lib.local_config_problem( build, message="To deploy iOS apps to a device, you must specify a " "path to a certificate to sign with.", examples={ "ios.profiles.DEFAULT.developer_certificate_path": path.abspath("/Users/Bob/certificate.pfx") }, more_info= "http://current-docs.trigger.io/tools/ios-windows.html") if not certificate_password: lib.local_config_problem( build, message="To deploy iOS apps to a device, you must specify a " "path the password to unlock your certificate.", examples={ "ios.profiles.DEFAULT.developer_certificate_password": "******" }, more_info= "http://current-docs.trigger.io/tools/ios-windows.html") cache_file = None development_certificate = False try: cert_name = subprocess.check_output([ 'java', '-jar', ensure_lib_available(build, 'p12name.jar'), certificate_path, certificate_password ]).strip() if cert_name.startswith('iPhone Developer:'): development_certificate = True except Exception: pass if development_certificate: # Development certificate signings can be cached # Hash for Forge binary + signing certificate + profile + info.plist h = hashlib.sha1() with open(path.join(path_to_app, 'Forge'), 'rb') as binary_file: h.update(binary_file.read()) with open(path.join(path_to_app, 'Info.plist'), 'rb') as info_plist_file: h.update(info_plist_file.read()) with open(certificate_path, 'rb') as certificate_file: h.update(certificate_file.read()) with open(path_to_embedded_profile, 'rb') as embedded_file: h.update(embedded_file.read()) if not path.exists( path.abspath( path.join(self.path_to_ios_build, '..', '.template', 'ios-signing-cache'))): os.makedirs( path.abspath( path.join(self.path_to_ios_build, '..', '.template', 'ios-signing-cache'))) cache_file = path.abspath( path.join(self.path_to_ios_build, '..', '.template', 'ios-signing-cache', h.hexdigest())) # XXX: Currently cache file is never saved, see below. if cache_file is not None and path.exists(cache_file): with temp_file() as resource_rules_temp: shutil.copy2(path.join(path_to_app, 'ResourceRules.plist'), resource_rules_temp) zip_to_extract = ZipFile(cache_file) zip_to_extract.extractall(path_to_app) zip_to_extract.close() shutil.copy2(resource_rules_temp, path.join(path_to_app, 'ResourceRules.plist')) return # Remote LOG.info( 'Sending app to remote server for codesigning. Uploading may take some time.' ) # Zip up app with temp_file() as app_zip_file: if cache_file is None: with ZipFile(app_zip_file, 'w', compression=ZIP_DEFLATED) as app_zip: for root, dirs, files in os.walk(path_to_app, topdown=False): for file in files: app_zip.write( path.join(root, file), path.join(root[len(path_to_app):], file)) os.remove(path.join(root, file)) for dir in dirs: os.rmdir(path.join(root, dir)) else: with ZipFile(app_zip_file, 'w', compression=ZIP_DEFLATED) as app_zip: app_zip.write(path.join(path_to_app, 'Forge'), 'Forge') app_zip.write(path.join(path_to_app, 'Info.plist'), 'Info.plist') app_zip.write(path_to_embedded_profile, 'embedded.mobileprovision') with temp_file() as tweaked_resource_rules: import biplist rules = biplist.readPlist( path.join(path_to_app, 'ResourceRules.plist')) # Don't sign anything rules['rules']['.*'] = False with open(tweaked_resource_rules, 'wb') as tweaked_resource_rules_file: biplist.writePlist( rules, tweaked_resource_rules_file) app_zip.write(tweaked_resource_rules, 'ResourceRules.plist') from poster.encode import multipart_encode from poster.streaminghttp import register_openers import urllib2 class FileWithProgress: def __init__(self, path, flags): self.total_size = os.path.getsize(path) self.file = open(path, flags) self.name = self.file.name self.path = path self.amount_read = 0 self.last_progress = 0 def read(self, length): data = self.file.read(length) if data != "": self.amount_read = self.amount_read + len(data) # TODO: Nicer progress output progress = 10 * self.amount_read / self.total_size if progress > self.last_progress: self.last_progress = progress LOG.info( str(10 * progress) + " percent uploaded: " + self.path) else: self.file.close() return data def fileno(self): return self.file.fileno() def seek(self, pos): return self.file.seek(pos) files = { 'app': FileWithProgress(app_zip_file, 'rb'), 'entitlements': FileWithProgress(entitlements_file, 'rb'), 'certificate': FileWithProgress(certificate_path, 'rb'), 'password': certificate_password } # Register the streaming http handlers with urllib2 register_openers() # headers contains the necessary Content-Type and Content-Length # datagen is a generator object that yields the encoded parameters datagen, headers = multipart_encode(files) # Create the Request object request = urllib2.Request("https://trigger.io/codesign/sign", datagen, headers) with temp_file() as signed_zip_file: resp = urllib2.urlopen(request) # Read the log lines from the start of the response while True: data = resp.readline() if data == "--failure\n": raise IOSError("Remote codesign failed") elif data == "--data\n" or data == "": break LOG.info(data.rstrip('\r\n')) # Read the binary data from the 2nd part of the response # TODO: Chunked download and progress with open(signed_zip_file, 'wb') as signed_zip: signed_zip.write(resp.read()) # Unzip response zip_to_extract = ZipFile(signed_zip_file) zip_to_extract.extractall(path_to_app) zip_to_extract.close() # XXX: Caching currently disabled as Info.plist changes on every build """if cache_file is not None: shutil.copy2(signed_zip_file, cache_file)""" LOG.info('Signed app received, continuing with packaging.') else: # Local codesign = self._check_for_codesign() resource_rules = path.abspath( path.join(path_to_app, 'ResourceRules.plist')) run_shell(codesign, '--force', '--preserve-metadata', '--entitlements', entitlements_file, '--sign', certificate, '--resource-rules={0}'.format(resource_rules), path_to_app)
def run_idevice(self, build, device, provisioning_profile, certificate=None, certificate_path=None, certificate_password=None): possible_app_location = '{0}/ios/device-*/'.format( self.path_to_ios_build) LOG.debug('Looking for apps at {0}'.format(possible_app_location)) possible_apps = glob(possible_app_location) if not possible_apps: raise IOSError("Couldn't find iOS app to run on a device") path_to_app = possible_apps[0] LOG.debug("Signing {app}".format(app=path_to_app)) plist_str = self._grab_plist_from_binary_mess(provisioning_profile) plist_dict = self._parse_plist(plist_str) self.check_plist_dict(plist_dict, self.path_to_ios_build) LOG.info("Plist OK") if sys.platform.startswith('darwin'): with temp_file() as temp_file_path: self._create_entitlements_file(build, plist_dict, temp_file_path) self._sign_app( build=build, provisioning_profile=provisioning_profile, certificate=certificate, entitlements_file=temp_file_path, ) fruitstrap = [ ensure_lib_available(build, 'fruitstrap'), '-d', '-g', ensure_lib_available(build, 'gdb-arm-apple-darwin'), '-t', '10', '-b', path_to_app ] if device and device.lower() != 'device': # pacific device given fruitstrap.append('-i') fruitstrap.append(device) LOG.info('Installing app on device {device}: is it connected?'. format(device=device)) else: LOG.info('Installing app on device: is it connected?') run_shell(*fruitstrap, fail_silently=False, command_log_level=logging.INFO, filter=lambda x: not x.startswith("warning"), check_for_interrupt=True) elif sys.platform.startswith('win'): with temp_file() as ipa_path: self.create_ipa_from_app( build=build, provisioning_profile=provisioning_profile, output_path_for_ipa=ipa_path, certificate_path=certificate_path, certificate_password=certificate_password, ) win_ios_install = [ ensure_lib_available(build, 'win-ios-install.exe') ] if device and device.lower() != 'device': # pacific device given win_ios_install.append(device) LOG.info( 'Installing app on device {device}: is it connected?'. format(device=device)) else: LOG.info('Installing app on device: is it connected?') win_ios_install.append(ipa_path) win_ios_install.append(_generate_package_name(build)) run_shell(*win_ios_install, fail_silently=False, command_log_level=logging.INFO, check_for_interrupt=True)
def update_android(cookies, **kw): previous_path = _update_target('an-inspector', cookies=cookies) current_path = os.path.abspath( os.path.join(os.path.dirname(__file__), '..', '..', 'inspector', 'an-inspector')) # If we're updating copy the module source from the previous inspector if previous_path is not None: shutil.rmtree(os.path.join(current_path, 'ForgeModule', 'src')) if os.path.exists(os.path.join(previous_path, 'src')): shutil.copytree(os.path.join(previous_path, 'src'), os.path.join(current_path, 'ForgeModule', 'src')) else: shutil.copytree(os.path.join(previous_path, 'ForgeModule', 'src'), os.path.join(current_path, 'ForgeModule', 'src')) # Prepare example module code with open( os.path.abspath( os.path.join(os.path.dirname(__file__), '..', '..', 'module', 'manifest.json'))) as manifest_file: manifest = json.load(manifest_file) module_name = str(manifest['name']) for root, dirnames, filenames in os.walk( os.path.join(current_path, 'ForgeModule')): for filename in filenames: with open(os.path.join(root, filename), 'rb') as source: lines = source.readlines() if 'templatemodule' in os.path.join(root, filename): os.remove(os.path.join(root, filename)) old_dir = os.path.split(os.path.join(root, filename))[0] if len(os.listdir(old_dir)) == 0: os.removedirs(old_dir) new_dir = os.path.split( os.path.join(root, filename).replace('templatemodule', module_name))[0] if not os.path.isdir(new_dir): os.makedirs(new_dir) with open( os.path.join(root, filename).replace('templatemodule', module_name), 'wb') as output: for line in lines: output.write(line.replace('templatemodule', module_name)) # Update inspector with module specific build details try: build.apply_module_to_android_project( os.path.abspath( os.path.join(os.path.dirname(__file__), '..', '..', 'module')), os.path.join(current_path, 'ForgeInspector'), skip_jar=True, inspector_config=True, include_tests=True, local_build_steps=os.path.join(current_path, 'ForgeInspector', 'assets', 'src')) # In the Android inspectors case we want any libs to be attached to the ForgeModule project, not the ForgeInspector if os.path.exists(os.path.join(current_path, 'ForgeInspector', 'libs')): for file_ in os.listdir( os.path.join(current_path, 'ForgeInspector', 'libs')): if not file_.startswith("."): shutil.move( os.path.join(current_path, 'ForgeInspector', 'libs', file_), os.path.join(current_path, 'ForgeModule', 'libs')) if os.path.exists( os.path.abspath( os.path.join(os.path.dirname(__file__), '..', '..', 'module', 'android', 'res'))): if not os.path.exists( os.path.join(current_path, 'ForgeModule', 'src')): os.makedirs(os.path.join(current_path, 'ForgeModule', 'src')) # Generate magic R.java if sys.platform.startswith('darwin'): aapt_exec = 'aapt_osx' elif sys.platform.startswith('win'): aapt_exec = 'aapt.exe' else: aapt_exec = 'aapt_linux' with open( os.path.abspath( os.path.join( os.path.dirname(__file__), '..', 'platform_version.txt'))) as platform_version_file: platform_version = platform_version_file.read().strip() subprocess.check_call([ utils.ensure_lib_available(cookies, platform_version, aapt_exec), 'package', '-m', '-M', os.path.join(current_path, 'ForgeModule', 'AndroidManifest.xml'), '-S', os.path.abspath( os.path.join(os.path.dirname(__file__), '..', '..', 'module', 'android', 'res')), '-J', os.path.join(current_path, 'ForgeModule', 'src'), '-I', utils.ensure_lib_available(cookies, platform_version, 'android-platform.apk') ]) for root, dirnames, filenames in os.walk( os.path.join(current_path, 'ForgeModule', 'src')): for filename in filenames: if filename != "R.java": continue # Tweak R.java to be magic with open(os.path.join(root, filename)) as source: content = source.read() # Don't tweak already tweaked files if content.find("import java.lang.reflect.Field") != -1: continue content = content.replace("final ", "") content = content.replace( "public class R", """import java.lang.reflect.Field; import android.util.Log; public class R""") content = re.sub( r'\/\* AUTO-GENERATED.*?\*\/', '''/* This file was generated as part of a ForgeModule. * * You may move this file to another package if you require, however do not modify its contents. * To add more resources: rebuild the inspector project. */''', content, flags=re.MULTILINE | re.DOTALL) content = re.sub( ''' public static class (\w+) {(.*?)\n }''', r''' public static class \1 {\2 static { try { Class<?> realRClass = Class.forName("io.trigger.forge.android.inspector.R$\1"); for (Field f : \1.class.getDeclaredFields()) { try { f.set(null, realRClass.getDeclaredField(f.getName()).get(null)); } catch (IllegalArgumentException e) { Log.e("Forge", e.toString()); } catch (IllegalAccessException e) { Log.e("Forge", e.toString()); } catch (NoSuchFieldException e) { Log.e("Forge", e.toString()); } } } catch (ClassNotFoundException e) { Log.e("Forge", e.toString()); } } }''', content, flags=re.MULTILINE | re.DOTALL) with open(os.path.join(root, filename), 'w') as output: output.write(content) except Exception: shutil.rmtree(current_path) try: raise #raise Exception("Applying build steps failed, check build steps and re-update inspector: %s" % e) finally: try: shutil.move(previous_path, current_path) except Exception: pass # Prefix eclipse project names with module name with open( os.path.abspath( os.path.join(os.path.dirname(__file__), '..', '..', 'module', 'manifest.json'))) as manifest_file: manifest = json.load(manifest_file) module_name = manifest['name'] for project in ('ForgeInspector', 'ForgeModule'): with open(os.path.join(current_path, project, '.project')) as project_file: project_conf = project_file.read() project_conf = project_conf.replace('<name>Forge', '<name>%s_Forge' % module_name) with open(os.path.join(current_path, project, '.project'), 'w') as project_file: project_file.write(project_conf) # Create hash for inspector with open(os.path.join(current_path, '.hash'), 'w') as hash_file: hash_file.write(hash_android())