Exemplo n.º 1
0
def on_platformio_start(ctx, force, caller):
    app.set_session_var("command_ctx", ctx)
    app.set_session_var("force_option", force)
    set_caller(caller)
    telemetry.on_command()

    if PlatformioCLI.in_silence():
        return

    after_upgrade(ctx)

    if not ensure_python3(raise_exception=False):
        click.secho(
            """
Python 2 and Python 3.5 are not compatible with PlatformIO Core 5.0.
Please check the migration guide on how to fix this warning message:
""",
            fg="yellow",
        )
        click.secho(
            "https://docs.platformio.org/en/latest/core/migration.html"
            "#drop-support-for-python-2-and-3-5",
            fg="blue",
        )
        click.echo("")
Exemplo n.º 2
0
def on_platformio_start(ctx, force, caller):
    app.set_session_var("command_ctx", ctx)
    app.set_session_var("force_option", force)
    set_caller(caller)
    telemetry.on_command()

    if not in_silence(ctx):
        after_upgrade(ctx)
Exemplo n.º 3
0
def on_platformio_start(ctx, force, caller):
    app.set_session_var("command_ctx", ctx)
    app.set_session_var("force_option", force)
    set_caller(caller)
    telemetry.on_command()

    if not in_silence(ctx):
        after_upgrade(ctx)
Exemplo n.º 4
0
def cli(  # pylint: disable=too-many-arguments, too-many-branches
    ctx,
    src,
    lib,
    exclude,
    board,
    build_dir,
    keep_build_dir,
    project_conf,
    project_option,
    verbose,
):

    if not src and getenv("PLATFORMIO_CI_SRC"):
        src = validate_path(ctx, None, getenv("PLATFORMIO_CI_SRC").split(":"))
    if not src:
        raise click.BadParameter("Missing argument 'src'")

    try:
        app.set_session_var("force_option", True)

        if not keep_build_dir and isdir(build_dir):
            fs.rmtree(build_dir)
        if not isdir(build_dir):
            makedirs(build_dir)

        for dir_name, patterns in dict(lib=lib, src=src).items():
            if not patterns:
                continue
            contents = []
            for p in patterns:
                contents += glob(p)
            _copy_contents(join(build_dir, dir_name), contents)

        if project_conf and isfile(project_conf):
            _copy_project_conf(build_dir, project_conf)
        elif not board:
            raise CIBuildEnvsEmpty()

        if exclude:
            _exclude_contents(build_dir, exclude)

        # initialise project
        ctx.invoke(
            cmd_project_init,
            project_dir=build_dir,
            board=board,
            project_option=project_option,
        )

        # process project
        ctx.invoke(cmd_run, project_dir=build_dir, verbose=verbose)
    finally:
        if not keep_build_dir:
            fs.rmtree(build_dir)
Exemplo n.º 5
0
def on_platformio_start(ctx, force, caller):
    ensure_python3(raise_exception=True)

    app.set_session_var("command_ctx", ctx)
    app.set_session_var("force_option", force)
    set_caller(caller)
    telemetry.on_command()

    if PlatformioCLI.in_silence():
        return
    after_upgrade(ctx)
Exemplo n.º 6
0
def set_caller(caller=None):
    caller = caller or getenv("PLATFORMIO_CALLER")
    if not caller:
        if getenv("VSCODE_PID") or getenv("VSCODE_NLS_CONFIG"):
            caller = "vscode"
        elif is_container():
            if getenv("C9_UID"):
                caller = "C9"
            elif getenv("USER") == "cabox":
                caller = "CA"
            elif getenv("CHE_API", getenv("CHE_API_ENDPOINT")):
                caller = "Che"
    app.set_session_var("caller_id", caller)
Exemplo n.º 7
0
def set_caller(caller=None):
    if not caller:
        if getenv("PLATFORMIO_CALLER"):
            caller = getenv("PLATFORMIO_CALLER")
        elif getenv("VSCODE_PID") or getenv("VSCODE_NLS_CONFIG"):
            caller = "vscode"
        elif util.is_container():
            if getenv("C9_UID"):
                caller = "C9"
            elif getenv("USER") == "cabox":
                caller = "CA"
            elif getenv("CHE_API", getenv("CHE_API_ENDPOINT")):
                caller = "Che"
    app.set_session_var("caller_id", caller)
