Example #1
0
def test():
    """
    Execute your automated tests
    """
    require_existing_project()
    sys.path.append(path('src/main/python'))
    suite = TestSuite()
    test_dirs = SETTINGS['test_dirs']
    for test_dir in map(path, test_dirs):
        sys.path.append(test_dir)
        try:
            dir_names = listdir(test_dir)
        except FileNotFoundError:
            continue
        for dir_name in dir_names:
            dir_path = join(test_dir, dir_name)
            if isfile(join(dir_path, '__init__.py')):
                suite.addTest(defaultTestLoader.discover(
                    dir_name, top_level_dir=test_dir
                ))
    has_tests = bool(list(suite))
    if has_tests:
        TextTestRunner().run(suite)
    else:
        _LOG.warning(
            'No tests found. You can add them to:\n * '+
            '\n * '.join(test_dirs)
        )
Example #2
0
def release():
    """
    Bump version and run clean,freeze,...,upload
    """
    require_existing_project()
    version = SETTINGS['version']
    next_version = _get_next_version(version)
    release_version = prompt_for_value('Release version', default=next_version)
    activate_profile('release')
    SETTINGS['version'] = release_version
    log_level = _LOG.level
    if log_level == logging.NOTSET:
        _LOG.setLevel(logging.WARNING)
    try:
        clean()
        freeze()
        installer()
        sign_installer()
        repo()
    finally:
        _LOG.setLevel(log_level)
    upload()
    base_json = 'src/build/settings/base.json'
    update_json(path(base_json), {'version': release_version})
    _LOG.info('Also, %s was updated with the new version.', base_json)
Example #3
0
def release(version=None):
    """
    Bump version and run clean,freeze,...,upload
    """
    require_existing_project()
    if version is None:
        curr_version = SETTINGS['version']
        next_version = _get_next_version(curr_version)
        release_version = prompt_for_value('Release version',
                                           default=next_version)
    elif version == 'current':
        release_version = SETTINGS['version']
    else:
        release_version = version
    activate_profile('release')
    SETTINGS['version'] = release_version
    log_level = _LOG.level
    if log_level == logging.NOTSET:
        _LOG.setLevel(logging.WARNING)
    try:
        clean()
        freeze()
        if is_windows() and _has_windows_codesigning_certificate():
            sign()
        installer()
        if (is_windows() and _has_windows_codesigning_certificate()) or \
            is_arch_linux() or is_fedora():
            sign_installer()
        repo()
    finally:
        _LOG.setLevel(log_level)
    upload()
    base_json = 'src/build/settings/base.json'
    update_json(path(base_json), {'version': release_version})
    _LOG.info('Also, %s was updated with the new version.', base_json)
Example #4
0
def installer():
    """
    Create an installer for your app
    """
    require_existing_project()
    if not exists(path('${freeze_dir}')):
        raise FbsError(
            'It seems your app has not yet been frozen. Please run:\n'
            '    fbs freeze')
    linux_distribution_not_supported_msg = \
        "Your Linux distribution is not supported, sorry. " \
        "You can run `fbs buildvm` followed by `fbs runvm` to start a Docker " \
        "VM of a supported distribution."
    try:
        installer_fname = SETTINGS['installer']
    except KeyError:
        if is_linux():
            raise FbsError(linux_distribution_not_supported_msg)
        raise
    out_file = join('target', installer_fname)
    msg_parts = ['Created %s.' % out_file]
    if is_windows():
        from fbs.installer.windows import create_installer_windows
        create_installer_windows()
    elif is_mac():
        from fbs.installer.mac import create_installer_mac
        create_installer_mac()
    elif is_linux():
        app_name = SETTINGS['app_name']
        if is_ubuntu():
            from fbs.installer.ubuntu import create_installer_ubuntu
            create_installer_ubuntu()
            install_cmd = 'sudo dpkg -i ' + out_file
            remove_cmd = 'sudo dpkg --purge ' + app_name
        elif is_arch_linux():
            from fbs.installer.arch import create_installer_arch
            create_installer_arch()
            install_cmd = 'sudo pacman -U ' + out_file
            remove_cmd = 'sudo pacman -R ' + app_name
        elif is_fedora():
            from fbs.installer.fedora import create_installer_fedora
            create_installer_fedora()
            install_cmd = 'sudo dnf install ' + out_file
            remove_cmd = 'sudo dnf remove ' + app_name
        else:
            raise FbsError(linux_distribution_not_supported_msg)
        msg_parts.append(
            'You can for instance install it via the following command:\n'
            '    %s\n'
            'This places it in /opt/%s. To uninstall it again, you can use:\n'
            '    %s' % (install_cmd, app_name, remove_cmd))
    else:
        raise FbsError('Unsupported OS')
    _LOG.info(' '.join(msg_parts))
