Skip to content

Commit c567bd1

Browse files
authored
Merge pull request #396 from github/bypass-ratelimiter
feat: add bypass to rate limiter
2 parents 2a00d91 + 364ff44 commit c567bd1

File tree

5 files changed

+51
-5
lines changed

5 files changed

+51
-5
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ This action can be configured to authenticate with GitHub App Installation or Pe
144144
| field | required | default | description |
145145
| ----------------------------- | -------- | ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
146146
| `GH_ENTERPRISE_URL` | False | `""` | URL of GitHub Enterprise instance to use for auth instead of github.com |
147+
| `RATE_LIMIT_BYPASS` | False | `false` | If set to `true`, the rate limit will be bypassed. This is useful if being run on an local GitHub server with rate limiting disabled. |
147148
| `HIDE_AUTHOR` | False | False | If set to `true`, the author will not be displayed in the generated Markdown file. |
148149
| `HIDE_ITEMS_CLOSED_COUNT` | False | False | If set to `true`, the number of items closed metric will not be displayed in the generated Markdown file. |
149150
| `HIDE_LABEL_METRICS` | False | False | If set to `true`, the time in label metrics will not be displayed in the generated Markdown file. |

config.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class EnvVars:
4343
non_mentioning_links (bool): If set to TRUE, links do not cause a notification in the desitnation repository
4444
report_title (str): The title of the report
4545
output_file (str): The name of the file to write the report to
46+
rate_limit_bypass (bool): If set to TRUE, bypass the rate limit for the GitHub API
4647
"""
4748

4849
def __init__(
@@ -68,6 +69,7 @@ def __init__(
6869
non_mentioning_links: bool,
6970
report_title: str,
7071
output_file: str,
72+
rate_limit_bypass: bool = False,
7173
):
7274
self.gh_app_id = gh_app_id
7375
self.gh_app_installation_id = gh_app_installation_id
@@ -90,6 +92,7 @@ def __init__(
9092
self.non_mentioning_links = non_mentioning_links
9193
self.report_title = report_title
9294
self.output_file = output_file
95+
self.rate_limit_bypass = rate_limit_bypass
9396

9497
def __repr__(self):
9598
return (
@@ -115,6 +118,7 @@ def __repr__(self):
115118
f"{self.non_mentioning_links}"
116119
f"{self.report_title}"
117120
f"{self.output_file}"
121+
f"{self.rate_limit_bypass}"
118122
)
119123

120124

@@ -198,6 +202,7 @@ def get_env_vars(test: bool = False) -> EnvVars:
198202

199203
report_title = os.getenv("REPORT_TITLE", "Issue Metrics")
200204
output_file = os.getenv("OUTPUT_FILE", "")
205+
rate_limit_bypass = get_bool_env_var("RATE_LIMIT_BYPASS", False)
201206

202207
# Hidden columns
203208
hide_author = get_bool_env_var("HIDE_AUTHOR", False)
@@ -234,4 +239,5 @@ def get_env_vars(test: bool = False) -> EnvVars:
234239
non_mentioning_links,
235240
report_title,
236241
output_file,
242+
rate_limit_bypass,
237243
)

issue_metrics.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ def search_issues(
5050
search_query: str,
5151
github_connection: github3.GitHub,
5252
owners_and_repositories: List[dict],
53+
rate_limit_bypass: bool = False,
5354
) -> List[github3.search.IssueSearchResult]: # type: ignore
5455
"""
5556
Searches for issues/prs/discussions in a GitHub repository that match
@@ -66,7 +67,13 @@ def search_issues(
6667
"""
6768

6869
# Rate Limit Handling: API only allows 30 requests per minute
69-
def wait_for_api_refresh(iterator: github3.structs.SearchIterator):
70+
def wait_for_api_refresh(
71+
iterator: github3.structs.SearchIterator, rate_limit_bypass: bool = False
72+
):
73+
# If the rate limit bypass is enabled, don't wait for the API to refresh
74+
if rate_limit_bypass:
75+
return
76+
7077
max_retries = 5
7178
retry_count = 0
7279
sleep_time = 70
@@ -90,7 +97,7 @@ def wait_for_api_refresh(iterator: github3.structs.SearchIterator):
9097
issues_iterator = github_connection.search_issues(
9198
search_query, per_page=issues_per_page
9299
)
93-
wait_for_api_refresh(issues_iterator)
100+
wait_for_api_refresh(issues_iterator, rate_limit_bypass)
94101

95102
issues = []
96103
repos_and_owners_string = ""
@@ -107,7 +114,7 @@ def wait_for_api_refresh(iterator: github3.structs.SearchIterator):
107114

108115
# requests are sent once per page of issues
109116
if idx % issues_per_page == 0:
110-
wait_for_api_refresh(issues_iterator)
117+
wait_for_api_refresh(issues_iterator, rate_limit_bypass)
111118

112119
except github3.exceptions.ForbiddenError:
113120
print(
@@ -288,7 +295,7 @@ def get_owners_and_repositories(
288295
return results_list
289296

290297

291-
def main():
298+
def main(): # pragma: no cover
292299
"""Run the issue-metrics script.
293300
294301
This function authenticates to GitHub, searches for issues/prs/discussions
@@ -312,6 +319,7 @@ def main():
312319
non_mentioning_links = env_vars.non_mentioning_links
313320
report_title = env_vars.report_title
314321
output_file = env_vars.output_file
322+
rate_limit_bypass = env_vars.rate_limit_bypass
315323

316324
gh_app_id = env_vars.gh_app_id
317325
gh_app_installation_id = env_vars.gh_app_installation_id
@@ -363,7 +371,9 @@ def main():
363371
write_to_markdown(None, None, None, None, None, None, None, None)
364372
return
365373
else:
366-
issues = search_issues(search_query, github_connection, owners_and_repositories)
374+
issues = search_issues(
375+
search_query, github_connection, owners_and_repositories, rate_limit_bypass
376+
)
367377
if len(issues) <= 0:
368378
print("No issues found")
369379
write_to_markdown(None, None, None, None, None, None, None, None)

test_config.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ def setUp(self):
8383
"OUTPUT_FILE",
8484
"REPORT_TITLE",
8585
"SEARCH_QUERY",
86+
"RATE_LIMIT_BYPASS",
8687
]
8788
for key in env_keys:
8889
if key in os.environ:
@@ -108,6 +109,7 @@ def setUp(self):
108109
"OUTPUT_FILE": "",
109110
"REPORT_TITLE": "",
110111
"SEARCH_QUERY": SEARCH_QUERY,
112+
"RATE_LIMIT_BYPASS": "false",
111113
},
112114
clear=True,
113115
)
@@ -135,6 +137,7 @@ def test_get_env_vars_with_github_app(self):
135137
False,
136138
"",
137139
"",
140+
False,
138141
)
139142
result = get_env_vars(True)
140143
self.assertEqual(str(result), str(expected_result))