Exemplo n.º 8
0
def cli(
        ctx,
        src,
        lib,
        exclude,
        board,  # pylint: disable=R0913
        build_dir,
        keep_build_dir,
        project_conf,
        verbose):

    if not src:
        src = getenv("PLATFORMIO_CI_SRC", "").split(":")
    if not src:
        raise click.BadParameter("Missing argument 'src'")

    try:
        app.set_session_var("force_option", True)
        _clean_dir(build_dir)

        for dir_name, patterns in dict(lib=lib, src=src).iteritems():
            if not patterns:
                continue
            contents = []
            for p in patterns:
                contents += glob(p)
            _copy_contents(join(build_dir, dir_name), contents)

        if project_conf and isfile(project_conf):
            copyfile(project_conf, join(build_dir, "platformio.ini"))
        elif not board:
            raise CIBuildEnvsEmpty()

        if exclude:
            _exclude_contents(build_dir, exclude)

        # initialise project
        ctx.invoke(cmd_init,
                   project_dir=build_dir,
                   board=board,
                   disable_auto_uploading=True)

        # process project
        ctx.invoke(cmd_run, project_dir=build_dir, verbose=verbose)
    finally:
        if not keep_build_dir:
            rmtree(build_dir,
                   onerror=lambda action, name, exc:
                   (chmod(name, stat.S_IWRITE), remove(name)))
Exemplo n.º 9
0
def set_caller(caller=None):
    caller = caller or getenv("PLATFORMIO_CALLER")
    if caller:
        return app.set_session_var("caller_id", caller)
    if getenv("VSCODE_PID") or getenv("VSCODE_NLS_CONFIG"):
        caller = "vscode"
    elif getenv("GITPOD_INSTANCE_ID") or getenv("GITPOD_WORKSPACE_URL"):
        caller = "gitpod"
    elif is_container():
        if getenv("C9_UID"):
            caller = "C9"
        elif getenv("USER") == "cabox":
            caller = "CA"
        elif getenv("CHE_API", getenv("CHE_API_ENDPOINT")):
            caller = "Che"
    return app.set_session_var("caller_id", caller)
Exemplo n.º 10
0
def cli(  # pylint: disable=too-many-arguments, too-many-branches
        ctx, src, lib, exclude, board, build_dir, keep_build_dir, project_conf,
        project_option, verbose):

    if not src and getenv("PLATFORMIO_CI_SRC"):
        src = validate_path(ctx, None, getenv("PLATFORMIO_CI_SRC").split(":"))
    if not src:
        raise click.BadParameter("Missing argument 'src'")

    try:
        app.set_session_var("force_option", True)

        if not keep_build_dir and isdir(build_dir):
            util.rmtree_(build_dir)
        if not isdir(build_dir):
            makedirs(build_dir)

        for dir_name, patterns in dict(lib=lib, src=src).items():
            if not patterns:
                continue
            contents = []
            for p in patterns:
                contents += glob(p)
            _copy_contents(join(build_dir, dir_name), contents)

        if project_conf and isfile(project_conf):
            _copy_project_conf(build_dir, project_conf)
        elif not board:
            raise CIBuildEnvsEmpty()

        if exclude:
            _exclude_contents(build_dir, exclude)

        # initialise project
        ctx.invoke(
            cmd_init,
            project_dir=build_dir,
            board=board,
            project_option=project_option)

        # process project
        ctx.invoke(cmd_run, project_dir=build_dir, verbose=verbose)
    finally:
        if not keep_build_dir:
            util.rmtree_(build_dir)
Exemplo n.º 11
0
def on_platformio_start(ctx, force):
    app.set_session_var("force_option", force)
    telemetry.on_command(ctx)

    # skip any check operations when upgrade process
    args = [str(s).lower() for s in sys.argv[1:]
            if not str(s).startswith("-")]
    if len(args) > 1 and args[1] == "upgrade":
        return

    after_upgrade(ctx)

    try:
        check_platformio_upgrade()
        check_internal_updates(ctx, "platforms")
        check_internal_updates(ctx, "libraries")
    except (exception.GetLatestVersionError, exception.APIRequestError):
        click.secho("Failed to check for PlatformIO upgrades. "
                    "Please check your Internet connection.", fg="red")