Example #5
0
def gengpgkey():
    """
    Generate a GPG key for code signing on Linux
    """
    require_existing_project()
    if exists(_DEST_DIR):
        raise FbsError('The %s folder already exists. Aborting.' % _DEST_DIR)
    try:
        email = prompt_for_value('Email address')
        name = prompt_for_value('Real name', default=SETTINGS['author'])
        passphrase = prompt_for_value('Key password', password=True)
    except KeyboardInterrupt:
        print('')
        return
    _LOG.info('Generating the GPG key. This can take a little...')
    _init_docker()
    args = ['run', '-t']
    if exists('/dev/urandom'):
        # Give the key generator more entropy on Posix:
        args.extend(['-v', '/dev/urandom:/dev/random'])
    args.extend([_DOCKER_IMAGE, '/root/genkey.sh', name, email, passphrase])
    result = _run_docker(args, check=True, stdout=PIPE, universal_newlines=True)
    key = _snip(
        result.stdout,
        "revocation certificate stored as '/root/.gnupg/openpgp-revocs.d/",
        ".rev'",
        include_bounds=False
    )
    pubkey = _snip(result.stdout,
                   '-----BEGIN PGP PUBLIC KEY BLOCK-----\n',
                   '-----END PGP PUBLIC KEY BLOCK-----\n')
    privkey = _snip(result.stdout,
                    '-----BEGIN PGP PRIVATE KEY BLOCK-----\n',
                    '-----END PGP PRIVATE KEY BLOCK-----\n')
    makedirs(path(_DEST_DIR), exist_ok=True)
    pubkey_dest = _DEST_DIR + '/' + _PUBKEY_NAME
    with open(path(pubkey_dest), 'w') as f:
        f.write(pubkey)
    privkey_dest = _DEST_DIR + '/' + _PRIVKEY_NAME
    with open(path(privkey_dest), 'w') as f:
        f.write(privkey)
    with open(path(_BASE_JSON)) as f:
        base_contents = f.read()
    new_base_contents = _extend_json(base_contents, {
        'gpg_key': key, 'gpg_name': name, 'gpg_pass': passphrase
    })
    with open(path(_BASE_JSON), 'w') as f:
        f.write(new_base_contents)
    _LOG.info(
        'Done. Created %s and ...%s. Also updated %s with the values you '
        'entered.', pubkey_dest, _PRIVKEY_NAME, _BASE_JSON
    )
Example #6
0
def sign_installer():
    """
    Sign installer, so the user's OS trusts it
    """
    require_existing_project()
    if is_arch_linux():
        from fbs.sign_installer.arch import sign_installer_arch
        sign_installer_arch()
    elif is_fedora():
        from fbs.sign_installer.fedora import sign_installer_fedora
        sign_installer_fedora()
    else:
        _LOG.info('This command is not (yet) supported on this platform.')
Example #7
0
def run():
    """
    Run your app from source
    """
    require_existing_project()
    if not _has_module('PyQt5') and not _has_module('PySide2'):
        raise FbsError("Couldn't find PyQt5 or PySide2. Maybe you need to:\n"
                       "    pip install PyQt5==5.9.2")
    env = dict(os.environ)
    pythonpath = path('src/main/python')
    old_pythonpath = env.get('PYTHONPATH', '')
    if old_pythonpath:
        pythonpath += os.pathsep + old_pythonpath
    env['PYTHONPATH'] = pythonpath
    subprocess.run([sys.executable, path(SETTINGS['main_module'])], env=env)
