From 03563ef6d24801ebfcc4438ba2b7f7ab37d2478d Mon Sep 17 00:00:00 2001 From: choldgraf Date: Mon, 15 Dec 2025 12:33:16 -0800 Subject: [PATCH] Add more helpful error messages --- github_activity/github_activity.py | 31 +++++++++++++++++++++++++++ github_activity/graphql.py | 34 ++++++++++++++++++++++++++---- tests/test_cli.py | 23 ++++++++++++++++++++ 3 files changed, 84 insertions(+), 4 deletions(-) diff --git a/github_activity/github_activity.py b/github_activity/github_activity.py index 40018c8..bccd67d 100644 --- a/github_activity/github_activity.py +++ b/github_activity/github_activity.py @@ -139,6 +139,7 @@ def get_activity( """ org, repo = _parse_target(target) + if repo: # We have org/repo search_query = f"repo:{org}/{repo}" @@ -192,6 +193,10 @@ def get_activity( "via the GitHub CLI (`gh auth login`)." ) + # Validate repository exists early if a specific repo was provided + if repo: + _validate_repository_exists(org, repo, auth) + # Figure out dates for our query since_dt, since_is_git_ref = _get_datetime_and_type(org, repo, since, auth) until_dt, until_is_git_ref = _get_datetime_and_type(org, repo, until, auth) @@ -862,6 +867,32 @@ def _parse_target(target): return org, repo +def _validate_repository_exists(org, repo, token): + """Validate that a repository exists on GitHub. + + Parameters + ---------- + org : str + The organization or user name + repo : str + The repository name + token : str + GitHub authentication token + + Raises + ------ + ValueError + If the repository does not exist or is not accessible + """ + auth = TokenAuth(token) + repo_url = f"https://api.github.com/repos/{org}/{repo}" + response = requests.head(repo_url, auth=auth) + if response.status_code == 404: + raise ValueError( + f"Repository '{org}/{repo}' not found. Please check the repository name." + ) + + def _get_datetime_and_type(org, repo, datetime_or_git_ref, auth): """Return a datetime object and bool indicating if it is a git reference or not.""" diff --git a/github_activity/graphql.py b/github_activity/graphql.py index bfe5312..4d853c4 100644 --- a/github_activity/graphql.py +++ b/github_activity/graphql.py @@ -195,16 +195,42 @@ def request(self, n_pages=100, n_per_page=50): auth=self.auth, ) if ii_request.status_code != 200: + # Check for common error cases and provide helpful messages + if ii_request.status_code == 403: + try: + error_data = ii_request.json() + error_message = error_data.get("message", "") + if "rate limit" in error_message.lower(): + raise Exception( + f"GitHub API rate limit exceeded. {error_message}\n" + "Please wait before making more requests, or use an authentication token with higher rate limits." + ) + else: + # Generic 403 - likely a permissions issue + raise Exception( + f"GitHub API access forbidden. {error_message}\n" + "This usually means your authentication token doesn't have the required permissions.\n" + "Please check that your token has the necessary scopes for this repository." + ) + except (ValueError, KeyError): + pass raise Exception( "Query failed to run by returning code of {}. {}".format( ii_request.status_code, ii_gql_query ) ) - if "errors" in ii_request.json().keys(): + errors = ii_request.json().get("errors") + if errors: + # Check for rate limit errors in GraphQL response + for error in errors: + if error.get("type") == "RATE_LIMITED": + error_message = error.get("message", "Rate limit exceeded") + raise Exception( + f"GitHub API rate limit exceeded. {error_message}\n" + "Please wait before making more requests, or use an authentication token with higher rate limits." + ) raise Exception( - "Query failed to run with error {}. {}".format( - ii_request.json()["errors"], ii_gql_query - ) + "Query failed to run with error {}. {}".format(errors, ii_gql_query) ) self.last_request = ii_request diff --git a/tests/test_cli.py b/tests/test_cli.py index 7efef3d..dc3d757 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -172,3 +172,26 @@ def test_bot_filtering(file_regression): # Use this regression test to make sure no bots are in the output file_regression.check(md, extension=".md") + + +def test_invalid_repository_error(): + """Test that invalid repository names produce clear error messages.""" + from github_activity.github_activity import get_activity + import pytest + + # Test with an invalid repository name + with pytest.raises(ValueError) as exc_info: + get_activity( + target="invalid-org/nonexistent-repo-12345", + since="2021-01-01", + until="2021-01-15", + ) + + # Verify the error message mentions the repository + error_message = str(exc_info.value) + assert "repository" in error_message.lower(), ( + f"Error should mention repository, got: {error_message}" + ) + assert "invalid-org/nonexistent-repo-12345" in error_message, ( + f"Error should include the repo name, got: {error_message}" + )