Exemplo n.º 12
0
def on_platformio_start(ctx, force):
    app.set_session_var("force_option", force)
    telemetry.on_command(ctx)

    # skip any check operations when upgrade process
    args = [str(s).lower() for s in sys.argv[1:] if not str(s).startswith("-")]
    if len(args) > 1 and args[1] == "upgrade":
        return

    after_upgrade(ctx)

    try:
        check_platformio_upgrade()
        check_internal_updates(ctx, "platforms")
        check_internal_updates(ctx, "libraries")
    except (exception.GetLatestVersionError, exception.APIRequestError):
        click.secho(
            "Failed to check for PlatformIO upgrades. "
            "Please check your Internet connection.",
            fg="red")
Exemplo n.º 13
0
def cli(ctx, src, lib, exclude, board,  # pylint: disable=R0913
        build_dir, keep_build_dir, project_conf, verbose):

    if not src:
        src = getenv("PLATFORMIO_CI_SRC", "").split(":")
    if not src:
        raise click.BadParameter("Missing argument 'src'")

    try:
        app.set_session_var("force_option", True)
        _clean_dir(build_dir)

        for dir_name, patterns in dict(lib=lib, src=src).iteritems():
            if not patterns:
                continue
            contents = []
            for p in patterns:
                contents += glob(p)
            _copy_contents(join(build_dir, dir_name), contents)

        if project_conf and isfile(project_conf):
            copyfile(project_conf, join(build_dir, "platformio.ini"))
        elif not board:
            raise CIBuildEnvsEmpty()

        if exclude:
            _exclude_contents(build_dir, exclude)

        # initialise project
        ctx.invoke(cmd_init, project_dir=build_dir, board=board,
                   disable_auto_uploading=True)

        # process project
        ctx.invoke(cmd_run, project_dir=build_dir, verbose=verbose)
    finally:
        if not keep_build_dir:
            rmtree(
                build_dir, onerror=lambda action, name, exc:
                (chmod(name, stat.S_IWRITE), remove(name))
            )
Exemplo n.º 14
0
def on_platformio_start(ctx, force, caller):
    app.set_session_var("command_ctx", ctx)
    app.set_session_var("force_option", force)
    app.set_session_var("caller_id", caller)
    telemetry.on_command()

    # skip any check operations when upgrade command
    ctx_args = ctx.args or []
    if ctx_args and (ctx.args[0] == "upgrade" or "--json-output" in ctx_args):
        return

    after_upgrade(ctx)
Exemplo n.º 15
0
def on_platformio_start(ctx, force, caller):
    app.set_session_var("command_ctx", ctx)
    app.set_session_var("force_option", force)
    app.set_session_var("caller_id", caller)
    telemetry.on_command()

    # skip any check operations when upgrade command
    ctx_args = ctx.args or []
    if ctx_args and (ctx.args[0] == "upgrade" or "--json-output" in ctx_args):
        return

    after_upgrade(ctx)
Exemplo n.º 16
0
def on_platformio_start(ctx, force, caller):
    if not caller:
        if getenv("PLATFORMIO_CALLER"):
            caller = getenv("PLATFORMIO_CALLER")
        elif getenv("C9_UID"):
            caller = "C9"

    app.set_session_var("command_ctx", ctx)
    app.set_session_var("force_option", force)
    app.set_session_var("caller_id", caller)
    telemetry.on_command()

    if not in_silence(ctx):
        after_upgrade(ctx)
Exemplo n.º 17
0
def on_platformio_start(ctx, force, caller):
    if not caller:
        if getenv("PLATFORMIO_CALLER"):
            caller = getenv("PLATFORMIO_CALLER")
        elif util.is_container():
            if getenv("C9_UID"):
                caller = "C9"
            elif getenv("USER") == "cabox":
                caller = "CA"
            elif getenv("CHE_API", getenv("CHE_API_ENDPOINT")):
                caller = "Che"

    app.set_session_var("command_ctx", ctx)
    app.set_session_var("force_option", force)
    app.set_session_var("caller_id", caller)
    telemetry.on_command()

    if not in_silence(ctx):
        after_upgrade(ctx)
Exemplo n.º 18
0
def on_platformio_start(ctx, force, caller):
    app.set_session_var("command_ctx", ctx)
    app.set_session_var("force_option", force)
    app.set_session_var("caller_id", caller)
    telemetry.on_command()

    # skip any check operations when upgrade command
    if len(ctx.args or []) and ctx.args[0] == "upgrade":
        return

    after_upgrade(ctx)

    try:
        check_platformio_upgrade()
        check_internal_updates(ctx, "platforms")
        check_internal_updates(ctx, "libraries")
    except (exception.GetLatestVersionError, exception.APIRequestError):
        click.secho("Failed to check for PlatformIO upgrades. "
                    "Please check your Internet connection.", fg="red")
