blob: 49e9bd0454dd58e29a5a2f6d6fd72c892a9d2e5b [file] [log] [blame]
#!/usr/bin/env python3
#
# Copyright 2018 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import argparse
import os
import sys
import yaml
import bazelci
from bazelci import BuildkiteException
# Buildkite has a max jobs limit at 2000, but that includes jobs already
# executed by the pipeline. Setting arbitrary lower number to make sure we fit
# under.
BUILDKITE_MAX_JOBS_LIMIT = 1500
BUILDKITE_ORG = os.environ["BUILDKITE_ORGANIZATION_SLUG"]
PIPELINE = "bazel-at-release-plus-incompatible-flags"
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_flags_from_command(command):
flags = {}
for entry in command.split(" "):
if entry.startswith("--") and "=" in entry:
key, _, value = entry[2:].partition("=")
flags[key] = value
return flags
def print_steps_for_failing_jobs(build_info):
failing_jobs = get_failing_jobs(build_info)
incompatible_flags = list(bazelci.fetch_incompatible_flags().keys())
pipeline_steps = []
counter = 0
for incompatible_flag in incompatible_flags:
for job in failing_jobs:
counter += 1
if counter > BUILDKITE_MAX_JOBS_LIMIT:
continue
label = "%s: %s" % (incompatible_flag, job["name"])
command = list(job["command"])
command[1] = command[1] + " --incompatible_flag=" + incompatible_flag
pipeline_steps.append(bazelci.create_step(label, command, job["platform"]))
if counter > BUILDKITE_MAX_JOBS_LIMIT:
bazelci.eprint(
"We only allow "
+ str(BUILDKITE_MAX_JOBS_LIMIT)
+ " jobs to be registered at once, skipping "
+ str(counter - BUILDKITE_MAX_JOBS_LIMIT)
+ " jobs."
)
print(yaml.dump({"steps": pipeline_steps}))
def main(argv=None):
if argv is None:
argv = sys.argv[1:]
parser = argparse.ArgumentParser(
description="Script for testing failing jobs with individual incompatible flag"
)
parser.add_argument("--build_number", type=str)
args = parser.parse_args(argv)
try:
if args.build_number:
client = bazelci.BuildkiteClient(org=BUILDKITE_ORG, pipeline=PIPELINE)
build_info = client.get_build_info(args.build_number)
print_steps_for_failing_jobs(build_info)
else:
parser.print_help()
return 2
except BuildkiteException as e:
bazelci.eprint(str(e))
return 1
return 0
if __name__ == "__main__":
sys.exit(main())