test_issue_metrics.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,32 @@ def test_search_issues_with_just_owner_or_org(self):
8888
issues = search_issues("is:open", mock_connection, owners)
8989
self.assertEqual(issues, mock_issues)
9090

91+
def test_search_issues_with_just_owner_or_org_with_bypass(self):
92+
"""Test that search_issues with just an owner/org returns the correct issues."""
93+
94+
# Set up the mock GitHub connection object
95+
mock_issues = [
96+
MagicMock(title="Issue 1"),
97+
MagicMock(title="Issue 2"),
98+
MagicMock(title="Issue 3"),
99+
]
100+
101+
# simulating github3.structs.SearchIterator return value
102+
mock_search_result = MagicMock()
103+
mock_search_result.__iter__.return_value = iter(mock_issues)
104+
mock_search_result.ratelimit_remaining = 30
105+
106+
mock_connection = MagicMock()
107+
mock_connection.search_issues.return_value = mock_search_result
108+
109+
# Call search_issues and check that it returns the correct issues
110+
org = {"owner": "org1"}
111+
owners = [org]
112+
issues = search_issues(
113+
"is:open", mock_connection, owners, rate_limit_bypass=True
114+
)
115+
self.assertEqual(issues, mock_issues)
116+
91117

92118
class TestGetOwnerAndRepository(unittest.TestCase):
93119
"""Unit tests for the get_owners_and_repositories function.

0 commit comments

Comments
 (0)