Example #8
0
def freeze(debug=False):
    """
    Compile your code to a standalone executable
    """
    require_existing_project()
    if not _has_module('PyInstaller'):
        raise FbsError("Could not find PyInstaller. Maybe you need to:\n"
                       "    pip install PyInstaller==3.4")
    version = SETTINGS['version']
    if not is_valid_version(version):
        raise FbsError(
            'Invalid version detected in settings. It should be three\n'
            'numbers separated by dots, such as "1.2.3". You have:\n\t"%s".\n'
            'Usually, this can be fixed in src/build/settings/base.json.' %
            version)
    # Import respective functions late to avoid circular import
    # fbs <-> fbs.freeze.X.
    app_name = SETTINGS['app_name']
    if is_mac():
        from fbs.freeze.mac import freeze_mac
        freeze_mac(debug=debug)
        executable = 'target/%s.app/Contents/MacOS/%s' % (app_name, app_name)
    else:
        executable = join('target', app_name, app_name)
        if is_windows():
            from fbs.freeze.windows import freeze_windows
            freeze_windows(debug=debug)
            executable += '.exe'
        elif is_linux():
            if is_ubuntu():
                from fbs.freeze.ubuntu import freeze_ubuntu
                freeze_ubuntu(debug=debug)
            elif is_arch_linux():
                from fbs.freeze.arch import freeze_arch
                freeze_arch(debug=debug)
            elif is_fedora():
                from fbs.freeze.fedora import freeze_fedora
                freeze_fedora(debug=debug)
            else:
                from fbs.freeze.linux import freeze_linux
                freeze_linux(debug=debug)
        else:
            raise FbsError('Unsupported OS')
    _LOG.info(
        "Done. You can now run `%s`. If that doesn't work, see "
        "https://build-system.fman.io/troubleshooting.", executable)
Example #9
0
def freeze(debug=False):
    """
    Compile your code to a standalone executable
    """
    require_existing_project()
    if not _has_module('PyInstaller'):
        raise FbsError(
            "Could not find PyInstaller. Maybe you need to:\n"
            "    pip install PyInstaller==3.4"
        )
    # Import respective functions late to avoid circular import
    # fbs <-> fbs.freeze.X.
    app_name = SETTINGS['app_name']
    if is_mac():
        from fbs.freeze.mac import freeze_mac
        freeze_mac(debug=debug)
        executable = 'target/%s.app/Contents/MacOS/%s' % (app_name, app_name)
    else:
        executable = join('target', app_name, app_name)
        if is_windows():
            from fbs.freeze.windows import freeze_windows
            freeze_windows(debug=debug)
            executable += '.exe'
        elif is_linux():
            if is_ubuntu():
                from fbs.freeze.ubuntu import freeze_ubuntu
                freeze_ubuntu(debug=debug)
            elif is_arch_linux():
                from fbs.freeze.arch import freeze_arch
                freeze_arch(debug=debug)
            elif is_fedora():
                from fbs.freeze.fedora import freeze_fedora
                freeze_fedora(debug=debug)
            else:
                from fbs.freeze.linux import freeze_linux
                freeze_linux(debug=debug)
        else:
            raise FbsError('Unsupported OS')
    _LOG.info(
        "Done. You can now run `%s`. If that doesn't work, see "
        "https://build-system.fman.io/troubleshooting.", executable
    )
Example #10
0
def release(version=None):
    """
    Bump version and run clean,freeze,...,upload
    """
    require_existing_project()
    if version is None:
        curr_version = SETTINGS['version']
        next_version = _get_next_version(curr_version)
        release_version = prompt_for_value('Release version',
                                           default=next_version)
    elif version == 'current':
        release_version = SETTINGS['version']
    else:
        release_version = version
    if not is_valid_version(release_version):
        if not is_valid_version(version):
            raise FbsError(
                'The release version of your app is invalid. It should be '
                'three\nnumbers separated by dots, such as "1.2.3". '
                'You have: "%s".' % release_version)
    activate_profile('release')
    SETTINGS['version'] = release_version
    log_level = _LOG.level
    if log_level == logging.NOTSET:
        _LOG.setLevel(logging.WARNING)
    try:
        clean()
        freeze()
        if is_windows() and _has_windows_codesigning_certificate():
            sign()
        installer()
        if (is_windows() and _has_windows_codesigning_certificate()) or \
            is_arch_linux() or is_fedora():
            sign_installer()
        if _repo_is_supported():
            repo()
    finally:
        _LOG.setLevel(log_level)
    upload()
    base_json = 'src/build/settings/base.json'
    update_json(path(base_json), {'version': release_version})
    _LOG.info('Also, %s was updated with the new version.', base_json)
