xref: /aosp_15_r20/external/mesa3d/bin/ci/nightly_compare.py (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1#!/usr/bin/env python3
2# Copyright © 2020 - 2024 Collabora Ltd.
3# Authors:
4#   David Heidelberg <[email protected]>
5#   Sergi Blanch Torne <[email protected]>
6# SPDX-License-Identifier: MIT
7
8"""
9Compare the two latest scheduled pipelines and provide information
10about the jobs you're interested in.
11"""
12
13import argparse
14import csv
15import re
16import requests
17import io
18from tabulate import tabulate
19
20import gitlab
21from colorama import Fore, Style
22from gitlab_common import read_token
23
24
25MARGE_BOT_USER_ID = 9716
26
27def print_failures_csv(id):
28    url = 'https://gitlab.freedesktop.org/mesa/mesa/-/jobs/' + str(id) + '/artifacts/raw/results/failures.csv'
29    missing: int = 0
30    MAX_MISS: int = 20
31    try:
32        response = requests.get(url)
33        response.raise_for_status()
34        csv_content = io.StringIO(response.text)
35        csv_reader = csv.reader(csv_content)
36        data = list(csv_reader)
37
38        for line in data[:]:
39            if line[1] == "UnexpectedImprovement(Pass)":
40                line[1] = Fore.GREEN + line[1] + Style.RESET_ALL
41            elif line[1] == "UnexpectedImprovement(Fail)":
42                line[1] = Fore.YELLOW + line[1] + Style.RESET_ALL
43            elif line[1] == "Crash" or line[1] == "Fail":
44                line[1] = Fore.RED + line[1] + Style.RESET_ALL
45            elif line[1] == "Missing":
46                if missing > MAX_MISS:
47                    data.remove(line)
48                    continue
49                missing += 1
50                line[1] = Fore.YELLOW + line[1] + Style.RESET_ALL
51            elif line[1] == "Fail":
52                line[1] = Fore.RED + line[1] + Style.RESET_ALL
53            else:
54                line[1] = Fore.WHITE + line[1] + Style.RESET_ALL
55
56        if missing > MAX_MISS:
57            data.append([Fore.RED + f"... more than {MAX_MISS} missing tests, something crashed?", "Missing" + Style.RESET_ALL])
58        headers = ["Test                                                                           ", "Result"]
59        print(tabulate(data, headers, tablefmt="plain"))
60    except Exception:
61        pass
62
63
64def job_failed_before(old_jobs, job):
65    for old_job in old_jobs:
66        if job.name == old_job.name:
67            return old_job
68
69
70def parse_args() -> None:
71    """Parse args"""
72    parser = argparse.ArgumentParser(
73        description="Tool to show merge requests assigned to the marge-bot",
74    )
75    parser.add_argument(
76        "--target",
77        metavar="target-job",
78        help="Target job regex. For multiple targets, pass multiple values, "
79        "eg. `--target foo bar`.",
80        required=False,
81        nargs=argparse.ONE_OR_MORE,
82    )
83    parser.add_argument(
84        "--token",
85        metavar="token",
86        help="force GitLab token, otherwise it's read from ~/.config/gitlab-token",
87    )
88    return parser.parse_args()
89
90
91if __name__ == "__main__":
92    args = parse_args()
93    token = read_token(args.token)
94    gl = gitlab.Gitlab(url="https://gitlab.freedesktop.org", private_token=token)
95
96    project = gl.projects.get("mesa/mesa")
97
98    print(
99        "\u001b]8;;https://gitlab.freedesktop.org/mesa/mesa/-/pipelines?page=1&scope=all&source=schedule\u001b\\Scheduled pipelines overview\u001b]8;;\u001b\\"
100    )
101    pipelines = project.pipelines.list(
102        source="schedule", ordered_by="created_at", sort="desc", page=1, per_page=2
103    )
104    print(
105        f"Old pipeline: {pipelines[1].created_at}\t\u001b]8;;{pipelines[1].web_url}\u001b\\{pipelines[1].status}\u001b]8;;\u001b\\\t{pipelines[1].sha}"
106    )
107    print(
108        f"New pipeline: {pipelines[0].created_at}\t\u001b]8;;{pipelines[0].web_url}\u001b\\{pipelines[0].status}\u001b]8;;\u001b\\\t{pipelines[0].sha}"
109    )
110    print(
111        f"\nWebUI visual compare: https://gitlab.freedesktop.org/mesa/mesa/-/compare/{pipelines[1].sha}...{pipelines[0].sha}\n"
112    )
113
114    # regex part
115    if args.target:
116        target = "|".join(args.target)
117        target = target.strip()
118        print("�� jobs: " + Fore.BLUE + target + Style.RESET_ALL)
119
120        target = f"({target})" + r"( \d+/\d+)?"
121    else:
122        target = ".*"
123
124    target_jobs_regex: re.Pattern = re.compile(target)
125
126    old_failed_jobs = []
127    for job in pipelines[1].jobs.list(all=True):
128        if (
129            job.status != "failed"
130            or target_jobs_regex
131            and not target_jobs_regex.fullmatch(job.name)
132        ):
133            continue
134        old_failed_jobs.append(job)
135
136    job_failed = False
137    for job in pipelines[0].jobs.list(all=True):
138        if (
139            job.status != "failed"
140            or target_jobs_regex
141            and not target_jobs_regex.fullmatch(job.name)
142        ):
143            continue
144
145        job_failed = True
146
147        previously_failed_job = job_failed_before(old_failed_jobs, job)
148        if previously_failed_job:
149            print(
150                Fore.YELLOW
151                + f":: \u001b]8;;{job.web_url}\u001b\\{job.name}\u001b]8;;\u001b\\"
152                + Fore.MAGENTA
153                + f" \u001b]8;;{previously_failed_job.web_url}\u001b\\(previous run)\u001b]8;;\u001b\\"
154                + Style.RESET_ALL
155            )
156        else:
157            print(
158                Fore.RED
159                + f":: \u001b]8;;{job.web_url}\u001b\\{job.name}\u001b]8;;\u001b\\"
160                + Style.RESET_ALL
161            )
162        print_failures_csv(job.id)
163
164    if not job_failed:
165        exit(0)
166
167    print("Commits between nightly pipelines:")
168    commit = project.commits.get(pipelines[0].sha)
169    while True:
170        print(
171            f"{commit.id}  \u001b]8;;{commit.web_url}\u001b\\{commit.title}\u001b]8;;\u001b\\"
172        )
173        if commit.id == pipelines[1].sha:
174            break
175        commit = project.commits.get(commit.parent_ids[0])
176