bot/code_coverage_bot/taskcluster.py (103 lines of code) (raw):

# -*- coding: utf-8 -*- import os import structlog import taskcluster from taskcluster.helper import TaskclusterConfig from code_coverage_bot.utils import download_file logger = structlog.getLogger(__name__) taskcluster_config = TaskclusterConfig("https://firefox-ci-tc.services.mozilla.com") FINISHED_STATUSES = ["completed", "failed", "exception"] ALL_STATUSES = FINISHED_STATUSES + ["unscheduled", "pending", "running"] NAME_PARTS_TO_SKIP = ("opt", "debug", "e10s", "1proc") def get_decision_task(branch, revision): route = f"gecko.v2.{branch}.revision.{revision}.taskgraph.decision" index = taskcluster_config.get_service("index") try: return index.findTask(route)["taskId"] except taskcluster.exceptions.TaskclusterRestFailure as e: if e.status_code == 404: return None raise def get_task_details(task_id): queue = taskcluster_config.get_service("queue") return queue.task(task_id) def get_task_status(task_id): queue = taskcluster_config.get_service("queue") return queue.status(task_id) def get_task_artifacts(task_id): queue = taskcluster_config.get_service("queue") return queue.listLatestArtifacts(task_id)["artifacts"] def get_tasks_in_group(group_id): queue = taskcluster_config.get_service("queue") token = None while True: query = {"limit": 200} if token is not None: query["continuationToken"] = token response = queue.listTaskGroup(group_id, query=query) yield from response["tasks"] token = response.get("continuationToken") if token is None: break def download_artifact(artifact_path: str, task_id: str, artifact_name: str) -> None: if os.path.exists(artifact_path): return # Build artifact public url # Use un-authenticated Taskcluster client to avoid taskcluster-proxy rewrite issue # https://github.com/taskcluster/taskcluster-proxy/issues/44 queue = taskcluster.Queue({"rootUrl": "https://firefox-ci-tc.services.mozilla.com"}) url = queue.buildUrl("getLatestArtifact", task_id, artifact_name) logger.debug("Downloading artifact", url=url) download_file(url, artifact_path) def is_coverage_task(task): return "ccov" in task["metadata"]["name"].split("/")[0].split("-") def name_to_chunk(name: str): """ Helper to convert a task name to a chunk Used by chunk mapping """ # Some tests are run on build machines, we define placeholder chunks for those. if name.startswith("build-signing-"): return "build-signing" elif name.startswith("build-"): return "build" name = name.split("/")[1] return "-".join(p for p in name.split("-") if p not in NAME_PARTS_TO_SKIP) def chunk_to_suite(chunk: str): """ Helper to convert a chunk to a suite (no numbers) Used by chunk mapping """ return "-".join(p for p in chunk.split("-") if not p.isdigit()) def get_chunk(task): """ Build clean chunk name from a Taskcluster task """ suite = get_suite(task) chunks = task["extra"].get("chunks", {}) if "current" in chunks: return f'{suite}-{chunks["current"]}' return suite def get_suite(task): """ Build clean suite name from a Taskcluster task """ assert isinstance(task, dict) tags = task["tags"] extra = task["extra"] if tags.get("kind") == "build": return "build" elif tags.get("kind") == "build-signing": return "build-signing" elif tags.get("kind") == "source-test": return "source-test" elif tags.get("kind") == "fuzzing": return "fuzzing" elif "suite" in extra: if isinstance(extra["suite"], dict): return extra["suite"]["name"] return extra["suite"] else: return tags.get("test-type") raise Exception(f"Unknown chunk for {task}") def get_platform(task): """ Build clean platform from a Taskcluster task """ assert isinstance(task, dict) tags = task.get("tags", {}) platform = tags.get("os") # Fallback on parsing the task name for signing tasks, as they don't have "os" in their tags. name = task.get("metadata", {}).get("name", "") if not platform and "signing" in name: name = name.split("/")[0] if "linux" in name: platform = "linux" if "win" in name: assert platform is None platform = "windows" if "mac" in name: assert platform is None platform = "macosx" if not platform: raise Exception(f"Unknown platform for {task}") # Weird case for android build on Linux docker if platform == "linux" and tags.get("android-stuff"): return "android" return platform