def get_failing_jobs(build_info):
    failing_jobs = []
    for job in build_info["jobs"]:
        if "state" in job and job["state"] == "failed":
            command = job["command"]
            if not command:
                bazelci.eprint("'command' field not found in the job: " +
                               str(job))
                continue
            # Skip if the job is not a runner job
            if command.find("bazelci.py runner") == -1:
                continue

            # Get rid of the incompatible flags in the command line because we are going to test them individually
            command_without_incompatible_flags = " ".join([
                i for i in command.split(" ")
                if not i.startswith("--incompatible_flag")
            ])

            # Recover the task name from job command
            flags = get_flags_from_command(command)
            task = flags.get("task")
            if not task:
                raise BuildkiteException(
                    "The following command has no --task argument: %s." %
                    command)

            # Fetch the original job config to retrieve the platform name.
            job_config = bazelci.load_config(
                http_url=flags.get("http_config"),
                file_config=flags.get("file_config"))

            # The config can either contain a "tasks" dict (new format) or a "platforms" dict (legacy format).
            all_tasks = job_config.get("tasks", job_config.get("platforms"))
            if not all_tasks:
                raise BuildkiteException(
                    "Malformed configuration: No 'tasks' or 'platforms' entry found."
                )

            task_config = all_tasks.get(task)
            if not task_config:
                raise BuildkiteException(
                    "Configuration does not contain an entry for task '%s'" %
                    task)

            # Shortcut: Users can skip the "platform" field if its value equals the task name.
            platform = task_config.get("platform") or task
            failing_jobs.append({
                "name":
                job["name"],
                "command":
                command_without_incompatible_flags.split("\n"),
                "platform":
                platform,
            })
    return failing_jobs
def get_failing_jobs(build_info):
    failing_jobs = []
    for job in build_info["jobs"]:
        if "state" in job and job["state"] == "failed":
            command = job["command"]
            # Skip if the job is not a runner job
            if command.find("bazelci.py runner") == -1:
                continue

            # Get rid of the incompatible flags in the command line because we are going to test them individually
            command_without_incompatible_flags = " ".join([
                i for i in command.split(" ")
                if not i.startswith("--incompatible_flag")
            ])

            # Recover the platform name from job command
            platform = None
            for s in command.split(" "):
                if s.startswith("--platform="):
                    platform = s[len("--platform="):]

            if not platform:
                raise BuildkiteException(
                    "Cannot recongnize platform from job command: %s" %
                    command)

            failing_jobs.append({
                "name":
                job["name"],
                "command":
                command_without_incompatible_flags.split("\n"),
                "platform":
                platform,
            })
    return failing_jobs
def main(argv=None):
    if argv is None:
        argv = sys.argv[1:]

    parser = argparse.ArgumentParser(description="Bazel Culprit Finder Script")

    subparsers = parser.add_subparsers(dest="subparsers_name")

    subparsers.add_parser("culprit_finder")

    runner = subparsers.add_parser("runner")
    runner.add_argument("--project_name", type=str)
    runner.add_argument("--platform_name", type=str)
    runner.add_argument("--good_bazel_commit", type=str)
    runner.add_argument("--bad_bazel_commit", type=str)
    runner.add_argument("--needs_clean", type=bool, nargs="?", const=True)

    args = parser.parse_args(argv)
    try:
        if args.subparsers_name == "culprit_finder":
            try:
                project_name = os.environ["PROJECT_NAME"]
                platform_name = os.environ["PLATFORM_NAME"]
                good_bazel_commit = os.environ["GOOD_BAZEL_COMMIT"]
                bad_bazel_commit = os.environ["BAD_BAZEL_COMMIT"]
            except KeyError as e:
                raise BuildkiteException("Environment variable %s must be set" % str(e))

            needs_clean = False
            if "NEEDS_CLEAN" in os.environ:
                needs_clean = True

            if project_name not in DOWNSTREAM_PROJECTS:
                raise BuildkiteException(
                    "Project name '%s' not recognized, available projects are %s"
                    % (project_name, str((DOWNSTREAM_PROJECTS.keys())))
                )

            if platform_name not in PLATFORMS:
                raise BuildkiteException(
                    "Platform name '%s' not recognized, available platforms are %s"
                    % (platform_name, str((PLATFORMS.keys())))
                )
            print_culprit_finder_pipeline(
                project_name=project_name,
                platform_name=platform_name,
                good_bazel_commit=good_bazel_commit,
                bad_bazel_commit=bad_bazel_commit,
                needs_clean=needs_clean,
            )
        elif args.subparsers_name == "runner":
            git_repo_location = clone_git_repository(args.project_name, args.platform_name)
            bazelci.print_collapsed_group("Check good bazel commit " + args.good_bazel_commit)
            if not test_with_bazel_at_commit(
                project_name=args.project_name,
                platform_name=args.platform_name,
                git_repo_location=git_repo_location,
                bazel_commit=args.good_bazel_commit,
                needs_clean=args.needs_clean,
            ):
                raise BuildkiteException(
                    "Given good commit (%s) is not actually good, abort bisecting."
                    % args.good_bazel_commit
                )
            start_bisecting(
                project_name=args.project_name,
                platform_name=args.platform_name,
                git_repo_location=git_repo_location,
                commits_list=get_bazel_commits_between(
                    args.good_bazel_commit, args.bad_bazel_commit
                ),
                needs_clean=args.needs_clean,
            )
        else:
            parser.print_help()
            return 2

    except BuildkiteException as e:
        bazelci.eprint(str(e))
        return 1
    return 0