-
-
Notifications
You must be signed in to change notification settings - Fork 34
/
buildbotapi.py
134 lines (107 loc) · 4.17 KB
/
buildbotapi.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import asyncio
import json
from dataclasses import dataclass
from typing import Any, cast
from aiohttp.client import ClientSession
JSON = dict[str, Any]
@dataclass
class Builder:
builderid: int
description: str | None
masterids: list[int]
name: str
tags: list[str]
def __init__(self, **kwargs: Any) -> None:
self.__dict__.update(**kwargs)
def __hash__(self) -> int:
return hash(self.builderid)
@dataclass
class Build:
id: int
is_currently_failing: bool
builderid: int
builder: Builder | None
def __init__(self, **kwargs: Any) -> None:
self.__dict__.update(**kwargs)
self.id = int(kwargs.get("number", -1))
self.is_currently_failing = kwargs.get("currently_failing", False)
self.builder = None
def __eq__(self, other: object) -> bool:
return isinstance(other, Build) and self.id == other.id
def __hash__(self) -> int:
return hash(self.id)
class BuildBotAPI:
def __init__(self, session: ClientSession) -> None:
self._session = session
async def authenticate(self, token: str) -> None:
await self._session.get(
"https://buildbot.python.org/all/auth/login", params={"token": token}
)
async def _fetch_text(self, url: str) -> str:
async with self._session.get(url) as resp:
return await resp.text()
async def _fetch_json(self, url: str) -> JSON:
return cast(JSON, json.loads(await self._fetch_text(url)))
async def stable_builders(self, branch: str | None = None) -> dict[int, Builder]:
stable_builders = {
id: builder
for (id, builder) in (await self.all_builders(branch=branch)).items()
if "stable" in builder.tags
}
return stable_builders
async def all_builders(self, branch: str | None = None) -> dict[int, Builder]:
url = "https://buildbot.python.org/all/api/v2/builders"
if branch is not None:
url = f"{url}?tags__contains={branch}"
_builders: dict[str, Any] = await self._fetch_json(url)
builders = _builders["builders"]
all_builders = {
builder["builderid"]: Builder(**builder) for builder in builders
}
return all_builders
async def is_builder_failing_currently(self, builder: Builder) -> bool:
builds_: dict[str, Any] = await self._fetch_json(
f"https://buildbot.python.org/all/api/v2/builds?complete__eq=true"
f"&&builderid__eq={builder.builderid}&&order=-complete_at"
f"&&limit=1"
)
builds = builds_["builds"]
if not builds:
return False
(build,) = builds
if build["results"] == 2:
return True
return False
async def get_build(self, builder_id: int, build_id: int) -> Build:
data = await self._fetch_json(
f"https://buildbot.python.org/all/api/v2/builders/{builder_id}"
f"/builds/{build_id}"
)
(build_data,) = data["builds"]
build: Build = Build(**build_data)
build.builder = (await self.all_builders())[build.builderid]
build.is_currently_failing = await self.is_builder_failing_currently(
build.builder
)
return build
async def get_recent_failures(self, limit: int = 100) -> set[Build]:
data = await self._fetch_json(
f"https://buildbot.python.org/all/api/v2/builds?"
f"complete__eq=true&&results__eq=2&&"
f"order=-complete_at&&limit={limit}"
)
stable_builders = await self.stable_builders()
all_failures = {
Build(**build)
for build in data["builds"]
if build["builderid"] in stable_builders
}
for failure in all_failures:
failure.builder = stable_builders[failure.builderid]
async def _get_missing_info(failure: Build) -> None:
assert failure.builder is not None
failure.is_currently_failing = await self.is_builder_failing_currently(
failure.builder
)
await asyncio.gather(*[_get_missing_info(failure) for failure in all_failures])
return all_failures