Exemplo n.º 19
0
def on_platformio_start(ctx, force, caller):
    app.set_session_var("command_ctx", ctx)
    app.set_session_var("force_option", force)
    app.set_session_var("caller_id", caller)
    telemetry.on_command()

    # skip any check operations when upgrade command
    if len(ctx.args or []) and ctx.args[0] == "upgrade":
        return

    after_upgrade(ctx)

    try:
        check_platformio_upgrade()
        check_internal_updates(ctx, "platforms")
        check_internal_updates(ctx, "libraries")
    except (exception.GetLatestVersionError, exception.APIRequestError):
        click.secho("Failed to check for PlatformIO upgrades. "
                    "Please check your Internet connection.", fg="red")
Exemplo n.º 20
0
def on_platformio_start(ctx, force, caller):
    if not caller:
        if getenv("PLATFORMIO_CALLER"):
            caller = getenv("PLATFORMIO_CALLER")
        elif util.is_container():
            if getenv("C9_UID"):
                caller = "C9"
            elif getenv("USER") == "cabox":
                caller = "CA"
            elif getenv("CHE_API", getenv("CHE_API_ENDPOINT")):
                caller = "Che"

    app.set_session_var("command_ctx", ctx)
    app.set_session_var("force_option", force)
    app.set_session_var("caller_id", caller)
    telemetry.on_command()

    if ctx.args and (ctx.args[0] == "upgrade" or "update" in ctx.args):
        clean_cache()
    if not in_silence(ctx):
        after_upgrade(ctx)
Exemplo n.º 21
0
def cli(ctx, project_dir, project_conf, environment, verbose, interface,
        __unprocessed):
    app.set_session_var("custom_project_conf", project_conf)

    # use env variables from Eclipse or CLion
    for sysenv in ("CWD", "PWD", "PLATFORMIO_PROJECT_DIR"):
        if is_platformio_project(project_dir):
            break
        if os.getenv(sysenv):
            project_dir = os.getenv(sysenv)

    with fs.cd(project_dir):
        config = ProjectConfig.get_instance(project_conf)
        config.validate(envs=[environment] if environment else None)

        env_name = environment or helpers.get_default_debug_env(config)
        env_options = config.items(env=env_name, as_dict=True)
        if not set(env_options.keys()) >= set(["platform", "board"]):
            raise ProjectEnvsNotAvailableError()
        debug_options = helpers.validate_debug_options(ctx, env_options)
        assert debug_options

    if not interface:
        return helpers.predebug_project(ctx, project_dir, env_name, False,
                                        verbose)

    configuration = load_project_ide_data(project_dir, env_name)
    if not configuration:
        raise DebugInvalidOptionsError("Could not load debug configuration")

    if "--version" in __unprocessed:
        result = proc.exec_command([configuration["gdb_path"], "--version"])
        if result["returncode"] == 0:
            return click.echo(result["out"])
        raise exception.PlatformioException("\n".join(
            [result["out"], result["err"]]))

    try:
        fs.ensure_udev_rules()
    except exception.InvalidUdevRules as e:
        click.echo(
            helpers.escape_gdbmi_stream("~",
                                        str(e) + "\n")
            if helpers.is_gdbmi_mode() else str(e) + "\n",
            nl=False,
        )

    debug_options["load_cmds"] = helpers.configure_esp32_load_cmds(
        debug_options, configuration)

    rebuild_prog = False
    preload = debug_options["load_cmds"] == ["preload"]
    load_mode = debug_options["load_mode"]
    if load_mode == "always":
        rebuild_prog = preload or not helpers.has_debug_symbols(
            configuration["prog_path"])
    elif load_mode == "modified":
        rebuild_prog = helpers.is_prog_obsolete(
            configuration["prog_path"]) or not helpers.has_debug_symbols(
                configuration["prog_path"])
    else:
        rebuild_prog = not isfile(configuration["prog_path"])

    if preload or (not rebuild_prog and load_mode != "always"):
        # don't load firmware through debug server
        debug_options["load_cmds"] = []

    if rebuild_prog:
        if helpers.is_gdbmi_mode():
            click.echo(
                helpers.escape_gdbmi_stream(
                    "~", "Preparing firmware for debugging...\n"),
                nl=False,
            )
            stream = helpers.GDBMIConsoleStream()
            with util.capture_std_streams(stream):
                helpers.predebug_project(ctx, project_dir, env_name, preload,
                                         verbose)
            stream.close()
        else:
            click.echo("Preparing firmware for debugging...")
            helpers.predebug_project(ctx, project_dir, env_name, preload,
                                     verbose)

        # save SHA sum of newly created prog
        if load_mode == "modified":
            helpers.is_prog_obsolete(configuration["prog_path"])

    if not isfile(configuration["prog_path"]):
        raise DebugInvalidOptionsError("Program/firmware is missed")

    # run debugging client
    inject_contrib_pysite()

    # pylint: disable=import-outside-toplevel
    from platformio.commands.debug.process.client import GDBClient, reactor

    client = GDBClient(project_dir, __unprocessed, debug_options, env_options)
    client.spawn(configuration["gdb_path"], configuration["prog_path"])

    signal.signal(signal.SIGINT, lambda *args, **kwargs: None)
    reactor.run()

    return True
