blob: d251bc03914e008b234b257a261ecb91bea4188c [file] [log] [blame]
Harrison Mutai0e9964e2024-06-14 09:51:42 +01001#!/usr/bin/env python3
2
3import re
4from dataclasses import dataclass
5
6import requests
7
Boyan Karatotev1c5e5c92025-06-27 11:46:41 +01008# Constants to produce the report with
Boyan Karatotev12cd5902025-06-25 09:22:46 +01009openci_url = "https://ci.trustedfirmware.org/"
Boyan Karatotev1c5e5c92025-06-27 11:46:41 +010010job_names = ["tf-a-daily", "tf-a-tftf-main"]
Harrison Mutai0e9964e2024-06-14 09:51:42 +010011
Boyan Karatotev1c5e5c92025-06-27 11:46:41 +010012# Jenkins API helpers
13def get_job_url(job_name: str) -> str:
14 return openci_url + f"job/{job_name}/api/json"
Harrison Mutai0e9964e2024-06-14 09:51:42 +010015
Boyan Karatotev1c5e5c92025-06-27 11:46:41 +010016def get_build_url(job_name: str, build_number: str) -> str:
17 return openci_url + f"job/{job_name}/{build_number}"
18
19def get_build_api(build_url: str) -> str:
20 return build_url + "/api/json"
21
22def get_build_console(build_url: str) -> str:
23 return build_url + "/consoleText"
24
25"""Finds the latest run of a given job by name"""
Harrison Mutai0e9964e2024-06-14 09:51:42 +010026class Job:
Boyan Karatotev1c5e5c92025-06-27 11:46:41 +010027 def __init__(self, job_name: str) -> None:
28 req = requests.get(get_job_url(job_name)).json()
29 name = req["displayName"]
30 number = req["lastCompletedBuild"]["number"]
Harrison Mutai0e9964e2024-06-14 09:51:42 +010031
Boyan Karatotev1c5e5c92025-06-27 11:46:41 +010032 self.build = Build(name, name, number, level=0)
33 self.passed = self.build.passed
Harrison Mutai0e9964e2024-06-14 09:51:42 +010034
Boyan Karatotev1c5e5c92025-06-27 11:46:41 +010035 def print_build_status(self):
36 self.build.print_build_status()
Harrison Mutai0e9964e2024-06-14 09:51:42 +010037
Boyan Karatotev1c5e5c92025-06-27 11:46:41 +010038"""Represents an individual build. Will recursively fetch sub builds"""
39class Build:
40 def __init__(self, job_name: str, pretty_job_name: str, build_number: str, level: int) -> None:
41 self.url = get_build_url(job_name, build_number)
42 req = requests.get(get_build_api(self.url)).json()
43 self.passed = req["result"].lower() == "success"
44
45 self.name = pretty_job_name
46 # The full display name is "{job_name} {build_number}"
47 if self.name == "":
48 self.name = req["fullDisplayName"].split(" ")[0]
49 # and builds should show up with their configuration name
50 elif self.name == "tf-a-builder":
51 self.name = req["actions"][0]["parameters"][1]["value"]
52
53 self.level = level
54 self.number = build_number
55 self.sub_builds = []
56
57 # parent job passed => children passed. Skip
58 if not self.passed:
59 # the main jobs list sub builds nicely
60 self.sub_builds = [
61 # the gateways get an alias to differentiate them
62 Build(build["jobName"], build["jobAlias"], build["buildNumber"], level + 1)
63 for build in req.get("subBuilds", [])
64 ]
65 # gateways don't, since they determine them dynamically
66 if self.sub_builds == []:
67 self.sub_builds = [
68 Build(name, name, num, level + 1)
69 for name, num in self.get_builds_from_console_log()
70 ]
71
72 # extracts (child_name, child_number) from the console output of a build
73 def get_builds_from_console_log(self) -> str:
74 log = requests.get(get_build_console(self.url)).text
75
76 return re.findall(r"(tf-a[-\w+]+) #(\d+) started", log)
77
78 def print_build_status(self):
79 print(self)
80
81 for build in self.sub_builds:
82 if not build.passed:
83 build.print_build_status()
Harrison Mutai0e9964e2024-06-14 09:51:42 +010084
85 def __str__(self) -> str:
Boyan Karatotev1c5e5c92025-06-27 11:46:41 +010086 return (f"{' ' * self.level * 4}* {'✅' if self.passed else '❌'} "
87 f"*{self.name}* [#{self.number}]({self.url})"
88 )
Harrison Mutai0e9964e2024-06-14 09:51:42 +010089
90def main():
Boyan Karatotev1c5e5c92025-06-27 11:46:41 +010091 jobs = [Job(name) for name in job_names]
Harrison Mutai0e9964e2024-06-14 09:51:42 +010092
93 print("🟢" if all(j.passed for j in jobs) else "🔴", "Daily Status")
94
95 for j in jobs:
Boyan Karatotev1c5e5c92025-06-27 11:46:41 +010096 j.print_build_status()
Harrison Mutai0e9964e2024-06-14 09:51:42 +010097
98if __name__ == "__main__":
99 main()