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