Skip to content

Commit 2517934

Browse files
committed
Fetch eval result comparison from GH workflow artifact
1 parent 0177104 commit 2517934

File tree

13 files changed

+7415
-20
lines changed

13 files changed

+7415
-20
lines changed

default.nix

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,26 @@ let
1111
withNom
1212
&& (builtins.tryEval (builtins.elem buildPlatform.system pkgs.ghc.meta.platforms)).value or false;
1313
in
14-
python3.pkgs.buildPythonApplication {
14+
python3Packages.buildPythonApplication {
1515
name = "nixpkgs-review";
1616
src = ./.;
1717
format = "pyproject";
18-
nativeBuildInputs = [ installShellFiles ] ++ lib.optional withAutocomplete python3.pkgs.argcomplete;
19-
propagatedBuildInputs = [ python3.pkgs.argcomplete ];
18+
nativeBuildInputs = [
19+
installShellFiles
20+
] ++ lib.optional withAutocomplete python3Packages.argcomplete;
21+
dependencies = with python3Packages; [
22+
argcomplete
23+
requests
24+
];
2025

2126
nativeCheckInputs =
2227
[
23-
python3.pkgs.setuptools
24-
python3.pkgs.pylint
28+
python3Packages.setuptools
29+
python3Packages.pylint
2530
glibcLocales
2631

2732
# needed for interactive unittests
28-
python3.pkgs.pytest
33+
python3Packages.pytest
2934
pkgs.nixVersions.stable or nix_2_4
3035
git
3136
]

nixpkgs_review/github.py

Lines changed: 91 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
import json
2+
import os
3+
import tempfile
24
import urllib.parse
35
import urllib.request
6+
import zipfile
47
from collections import defaultdict
58
from typing import Any
69

10+
import requests
11+
12+
from .utils import System
13+
714

815
def pr_url(pr: int) -> str:
916
return f"https://github.com/NixOS/nixpkgs/pull/{pr}"
@@ -12,20 +19,31 @@ def pr_url(pr: int) -> str:
1219
class GithubClient:
1320
def __init__(self, api_token: str | None) -> None:
1421
self.api_token = api_token
22+
self.headers: dict[str, str] = {
23+
"Content-Type": "application/json",
24+
"Accept": "application/vnd.github+json",
25+
}
26+
if self.api_token:
27+
self.headers["Authorization"] = f"token {self.api_token}"
1528

1629
def _request(
17-
self, path: str, method: str, data: dict[str, Any] | None = None
30+
self,
31+
path: str,
32+
method: str,
33+
data: dict[str, Any] | None = None,
1834
) -> Any:
1935
url = urllib.parse.urljoin("https://api.github.com/", path)
20-
headers = {"Content-Type": "application/json"}
21-
if self.api_token:
22-
headers["Authorization"] = f"token {self.api_token}"
2336

2437
body = None
2538
if data:
2639
body = json.dumps(data).encode("ascii")
2740

28-
req = urllib.request.Request(url, headers=headers, method=method, data=body)
41+
req = urllib.request.Request(
42+
url,
43+
headers=self.headers,
44+
method=method,
45+
data=body,
46+
)
2947
with urllib.request.urlopen(req) as resp:
3048
return json.loads(resp.read())
3149

@@ -69,8 +87,74 @@ def pull_request(self, number: int) -> Any:
6987
"Get a pull request"
7088
return self.get(f"repos/NixOS/nixpkgs/pulls/{number}")
7189

72-
def get_borg_eval_gist(self, pr: dict[str, Any]) -> dict[str, set[str]] | None:
73-
packages_per_system: defaultdict[str, set[str]] = defaultdict(set)
90+
def get_json_from_artifact(self, workflow_id: int, json_filename: str) -> Any:
91+
"""
92+
- Download a workflow artifact
93+
- Extract the archive
94+
- Open, deserialize and return a specific `json_filename` JSON file
95+
"""
96+
download_url: str = f"https://api.github.com/repos/NixOS/nixpkgs/actions/artifacts/{workflow_id}/zip"
97+
98+
print("-------URL", download_url)
99+
with requests.get(
100+
url=download_url,
101+
headers=self.headers,
102+
stream=True,
103+
) as resp:
104+
with tempfile.TemporaryDirectory() as temp_dir:
105+
# download zip file to disk
106+
with tempfile.NamedTemporaryFile(delete_on_close=False) as f:
107+
f.write(resp.content)
108+
f.close()
109+
110+
# Extract zip archive to temporary directory
111+
with zipfile.ZipFile(f.name, "r") as zip_ref:
112+
zip_ref.extractall(temp_dir)
113+
114+
with open(os.path.join(temp_dir, json_filename)) as f:
115+
return json.loads(f.read())
116+
117+
return None
118+
119+
def get_github_action_eval_result(
120+
self, pr: dict[str, Any]
121+
) -> dict[System, set[str]] | None:
122+
commit_sha: str = pr["head"]["sha"]
123+
124+
workflow_runs_resp: Any = self.get(
125+
f"repos/NixOS/nixpkgs/actions/runs?head_sha={commit_sha}"
126+
)
127+
if (
128+
not isinstance(workflow_runs_resp, dict)
129+
or "workflow_runs" not in workflow_runs_resp
130+
):
131+
return None
132+
133+
workflow_runs: list[Any] = workflow_runs_resp["workflow_runs"]
134+
135+
if not workflow_runs:
136+
return None
137+
138+
for workflow_run in workflow_runs:
139+
if workflow_run["name"] == "Eval":
140+
artifacts: list[Any] = self.get(
141+
workflow_run["artifacts_url"],
142+
)["artifacts"]
143+
144+
for artifact in artifacts:
145+
if artifact["name"] == "comparison":
146+
changed_paths: Any = self.get_json_from_artifact(
147+
workflow_id=artifact["id"],
148+
json_filename="changed-paths.json",
149+
)
150+
print("CHANGED PATHS", changed_paths)
151+
if changed_paths is not None:
152+
if "rebuildsByPlatform" in changed_paths:
153+
return changed_paths["rebuildsByPlatform"] # type: ignore
154+
return None
155+
156+
def get_borg_eval_gist(self, pr: dict[str, Any]) -> dict[System, set[str]] | None:
157+
packages_per_system: defaultdict[System, set[str]] = defaultdict(set)
74158
statuses = self.get(pr["statuses_url"])
75159
for status in statuses:
76160
if (

nixpkgs_review/review.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -277,9 +277,18 @@ def build(
277277
def build_pr(self, pr_number: int) -> dict[System, list[Attr]]:
278278
pr = self.github_client.pull_request(pr_number)
279279

280-
packages_per_system: dict[System, set[str]] | None
280+
packages_per_system: dict[System, set[str]] | None = None
281281
if self.use_ofborg_eval and all(system in PLATFORMS for system in self.systems):
282-
packages_per_system = self.github_client.get_borg_eval_gist(pr)
282+
# Attempt to fetch the GitHub actions evaluation result
283+
packages_per_system = self.github_client.get_github_action_eval_result(pr)
284+
from pprint import pprint
285+
286+
pprint(packages_per_system)
287+
288+
# If unsuccessfull, fallback to ofborg
289+
if packages_per_system is None:
290+
packages_per_system = self.github_client.get_borg_eval_gist(pr)
291+
283292
else:
284293
packages_per_system = None
285294

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"rebuildsByPlatform": {
3+
"x86_64-linux": ["pkg1"],
4+
"aarch64-linux": ["pkg1"],
5+
"x86_64-darwin": ["pkg1"],
6+
"aarch64-darwin": ["pkg1"]
7+
}
8+
}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
{
2+
"total_count": 7,
3+
"artifacts": [
4+
{
5+
"id": 2321218831,
6+
"node_id": "MDg6QXJ0aWZhY3QyMzIxMjE4ODMx",
7+
"name": "paths",
8+
"size_in_bytes": 521492,
9+
"url": "https://api.github.com/repos/NixOS/nixpkgs/actions/artifacts/2321218831",
10+
"archive_download_url": "https://api.github.com/repos/NixOS/nixpkgs/actions/artifacts/2321218831/zip",
11+
"expired": false,
12+
"created_at": "2024-12-14T15:00:05Z",
13+
"updated_at": "2024-12-14T15:00:05Z",
14+
"expires_at": "2025-03-14T14:58:40Z",
15+
"workflow_run": {
16+
"id": 12330828258,
17+
"repository_id": 4542716,
18+
"head_repository_id": 495348462,
19+
"head_branch": "equinox",
20+
"head_sha": "8409ac0f68974193d3705e3283e73d85c3688e60"
21+
}
22+
},
23+
{
24+
"id": 2321223435,
25+
"node_id": "MDg6QXJ0aWZhY3QyMzIxMjIzNDM1",
26+
"name": "intermediate-x86_64-darwin",
27+
"size_in_bytes": 3058155,
28+
"url": "https://api.github.com/repos/NixOS/nixpkgs/actions/artifacts/2321223435",
29+
"archive_download_url": "https://api.github.com/repos/NixOS/nixpkgs/actions/artifacts/2321223435/zip",
30+
"expired": false,
31+
"created_at": "2024-12-14T15:03:47Z",
32+
"updated_at": "2024-12-14T15:03:47Z",
33+
"expires_at": "2025-03-14T14:58:40Z",
34+
"workflow_run": {
35+
"id": 12330828258,
36+
"repository_id": 4542716,
37+
"head_repository_id": 495348462,
38+
"head_branch": "equinox",
39+
"head_sha": "8409ac0f68974193d3705e3283e73d85c3688e60"
40+
}
41+
},
42+
{
43+
"id": 2321223453,
44+
"node_id": "MDg6QXJ0aWZhY3QyMzIxMjIzNDUz",
45+
"name": "intermediate-aarch64-darwin",
46+
"size_in_bytes": 3053797,
47+
"url": "https://api.github.com/repos/NixOS/nixpkgs/actions/artifacts/2321223453",
48+
"archive_download_url": "https://api.github.com/repos/NixOS/nixpkgs/actions/artifacts/2321223453/zip",
49+
"expired": false,
50+
"created_at": "2024-12-14T15:03:48Z",
51+
"updated_at": "2024-12-14T15:03:48Z",
52+
"expires_at": "2025-03-14T14:58:40Z",
53+
"workflow_run": {
54+
"id": 12330828258,
55+
"repository_id": 4542716,
56+
"head_repository_id": 495348462,
57+
"head_branch": "equinox",
58+
"head_sha": "8409ac0f68974193d3705e3283e73d85c3688e60"
59+
}
60+
},
61+
{
62+
"id": 2321223692,
63+
"node_id": "MDg6QXJ0aWZhY3QyMzIxMjIzNjky",
64+
"name": "intermediate-aarch64-linux",
65+
"size_in_bytes": 3640673,
66+
"url": "https://api.github.com/repos/NixOS/nixpkgs/actions/artifacts/2321223692",
67+
"archive_download_url": "https://api.github.com/repos/NixOS/nixpkgs/actions/artifacts/2321223692/zip",
68+
"expired": false,
69+
"created_at": "2024-12-14T15:03:59Z",
70+
"updated_at": "2024-12-14T15:03:59Z",
71+
"expires_at": "2025-03-14T14:58:40Z",
72+
"workflow_run": {
73+
"id": 12330828258,
74+
"repository_id": 4542716,
75+
"head_repository_id": 495348462,
76+
"head_branch": "equinox",
77+
"head_sha": "8409ac0f68974193d3705e3283e73d85c3688e60"
78+
}
79+
},
80+
{
81+
"id": 2321225411,
82+
"node_id": "MDg6QXJ0aWZhY3QyMzIxMjI1NDEx",
83+
"name": "intermediate-x86_64-linux",
84+
"size_in_bytes": 3730877,
85+
"url": "https://api.github.com/repos/NixOS/nixpkgs/actions/artifacts/2321225411",
86+
"archive_download_url": "https://api.github.com/repos/NixOS/nixpkgs/actions/artifacts/2321225411/zip",
87+
"expired": false,
88+
"created_at": "2024-12-14T15:05:13Z",
89+
"updated_at": "2024-12-14T15:05:13Z",
90+
"expires_at": "2025-03-14T14:58:40Z",
91+
"workflow_run": {
92+
"id": 12330828258,
93+
"repository_id": 4542716,
94+
"head_repository_id": 495348462,
95+
"head_branch": "equinox",
96+
"head_sha": "8409ac0f68974193d3705e3283e73d85c3688e60"
97+
}
98+
},
99+
{
100+
"id": 2321227061,
101+
"node_id": "MDg6QXJ0aWZhY3QyMzIxMjI3MDYx",
102+
"name": "result",
103+
"size_in_bytes": 11396380,
104+
"url": "https://api.github.com/repos/NixOS/nixpkgs/actions/artifacts/2321227061",
105+
"archive_download_url": "https://api.github.com/repos/NixOS/nixpkgs/actions/artifacts/2321227061/zip",
106+
"expired": false,
107+
"created_at": "2024-12-14T15:06:14Z",
108+
"updated_at": "2024-12-14T15:06:14Z",
109+
"expires_at": "2025-03-14T14:58:40Z",
110+
"workflow_run": {
111+
"id": 12330828258,
112+
"repository_id": 4542716,
113+
"head_repository_id": 495348462,
114+
"head_branch": "equinox",
115+
"head_sha": "8409ac0f68974193d3705e3283e73d85c3688e60"
116+
}
117+
},
118+
{
119+
"id": 2321227177,
120+
"node_id": "MDg6QXJ0aWZhY3QyMzIxMjI3MTc3",
121+
"name": "comparison",
122+
"size_in_bytes": 787,
123+
"url": "https://api.github.com/repos/NixOS/nixpkgs/actions/artifacts/2321227177",
124+
"archive_download_url": "https://api.github.com/repos/NixOS/nixpkgs/actions/artifacts/2321227177/zip",
125+
"expired": false,
126+
"created_at": "2024-12-14T15:06:20Z",
127+
"updated_at": "2024-12-14T15:06:20Z",
128+
"expires_at": "2025-03-14T14:58:40Z",
129+
"workflow_run": {
130+
"id": 12330828258,
131+
"repository_id": 4542716,
132+
"head_repository_id": 495348462,
133+
"head_branch": "equinox",
134+
"head_sha": "8409ac0f68974193d3705e3283e73d85c3688e60"
135+
}
136+
}
137+
]
138+
}

0 commit comments

Comments
 (0)