aboutsummaryrefslogtreecommitdiffstats
path: root/pydis_site
diff options
context:
space:
mode:
authorGravatar Hassan Abouelela <[email protected]>2022-07-24 08:32:43 +0200
committerGravatar Hassan Abouelela <[email protected]>2022-07-24 08:32:43 +0200
commit37001bca59c1d3d5fc8a8dadffda00d55fc9e0b6 (patch)
tree8af774a5084d30d8cb1ecd94d7e9e0083cdfec64 /pydis_site
parentMerge branch 'main' into github-artifacts (diff)
Use Dataclass For Workflow Run
Signed-off-by: Hassan Abouelela <[email protected]>
Diffstat (limited to 'pydis_site')
-rw-r--r--pydis_site/apps/api/github_utils.py41
-rw-r--r--pydis_site/apps/api/tests/test_github_utils.py50
2 files changed, 62 insertions, 29 deletions
diff --git a/pydis_site/apps/api/github_utils.py b/pydis_site/apps/api/github_utils.py
index 707b36e5..c4ace6b7 100644
--- a/pydis_site/apps/api/github_utils.py
+++ b/pydis_site/apps/api/github_utils.py
@@ -1,7 +1,8 @@
"""Utilities for working with the GitHub API."""
-
+import dataclasses
import datetime
import math
+import typing
import httpx
import jwt
@@ -50,6 +51,29 @@ class RunPendingError(ArtifactProcessingError):
status = 202
[email protected](frozen=True)
+class WorkflowRun:
+ """
+ A workflow run from the GitHub API.
+
+ https://docs.github.com/en/rest/actions/workflow-runs#get-a-workflow-run
+ """
+
+ name: str
+ head_sha: str
+ created_at: str
+ status: str
+ conclusion: str
+ artifacts_url: str
+
+ @classmethod
+ def from_raw(cls, data: dict[str, typing.Any]):
+ """Create an instance using the raw data from the API, discarding unused fields."""
+ return cls(**{
+ key.name: data[key.name] for key in dataclasses.fields(cls)
+ })
+
+
def generate_token() -> str:
"""
Generate a JWT token to access the GitHub API.
@@ -121,12 +145,12 @@ def authorize(owner: str, repo: str) -> httpx.Client:
raise e
-def check_run_status(run: dict) -> str:
+def check_run_status(run: WorkflowRun) -> str:
"""Check if the provided run has been completed, otherwise raise an exception."""
- created_at = datetime.datetime.strptime(run["created_at"], ISO_FORMAT_STRING)
+ created_at = datetime.datetime.strptime(run.created_at, ISO_FORMAT_STRING)
run_time = datetime.datetime.now() - created_at
- if run["status"] != "completed":
+ if run.status != "completed":
if run_time <= MAX_RUN_TIME:
raise RunPendingError(
f"The requested run is still pending. It was created "
@@ -135,12 +159,12 @@ def check_run_status(run: dict) -> str:
else:
raise RunTimeoutError("The requested workflow was not ready in time.")
- if run["conclusion"] != "success":
+ if run.conclusion != "success":
# The action failed, or did not run
- raise ActionFailedError(f"The requested workflow ended with: {run['conclusion']}")
+ raise ActionFailedError(f"The requested workflow ended with: {run.conclusion}")
# The requested action is ready
- return run["artifacts_url"]
+ return run.artifacts_url
def get_artifact(owner: str, repo: str, sha: str, action_name: str, artifact_name: str) -> str:
@@ -155,7 +179,8 @@ def get_artifact(owner: str, repo: str, sha: str, action_name: str, artifact_nam
# Filter the runs for the one associated with the given SHA
for run in runs["workflow_runs"]:
- if run["name"] == action_name and sha == run["head_sha"]:
+ run = WorkflowRun.from_raw(run)
+ if run.name == action_name and sha == run.head_sha:
break
else:
raise NotFoundError(
diff --git a/pydis_site/apps/api/tests/test_github_utils.py b/pydis_site/apps/api/tests/test_github_utils.py
index a9eab9a5..f5e072a9 100644
--- a/pydis_site/apps/api/tests/test_github_utils.py
+++ b/pydis_site/apps/api/tests/test_github_utils.py
@@ -1,4 +1,6 @@
+import dataclasses
import datetime
+import typing
import unittest
from unittest import mock
@@ -42,45 +44,46 @@ class GeneralUtilityTests(unittest.TestCase):
class CheckRunTests(unittest.TestCase):
"""Tests the check_run_status utility."""
+ run_kwargs: typing.Mapping = {
+ "name": "run_name",
+ "head_sha": "sha",
+ "status": "completed",
+ "conclusion": "success",
+ "created_at": datetime.datetime.now().strftime(github_utils.ISO_FORMAT_STRING),
+ "artifacts_url": "url",
+ }
+
def test_completed_run(self):
"""Test that an already completed run returns the correct URL."""
final_url = "some_url_string_1234"
- result = github_utils.check_run_status({
- "status": "completed",
- "conclusion": "success",
- "created_at": datetime.datetime.now().strftime(github_utils.ISO_FORMAT_STRING),
- "artifacts_url": final_url,
- })
+ kwargs = dict(self.run_kwargs, artifacts_url=final_url)
+ result = github_utils.check_run_status(github_utils.WorkflowRun(**kwargs))
self.assertEqual(final_url, result)
def test_pending_run(self):
"""Test that a pending run raises the proper exception."""
+ kwargs = dict(self.run_kwargs, status="pending")
with self.assertRaises(github_utils.RunPendingError):
- github_utils.check_run_status({
- "status": "pending",
- "created_at": datetime.datetime.now().strftime(github_utils.ISO_FORMAT_STRING),
- })
+ github_utils.check_run_status(github_utils.WorkflowRun(**kwargs))
def test_timeout_error(self):
"""Test that a timeout is declared after a certain duration."""
+ kwargs = dict(self.run_kwargs, status="pending")
# Set the creation time to well before the MAX_RUN_TIME
# to guarantee the right conclusion
- created = (
+ kwargs["created_at"] = (
datetime.datetime.now() - github_utils.MAX_RUN_TIME - datetime.timedelta(minutes=10)
).strftime(github_utils.ISO_FORMAT_STRING)
with self.assertRaises(github_utils.RunTimeoutError):
- github_utils.check_run_status({"status": "pending", "created_at": created})
+ github_utils.check_run_status(github_utils.WorkflowRun(**kwargs))
def test_failed_run(self):
"""Test that a failed run raises the proper exception."""
+ kwargs = dict(self.run_kwargs, conclusion="failed")
with self.assertRaises(github_utils.ActionFailedError):
- github_utils.check_run_status({
- "status": "completed",
- "conclusion": "failed",
- "created_at": datetime.datetime.now().strftime(github_utils.ISO_FORMAT_STRING),
- })
+ github_utils.check_run_status(github_utils.WorkflowRun(**kwargs))
def get_response_authorize(_: httpx.Client, request: httpx.Request, **__) -> httpx.Response:
@@ -172,11 +175,16 @@ class ArtifactFetcherTests(unittest.TestCase):
if request.method == "GET":
if path == "/repos/owner/repo/actions/runs":
+ run = github_utils.WorkflowRun(
+ name="action_name",
+ head_sha="action_sha",
+ created_at=datetime.datetime.now().strftime(github_utils.ISO_FORMAT_STRING),
+ status="completed",
+ conclusion="success",
+ artifacts_url="artifacts_url"
+ )
return httpx.Response(
- 200, request=request, json={"workflow_runs": [{
- "name": "action_name",
- "head_sha": "action_sha"
- }]}
+ 200, request=request, json={"workflow_runs": [dataclasses.asdict(run)]}
)
elif path == "/artifact_url":
return httpx.Response(