Example #11
0
def installer():
    """
    Create an installer for your app
    """
    require_existing_project()
    out_file = join('target', SETTINGS['installer'])
    msg_parts = ['Created %s.' % out_file]
    if is_windows():
        from fbs.installer.windows import create_installer_windows
        create_installer_windows()
    elif is_mac():
        from fbs.installer.mac import create_installer_mac
        create_installer_mac()
    elif is_linux():
        app_name = SETTINGS['app_name']
        if is_ubuntu():
            from fbs.installer.ubuntu import create_installer_ubuntu
            create_installer_ubuntu()
            install_cmd = 'sudo dpkg -i ' + out_file
            remove_cmd = 'sudo dpkg --purge ' + app_name
        elif is_arch_linux():
            from fbs.installer.arch import create_installer_arch
            create_installer_arch()
            install_cmd = 'sudo pacman -U ' + out_file
            remove_cmd = 'sudo pacman -R ' + app_name
        elif is_fedora():
            from fbs.installer.fedora import create_installer_fedora
            create_installer_fedora()
            install_cmd = 'sudo dnf install ' + out_file
            remove_cmd = 'sudo dnf remove ' + app_name
        else:
            raise FbsError('Unsupported Linux distribution')
        msg_parts.append(
            'You can for instance install it via the following command:\n'
            '    %s\n'
            'This places it in /opt/%s. To uninstall it again, you can use:\n'
            '    %s' % (install_cmd, app_name, remove_cmd))
    else:
        raise FbsError('Unsupported OS')
    _LOG.info(' '.join(msg_parts))
Example #12
0
def upload():
    """
    Upload installer and repository to fbs.sh
    """
    require_existing_project()
    try:
        username = SETTINGS['fbs_user']
        password = SETTINGS['fbs_pass']
    except KeyError as e:
        raise FbsError(
            'Could not find setting "%s". You may want to invoke one of the '
            'following:\n'
            ' * fbs register\n'
            ' * fbs login'
            % (e.args[0],)
        ) from None
    _upload_repo(username, password)
    app_name = SETTINGS['app_name']
    url = lambda p: 'https://fbs.sh/%s/%s/%s' % (username, app_name, p)
    message = 'Done! '
    pkg_name = app_name.lower()
    installer_url = url(SETTINGS['installer'])
    if is_linux():
        message += 'Your users can now install your app via the following ' \
                   'commands:\n'
        format_commands = lambda *cmds: '\n'.join('    ' + c for c in cmds)
        repo_url = url(SETTINGS['repo_subdir'])
        if is_ubuntu():
            message += format_commands(
                "sudo apt-get install apt-transport-https",
                "wget -qO - %s | sudo apt-key add -" % url('public-key.gpg'),
                "echo 'deb [arch=amd64] %s stable main' | " % repo_url +
                "sudo tee /etc/apt/sources.list.d/%s.list" % pkg_name,
                "sudo apt-get update",
                "sudo apt-get install " + pkg_name
            )
            message += '\nIf they already have your app installed, they can ' \
                       'force an immediate update via:\n'
            message += format_commands(
                'sudo apt-get update '
                '-o Dir::Etc::sourcelist="/etc/apt/sources.list.d/%s.list" '
                '-o Dir::Etc::sourceparts="-" -o APT::Get::List-Cleanup="0"'
                % pkg_name,
                'sudo apt-get install --only-upgrade ' + pkg_name
            )
        elif is_arch_linux():
            message += format_commands(
                "curl -O %s && " % url('public-key.gpg') +
                "sudo pacman-key --add public-key.gpg && " +
                "sudo pacman-key --lsign-key %s && " % SETTINGS['gpg_key'] +
                "rm public-key.gpg",
                "echo -e '\\n[%s]\\nServer = %s' | sudo tee -a /etc/pacman.conf"
                % (app_name, repo_url),
                "sudo pacman -Syu " + pkg_name
            )
            message += '\nIf they already have your app installed, they can ' \
                       'force an immediate update via:\n'
            message += format_commands('sudo pacman -Syu --needed ' + pkg_name)
        elif is_fedora():
            message += format_commands(
                "sudo rpm -v --import " + url('public-key.gpg'),
                "sudo dnf config-manager --add-repo %s/%s.repo"
                % (repo_url, app_name),
                "sudo dnf install " + pkg_name
            )
            message += "\n(On CentOS, replace 'dnf' by 'yum' and " \
                       "'dnf config-manager' by 'yum-config-manager'.)"
            message += '\nIf they already have your app installed, they can ' \
                       'force an immediate update via:\n'
            message += \
                format_commands('sudo dnf upgrade %s --refresh' % pkg_name)
            message += '\nThis is for Fedora. For CentOS, use:\n'
            message += format_commands(
                'sudo yum clean all && sudo yum upgrade ' + pkg_name
            )
        else:
            raise FbsError('This Linux distribution is not supported.')
        message += '\nFinally, your users can also install without automatic ' \
                   'updates by downloading:\n    ' + installer_url
        extra = {'wrap': False}
    else:
        message += 'Your users can now download and install %s.' % installer_url
        extra = None
    _LOG.info(message, extra=extra)