Exemplo n.º 22
0
def cli(  # pylint: disable=redefined-builtin
    ctx,
    environment,
    ignore,
    filter,
    upload_port,
    test_port,
    project_dir,
    project_conf,
    without_building,
    without_uploading,
    without_testing,
    no_reset,
    monitor_rts,
    monitor_dtr,
    verbose,
):
    app.set_session_var("custom_project_conf", project_conf)

    with fs.cd(project_dir):
        config = ProjectConfig.get_instance(project_conf)
        config.validate(envs=environment)

        test_dir = config.get_optional_dir("test")
        if not isdir(test_dir):
            raise exception.TestDirNotExists(test_dir)
        test_names = get_test_names(test_dir)

        if not verbose:
            click.echo("Verbose mode can be enabled via `-v, --verbose` option")
        click.secho("Collected %d items" % len(test_names), bold=True)

        results = []
        default_envs = config.default_envs()
        for testname in test_names:

            for envname in config.envs():
                section = "env:%s" % envname

                # filter and ignore patterns
                patterns = dict(filter=list(filter), ignore=list(ignore))
                for key in patterns:
                    patterns[key].extend(config.get(section, "test_%s" % key, []))

                skip_conditions = [
                    environment and envname not in environment,
                    not environment and default_envs and envname not in default_envs,
                    testname != "*"
                    and patterns["filter"]
                    and not any(fnmatch(testname, p) for p in patterns["filter"]),
                    testname != "*"
                    and any(fnmatch(testname, p) for p in patterns["ignore"]),
                ]
                if any(skip_conditions):
                    results.append({"env": envname, "test": testname})
                    continue

                click.echo()
                print_processing_header(testname, envname)

                cls = (
                    EmbeddedTestProcessor
                    if is_embedded_platform(config.get(section, "platform"))
                    else NativeTestProcessor
                )
                tp = cls(
                    ctx,
                    testname,
                    envname,
                    dict(
                        project_config=config,
                        project_dir=project_dir,
                        upload_port=upload_port,
                        test_port=test_port,
                        without_building=without_building,
                        without_uploading=without_uploading,
                        without_testing=without_testing,
                        no_reset=no_reset,
                        monitor_rts=monitor_rts,
                        monitor_dtr=monitor_dtr,
                        verbose=verbose,
                        silent=not verbose,
                    ),
                )
                result = {
                    "env": envname,
                    "test": testname,
                    "duration": time(),
                    "succeeded": tp.process(),
                }
                result["duration"] = time() - result["duration"]
                results.append(result)

                print_processing_footer(result)

    if without_testing:
        return

    print_testing_summary(results, verbose)

    command_failed = any(r.get("succeeded") is False for r in results)
    if command_failed:
        raise exception.ReturnErrorCode(1)
