Skip to content

Commit

Permalink
Fetch eval result comparison from GH workflow artifact
Browse files Browse the repository at this point in the history
  • Loading branch information
GaetanLepage committed Dec 14, 2024
1 parent 0177104 commit 62c96ff
Show file tree
Hide file tree
Showing 12 changed files with 7,400 additions and 18 deletions.
17 changes: 11 additions & 6 deletions default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,26 @@ let
withNom
&& (builtins.tryEval (builtins.elem buildPlatform.system pkgs.ghc.meta.platforms)).value or false;
in
python3.pkgs.buildPythonApplication {
python3Packages.buildPythonApplication {
name = "nixpkgs-review";
src = ./.;
format = "pyproject";
nativeBuildInputs = [ installShellFiles ] ++ lib.optional withAutocomplete python3.pkgs.argcomplete;
propagatedBuildInputs = [ python3.pkgs.argcomplete ];
nativeBuildInputs = [
installShellFiles
] ++ lib.optional withAutocomplete python3Packages.argcomplete;
dependencies = with python3Packages; [
argcomplete
requests
];

nativeCheckInputs =
[
python3.pkgs.setuptools
python3.pkgs.pylint
python3Packages.setuptools
python3Packages.pylint
glibcLocales

# needed for interactive unittests
python3.pkgs.pytest
python3Packages.pytest
pkgs.nixVersions.stable or nix_2_4
git
]
Expand Down
96 changes: 89 additions & 7 deletions nixpkgs_review/github.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import json
import os
import tempfile
import urllib.parse
import urllib.request
import zipfile
from collections import defaultdict
from typing import Any

import requests

from .utils import System


def pr_url(pr: int) -> str:
return f"https://github.com/NixOS/nixpkgs/pull/{pr}"
Expand All @@ -12,20 +19,31 @@ def pr_url(pr: int) -> str:
class GithubClient:
def __init__(self, api_token: str | None) -> None:
self.api_token = api_token
self.headers: dict[str, str] = {
"Content-Type": "application/json",
"Accept": "application/vnd.github+json",
}
if self.api_token:
self.headers["Authorization"] = f"token {self.api_token}"

def _request(
self, path: str, method: str, data: dict[str, Any] | None = None
self,
path: str,
method: str,
data: dict[str, Any] | None = None,
) -> Any:
url = urllib.parse.urljoin("https://api.github.com/", path)
headers = {"Content-Type": "application/json"}
if self.api_token:
headers["Authorization"] = f"token {self.api_token}"

body = None
if data:
body = json.dumps(data).encode("ascii")

req = urllib.request.Request(url, headers=headers, method=method, data=body)
req = urllib.request.Request(
url,
headers=self.headers,
method=method,
data=body,
)
with urllib.request.urlopen(req) as resp:
return json.loads(resp.read())

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

def get_borg_eval_gist(self, pr: dict[str, Any]) -> dict[str, set[str]] | None:
packages_per_system: defaultdict[str, set[str]] = defaultdict(set)
def get_json_from_artifact(self, workflow_id: int, json_filename: str) -> Any:
"""
- Download a workflow artifact
- Extract the archive
- Open, deserialize and return a specific `json_filename` JSON file
"""
download_url: str = f"https://api.github.com/repos/NixOS/nixpkgs/actions/artifacts/{workflow_id}/zip"

with requests.get(
url=download_url,
headers=self.headers,
stream=True,
) as resp:
with tempfile.TemporaryDirectory() as temp_dir:
# download zip file to disk
with tempfile.NamedTemporaryFile(delete_on_close=False) as f:
f.write(resp.content)
f.close()

# Extract zip archive to temporary directory
with zipfile.ZipFile(f.name, "r") as zip_ref:
zip_ref.extractall(temp_dir)

with open(os.path.join(temp_dir, json_filename)) as f:
return json.loads(f.read())

return None

def get_github_action_eval_result(
self, pr: dict[str, Any]
) -> dict[System, set[str]] | None:
commit_sha: str = pr["head"]["sha"]

workflow_runs_resp: Any = self.get(
f"repos/NixOS/nixpkgs/actions/runs?head_sha={commit_sha}"
)
if (
not isinstance(workflow_runs_resp, dict)
or "workflow_runs" not in workflow_runs_resp
):
return None

workflow_runs: list[Any] = workflow_runs_resp["workflow_runs"]

if not workflow_runs:
return None

for workflow_run in workflow_runs:
if workflow_run["name"] == "Eval":
artifacts: list[Any] = self.get(
workflow_run["artifacts_url"],
)["artifacts"]

for artifact in artifacts:
if artifact["name"] == "comparison":
changed_paths: Any = self.get_json_from_artifact(
workflow_id=artifact["id"],
json_filename="changed-paths.json",
)
if changed_paths is not None:
if "rebuildsByPlatform" in changed_paths:
return changed_paths["rebuildsByPlatform"] # type: ignore
return None

def get_borg_eval_gist(self, pr: dict[str, Any]) -> dict[System, set[str]] | None:
packages_per_system: defaultdict[System, set[str]] = defaultdict(set)
statuses = self.get(pr["statuses_url"])
for status in statuses:
if (
Expand Down
10 changes: 8 additions & 2 deletions nixpkgs_review/review.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,9 +277,15 @@ def build(
def build_pr(self, pr_number: int) -> dict[System, list[Attr]]:
pr = self.github_client.pull_request(pr_number)

packages_per_system: dict[System, set[str]] | None
packages_per_system: dict[System, set[str]] | None = None
if self.use_ofborg_eval and all(system in PLATFORMS for system in self.systems):
packages_per_system = self.github_client.get_borg_eval_gist(pr)
# Attempt to fetch the GitHub actions evaluation result
packages_per_system = self.github_client.get_github_action_eval_result(pr)

# If unsuccessfull, fallback to ofborg
if packages_per_system is None:
packages_per_system = self.github_client.get_borg_eval_gist(pr)

else:
packages_per_system = None

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"rebuildsByPlatform": {
"x86_64-linux": ["pkg1"],
"aarch64-linux": ["pkg1"],
"x86_64-darwin": ["pkg1"],
"aarch64-darwin": ["pkg1"]
}
}
138 changes: 138 additions & 0 deletions tests/assets/test_pr_github_action_eval/github-artifacts-363128.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
{
"total_count": 7,
"artifacts": [
{
"id": 2321218831,
"node_id": "MDg6QXJ0aWZhY3QyMzIxMjE4ODMx",
"name": "paths",
"size_in_bytes": 521492,
"url": "https://api.github.com/repos/NixOS/nixpkgs/actions/artifacts/2321218831",
"archive_download_url": "https://api.github.com/repos/NixOS/nixpkgs/actions/artifacts/2321218831/zip",
"expired": false,
"created_at": "2024-12-14T15:00:05Z",
"updated_at": "2024-12-14T15:00:05Z",
"expires_at": "2025-03-14T14:58:40Z",
"workflow_run": {
"id": 12330828258,
"repository_id": 4542716,
"head_repository_id": 495348462,
"head_branch": "equinox",
"head_sha": "8409ac0f68974193d3705e3283e73d85c3688e60"
}
},
{
"id": 2321223435,
"node_id": "MDg6QXJ0aWZhY3QyMzIxMjIzNDM1",
"name": "intermediate-x86_64-darwin",
"size_in_bytes": 3058155,
"url": "https://api.github.com/repos/NixOS/nixpkgs/actions/artifacts/2321223435",
"archive_download_url": "https://api.github.com/repos/NixOS/nixpkgs/actions/artifacts/2321223435/zip",
"expired": false,
"created_at": "2024-12-14T15:03:47Z",
"updated_at": "2024-12-14T15:03:47Z",
"expires_at": "2025-03-14T14:58:40Z",
"workflow_run": {
"id": 12330828258,
"repository_id": 4542716,
"head_repository_id": 495348462,
"head_branch": "equinox",
"head_sha": "8409ac0f68974193d3705e3283e73d85c3688e60"
}
},
{
"id": 2321223453,
"node_id": "MDg6QXJ0aWZhY3QyMzIxMjIzNDUz",
"name": "intermediate-aarch64-darwin",
"size_in_bytes": 3053797,
"url": "https://api.github.com/repos/NixOS/nixpkgs/actions/artifacts/2321223453",
"archive_download_url": "https://api.github.com/repos/NixOS/nixpkgs/actions/artifacts/2321223453/zip",
"expired": false,
"created_at": "2024-12-14T15:03:48Z",
"updated_at": "2024-12-14T15:03:48Z",
"expires_at": "2025-03-14T14:58:40Z",
"workflow_run": {
"id": 12330828258,
"repository_id": 4542716,
"head_repository_id": 495348462,
"head_branch": "equinox",
"head_sha": "8409ac0f68974193d3705e3283e73d85c3688e60"
}
},
{
"id": 2321223692,
"node_id": "MDg6QXJ0aWZhY3QyMzIxMjIzNjky",
"name": "intermediate-aarch64-linux",
"size_in_bytes": 3640673,
"url": "https://api.github.com/repos/NixOS/nixpkgs/actions/artifacts/2321223692",
"archive_download_url": "https://api.github.com/repos/NixOS/nixpkgs/actions/artifacts/2321223692/zip",
"expired": false,
"created_at": "2024-12-14T15:03:59Z",
"updated_at": "2024-12-14T15:03:59Z",
"expires_at": "2025-03-14T14:58:40Z",
"workflow_run": {
"id": 12330828258,
"repository_id": 4542716,
"head_repository_id": 495348462,
"head_branch": "equinox",
"head_sha": "8409ac0f68974193d3705e3283e73d85c3688e60"
}
},
{
"id": 2321225411,
"node_id": "MDg6QXJ0aWZhY3QyMzIxMjI1NDEx",
"name": "intermediate-x86_64-linux",
"size_in_bytes": 3730877,
"url": "https://api.github.com/repos/NixOS/nixpkgs/actions/artifacts/2321225411",
"archive_download_url": "https://api.github.com/repos/NixOS/nixpkgs/actions/artifacts/2321225411/zip",
"expired": false,
"created_at": "2024-12-14T15:05:13Z",
"updated_at": "2024-12-14T15:05:13Z",
"expires_at": "2025-03-14T14:58:40Z",
"workflow_run": {
"id": 12330828258,
"repository_id": 4542716,
"head_repository_id": 495348462,
"head_branch": "equinox",
"head_sha": "8409ac0f68974193d3705e3283e73d85c3688e60"
}
},
{
"id": 2321227061,
"node_id": "MDg6QXJ0aWZhY3QyMzIxMjI3MDYx",
"name": "result",
"size_in_bytes": 11396380,
"url": "https://api.github.com/repos/NixOS/nixpkgs/actions/artifacts/2321227061",
"archive_download_url": "https://api.github.com/repos/NixOS/nixpkgs/actions/artifacts/2321227061/zip",
"expired": false,
"created_at": "2024-12-14T15:06:14Z",
"updated_at": "2024-12-14T15:06:14Z",
"expires_at": "2025-03-14T14:58:40Z",
"workflow_run": {
"id": 12330828258,
"repository_id": 4542716,
"head_repository_id": 495348462,
"head_branch": "equinox",
"head_sha": "8409ac0f68974193d3705e3283e73d85c3688e60"
}
},
{
"id": 2321227177,
"node_id": "MDg6QXJ0aWZhY3QyMzIxMjI3MTc3",
"name": "comparison",
"size_in_bytes": 787,
"url": "https://api.github.com/repos/NixOS/nixpkgs/actions/artifacts/2321227177",
"archive_download_url": "https://api.github.com/repos/NixOS/nixpkgs/actions/artifacts/2321227177/zip",
"expired": false,
"created_at": "2024-12-14T15:06:20Z",
"updated_at": "2024-12-14T15:06:20Z",
"expires_at": "2025-03-14T14:58:40Z",
"workflow_run": {
"id": 12330828258,
"repository_id": 4542716,
"head_repository_id": 495348462,
"head_branch": "equinox",
"head_sha": "8409ac0f68974193d3705e3283e73d85c3688e60"
}
}
]
}
Loading

0 comments on commit 62c96ff

Please sign in to comment.