Exemple #1
0
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)
Exemple #2
0
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)
Exemple #3
0
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)
Exemple #4
0
    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.'
                )
Exemple #5
0
    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)
Exemple #7
0
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())