Exemplo n.º 23
0
def cli(
    environment,
    project_dir,
    project_conf,
    pattern,
    flags,
    severity,
    silent,
    verbose,
    json_output,
    fail_on_defect,
    skip_packages,
):
    app.set_session_var("custom_project_conf", project_conf)

    # find project directory on upper level
    if isfile(project_dir):
        project_dir = find_project_dir_above(project_dir)

    results = []
    with fs.cd(project_dir):
        config = ProjectConfig.get_instance(project_conf)
        config.validate(environment)

        default_envs = config.default_envs()
        for envname in config.envs():
            skipenv = any([
                environment and envname not in environment,
                not environment and default_envs
                and envname not in default_envs,
            ])

            env_options = config.items(env=envname, as_dict=True)
            env_dump = []
            for k, v in env_options.items():
                if k not in ("platform", "framework", "board"):
                    continue
                env_dump.append(
                    "%s: %s" % (k, ", ".join(v) if isinstance(v, list) else v))

            default_patterns = [
                config.get_optional_dir("src"),
                config.get_optional_dir("include"),
            ]
            tool_options = dict(
                verbose=verbose,
                silent=silent,
                patterns=pattern
                or env_options.get("check_patterns", default_patterns),
                flags=flags or env_options.get("check_flags"),
                severity=[
                    DefectItem.SEVERITY_LABELS[DefectItem.SEVERITY_HIGH]
                ] if silent else severity
                or config.get("env:" + envname, "check_severity"),
                skip_packages=skip_packages
                or env_options.get("check_skip_packages"),
            )

            for tool in config.get("env:" + envname, "check_tool"):
                if skipenv:
                    results.append({"env": envname, "tool": tool})
                    continue
                if not silent and not json_output:
                    print_processing_header(tool, envname, env_dump)

                ct = CheckToolFactory.new(tool, project_dir, config, envname,
                                          tool_options)

                result = {"env": envname, "tool": tool, "duration": time()}
                rc = ct.check(on_defect_callback=None if (
                    json_output or verbose
                ) else lambda defect: click.echo(repr(defect)))

                result["defects"] = ct.get_defects()
                result["duration"] = time() - result["duration"]

                result["succeeded"] = rc == 0
                if fail_on_defect:
                    result["succeeded"] = rc == 0 and not any(
                        DefectItem.SEVERITY_LABELS[
                            d.severity] in fail_on_defect
                        for d in result["defects"])
                result["stats"] = collect_component_stats(result)
                results.append(result)

                if verbose:
                    click.echo("\n".join(repr(d) for d in result["defects"]))

                if not json_output and not silent:
                    if rc != 0:
                        click.echo("Error: %s failed to perform check! Please "
                                   "examine tool output in verbose mode." %
                                   tool)
                    elif not result["defects"]:
                        click.echo("No defects found")
                    print_processing_footer(result)

        if json_output:
            click.echo(dump_json_to_unicode(results_to_json(results)))
        elif not silent:
            print_check_summary(results)

    command_failed = any(r.get("succeeded") is False for r in results)
    if command_failed:
        raise exception.ReturnErrorCode(1)
Exemplo n.º 24
0
def cli(
    ctx,
    environment,
    target,
    upload_port,
    project_dir,
    project_conf,
    jobs,
    silent,
    verbose,
    disable_auto_clean,
):
    app.set_session_var("custom_project_conf", project_conf)

    # find project directory on upper level
    if isfile(project_dir):
        project_dir = find_project_dir_above(project_dir)

    is_test_running = CTX_META_TEST_IS_RUNNING in ctx.meta

    with fs.cd(project_dir):
        config = ProjectConfig.get_instance(project_conf)
        config.validate(environment)

        # clean obsolete build dir
        if not disable_auto_clean:
            build_dir = config.get_optional_dir("build")
            try:
                clean_build_dir(build_dir, config)
            except:  # pylint: disable=bare-except
                click.secho(
                    "Can not remove temporary directory `%s`. Please remove "
                    "it manually to avoid build issues" % build_dir,
                    fg="yellow",
                )

        handle_legacy_libdeps(project_dir, config)

        default_envs = config.default_envs()
        results = []
        for env in config.envs():
            skipenv = any([
                environment and env not in environment,
                not environment and default_envs and env not in default_envs,
            ])
            if skipenv:
                results.append({"env": env})
                continue

            # print empty line between multi environment project
            if not silent and any(
                    r.get("succeeded") is not None for r in results):
                click.echo()

            results.append(
                process_env(
                    ctx,
                    env,
                    config,
                    environment,
                    target,
                    upload_port,
                    silent,
                    verbose,
                    jobs,
                    is_test_running,
                ))

        command_failed = any(r.get("succeeded") is False for r in results)

        if not is_test_running and (command_failed
                                    or not silent) and len(results) > 1:
            print_processing_summary(results)

        if command_failed:
            raise exception.ReturnErrorCode(1)
        return True