mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-11 18:01:14 +00:00
Log API Calls and the Rate Limit Left on Each macOS Admission Check
The calls per check grow with the number of active runs, and the token's hourly limit is shared by the whole repository, so each check now logs how many calls it made and what the last response says is left.
This commit is contained in:
@@ -131,6 +131,10 @@ class Api:
|
||||
def __init__(self, token, url="https://api.github.com"):
|
||||
self.token = token
|
||||
self.url = url.rstrip("/")
|
||||
# The token's hourly limit is shared by every workflow in the repository,
|
||||
# so what the last response says is left shows their use too.
|
||||
self.calls = 0
|
||||
self.remaining = self.limit = "?"
|
||||
|
||||
def get(self, path, **params):
|
||||
query = urllib.parse.urlencode(params)
|
||||
@@ -139,8 +143,22 @@ class Api:
|
||||
"Authorization": f"Bearer {self.token}",
|
||||
"X-GitHub-Api-Version": "2022-11-28",
|
||||
})
|
||||
with urllib.request.urlopen(request, timeout=30) as response:
|
||||
return json.load(response)
|
||||
self.calls += 1
|
||||
try:
|
||||
with urllib.request.urlopen(request, timeout=30) as response:
|
||||
self.note_rate_limit(response.headers)
|
||||
return json.load(response)
|
||||
except urllib.error.HTTPError as error:
|
||||
# A rate-limited request fails, and its headers say so.
|
||||
self.note_rate_limit(error.headers)
|
||||
raise
|
||||
|
||||
def note_rate_limit(self, headers):
|
||||
self.remaining = headers.get("X-RateLimit-Remaining", "?")
|
||||
self.limit = headers.get("X-RateLimit-Limit", "?")
|
||||
|
||||
def usage(self, calls_before):
|
||||
return f" API calls this check: {self.calls - calls_before}, rate limit left: {self.remaining} of {self.limit}"
|
||||
|
||||
|
||||
def list_runs(api, repo, workflow, **params):
|
||||
@@ -227,9 +245,19 @@ def main():
|
||||
api = Api(os.environ["GH_TOKEN"], os.environ.get("GITHUB_API_URL", "https://api.github.com"))
|
||||
run_id = int(os.environ["GITHUB_RUN_ID"])
|
||||
limit = int(os.environ.get("MACOS_RUNNER_LIMIT") or 5)
|
||||
|
||||
def measure_now():
|
||||
calls_before = api.calls
|
||||
try:
|
||||
total, lines = measure(api, repo, workflow, run_id, datetime.datetime.now(datetime.timezone.utc),
|
||||
priority=os.environ.get("PRIORITY") == "true")
|
||||
except Exception:
|
||||
print(api.usage(calls_before).strip())
|
||||
raise
|
||||
return total, lines + [api.usage(calls_before)]
|
||||
|
||||
reason = wait(
|
||||
lambda: measure(api, repo, workflow, run_id, datetime.datetime.now(datetime.timezone.utc),
|
||||
priority=os.environ.get("PRIORITY") == "true"),
|
||||
measure_now,
|
||||
limit,
|
||||
wait_minutes=int(os.environ.get("WAIT_MINUTES") or 240),
|
||||
poll_seconds=int(os.environ.get("POLL_SECONDS") or 180),
|
||||
|
||||
@@ -9,6 +9,7 @@ import os
|
||||
import sys
|
||||
import unittest
|
||||
import urllib.error
|
||||
from unittest import mock
|
||||
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
||||
|
||||
@@ -289,6 +290,41 @@ class MeasureTest(unittest.TestCase):
|
||||
self.assertFalse(any("event" in params for _, params in api.calls))
|
||||
|
||||
|
||||
class ApiTest(unittest.TestCase):
|
||||
class Response:
|
||||
def __init__(self, headers):
|
||||
self.headers = headers
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *_):
|
||||
return False
|
||||
|
||||
def read(self):
|
||||
return b"{}"
|
||||
|
||||
def test_reports_calls_and_the_rate_limit_left(self):
|
||||
api = admission.Api("token")
|
||||
responses = [self.Response({}),
|
||||
self.Response({"X-RateLimit-Remaining": "812", "X-RateLimit-Limit": "1000"})]
|
||||
with mock.patch.object(admission.urllib.request, "urlopen", side_effect=responses):
|
||||
api.get("first")
|
||||
calls_before = api.calls
|
||||
api.get("second")
|
||||
self.assertEqual(api.usage(0), " API calls this check: 2, rate limit left: 812 of 1000")
|
||||
self.assertEqual(api.usage(calls_before), " API calls this check: 1, rate limit left: 812 of 1000")
|
||||
|
||||
def test_reads_the_rate_limit_from_a_failed_request(self):
|
||||
api = admission.Api("token")
|
||||
headers = {"X-RateLimit-Remaining": "0", "X-RateLimit-Limit": "1000"}
|
||||
error = urllib.error.HTTPError("url", 403, "Forbidden", headers, None)
|
||||
with mock.patch.object(admission.urllib.request, "urlopen", side_effect=error), \
|
||||
self.assertRaises(urllib.error.HTTPError):
|
||||
api.get("runs")
|
||||
self.assertEqual(api.usage(0), " API calls this check: 1, rate limit left: 0 of 1000")
|
||||
|
||||
|
||||
class WaitTest(unittest.TestCase):
|
||||
def run_wait(self, results, limit=5, wait_minutes=60, poll_seconds=120):
|
||||
results = iter(results)
|
||||
|
||||
Reference in New Issue
Block a user