Example #13
0
def repo():
    """
    Generate files for automatic updates
    """
    require_existing_project()
    if not _repo_is_supported():
        raise FbsError('This command is not supported on this platform.')
    app_name = SETTINGS['app_name']
    pkg_name = app_name.lower()
    try:
        gpg_key = SETTINGS['gpg_key']
    except KeyError:
        raise FbsError(
            'GPG key for code signing is not configured. You might want to '
            'either\n'
            '    1) run `fbs gengpgkey` or\n'
            '    2) set "gpg_key" and "gpg_pass" in src/build/settings/.'
        )
    if is_ubuntu():
        from fbs.repo.ubuntu import create_repo_ubuntu
        if not SETTINGS['description']:
            _LOG.info(
                'Hint: Your app\'s "description" is empty. Consider setting it '
                'in src/build/settings/linux.json.'
            )
        create_repo_ubuntu()
        _LOG.info(
            'Done. You can test the repository with the following commands:\n'
            '    echo "deb [arch=amd64] file://%s stable main" '
                '| sudo tee /etc/apt/sources.list.d/%s.list\n'
            '    sudo apt-key add %s\n'
            '    sudo apt-get update\n'
            '    sudo apt-get install %s\n'
            'To revert these changes:\n'
            '    sudo dpkg --purge %s\n'
            '    sudo apt-key del %s\n'
            '    sudo rm /etc/apt/sources.list.d/%s.list\n'
            '    sudo apt-get update',
            path('target/repo'), pkg_name,
            path('src/sign/linux/public-key.gpg'), pkg_name, pkg_name, gpg_key,
            pkg_name,
            extra={'wrap': False}
        )
    elif is_arch_linux():
        from fbs.repo.arch import create_repo_arch
        create_repo_arch()
        _LOG.info(
            "Done. You can test the repository with the following commands:\n"
            "    sudo cp /etc/pacman.conf /etc/pacman.conf.bu\n"
            "    echo -e '\\n[%s]\\nServer = file://%s' "
                "| sudo tee -a /etc/pacman.conf\n"
            "    sudo pacman-key --add %s\n"
            "    sudo pacman-key --lsign-key %s\n"
            "    sudo pacman -Syu %s\n"
            "To revert these changes:\n"
            "    sudo pacman -R %s\n"
            "    sudo pacman-key --delete %s\n"
            "    sudo mv /etc/pacman.conf.bu /etc/pacman.conf",
            app_name, path('target/repo'),
            path('src/sign/linux/public-key.gpg'), gpg_key, pkg_name, pkg_name,
            gpg_key,
            extra={'wrap': False}
        )
    else:
        assert is_fedora()
        from fbs.repo.fedora import create_repo_fedora
        create_repo_fedora()
        _LOG.info(
            "Done. You can test the repository with the following commands:\n"
            "    sudo rpm -v --import %s\n"
            "    sudo dnf config-manager --add-repo file://%s/target/repo\n"
            "    sudo dnf install %s\n"
            "To revert these changes:\n"
            "    sudo dnf remove %s\n"
            "    sudo rm /etc/yum.repos.d/*%s*.repo\n"
            "    sudo rpm --erase gpg-pubkey-%s",
            path('src/sign/linux/public-key.gpg'), SETTINGS['project_dir'],
            pkg_name, pkg_name, app_name, gpg_key[-8:].lower(),
            extra={'wrap': False}
        )