Skip to content

Commit

Permalink
Merge pull request #103 from github/jm-use-app-token-in-graphql-when-…
Browse files Browse the repository at this point in the history
…using-github-app

fix: use GitHub App token when authed with GitHub App
  • Loading branch information
jmeridth committed Apr 26, 2024
2 parents 50f0f0b + 888d006 commit 70533c5
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 10 deletions.
18 changes: 16 additions & 2 deletions .env-example
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
GH_TOKEN = " "
ORGANIZATION = "organization"
BATCH_SIZE = ""
BODY = ""
COMMIT_MESSAGE = ""
CREATED_AFTER_DATE = ""
DRY_RUN = ""
ENABLE_SECURITY_UPDATES = ""
EXEMPT_ECOSYSTEMS = ""
EXEMPT_REOPS = ""
FILTER_VISIBILITY = ""
GH_TOKEN = ""
GROUP_DEPENDENCIES = ""
ORGANIZATION = ""
PROJECT_ID = ""
REPOSITORY = ""
TITLE = ""
TYPE = ""

# GITHUB APP
GH_APP_ID = ""
Expand Down
27 changes: 27 additions & 0 deletions auth.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""This is the module that contains functions related to authenticating to GitHub with a personal access token."""

import github3
import requests


def auth_to_github(
Expand Down Expand Up @@ -41,3 +42,29 @@ def auth_to_github(
if not github_connection:
raise ValueError("Unable to authenticate to GitHub")
return github_connection # type: ignore


def get_github_app_installation_token(
gh_app_id: str, gh_app_private_key_bytes: bytes, gh_app_installation_id: str
) -> str | None:
"""
Get a GitHub App Installation token.
Args:
gh_app_id (str): the GitHub App ID
gh_app_private_key_bytes (bytes): the GitHub App Private Key
gh_app_installation_id (str): the GitHub App Installation ID
Returns:
str: the GitHub App token
"""
jwt_headers = github3.apps.create_jwt_headers(gh_app_private_key_bytes, gh_app_id)
url = f"https://api.github.com/app/installations/{gh_app_installation_id}/access_tokens"

try:
response = requests.post(url, headers=jwt_headers, json=None, timeout=5)
response.raise_for_status()
except requests.exceptions.RequestException as e:
print(f"Request failed: {e}")
return None
return response.json().get("token")
6 changes: 2 additions & 4 deletions env.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,7 @@ def get_int_env_var(env_var_name: str) -> int | None:
return None


def get_env_vars(
test: bool = False,
) -> tuple[
def get_env_vars(test: bool = False) -> tuple[
str | None,
list[str],
int | None,
Expand Down Expand Up @@ -95,7 +93,7 @@ def get_env_vars(
exempt_ecosystems_list (list[str]): A list of package ecosystems to exempt from the action
"""
if not test:
# Load from .env file if it exists
# Load from .env file if it exists and not testing
dotenv_path = join(dirname(__file__), ".env")
load_dotenv(dotenv_path)

Expand Down
5 changes: 5 additions & 0 deletions evergreen.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ def main(): # pragma: no cover
token, gh_app_id, gh_app_installation_id, gh_app_private_key, ghe
)

if not token and gh_app_id and gh_app_installation_id and gh_app_private_key:
token = auth.get_github_app_installation_token(
gh_app_id, gh_app_private_key, gh_app_installation_id
)

# If Project ID is set lookup the global project ID
if project_id:
# Check Organization is set as it is required for linking to a project
Expand Down
24 changes: 21 additions & 3 deletions test_auth.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Test cases for the auth module."""

import unittest
from unittest.mock import patch
from unittest.mock import MagicMock, patch

import auth

Expand All @@ -27,9 +27,9 @@ def test_auth_to_github_without_token(self):
Test the auth_to_github function when the token is not provided.
Expect a ValueError to be raised.
"""
with self.assertRaises(ValueError) as cm:
with self.assertRaises(ValueError) as context_manager:
auth.auth_to_github("", "", "", b"", "")
the_exception = cm.exception
the_exception = context_manager.exception
self.assertEqual(
str(the_exception),
"GH_TOKEN or the set of [GH_APP_ID, GH_APP_INSTALLATION_ID, GH_APP_PRIVATE_KEY] environment variables are not set",
Expand All @@ -45,6 +45,24 @@ def test_auth_to_github_with_ghe(self, mock_ghe):

self.assertEqual(result, "Authenticated to GitHub Enterprise")

@patch("github3.apps.create_jwt_headers", MagicMock(return_value="gh_token"))
@patch("requests.post")
def test_get_github_app_installation_token(self, mock_post):
"""
Test the get_github_app_installation_token function.
"""
dummy_token = "dummytoken"
mock_response = MagicMock()
mock_response.raise_for_status.return_value = None
mock_response.json.return_value = {"token": dummy_token}
mock_post.return_value = mock_response

result = auth.get_github_app_installation_token(
b"gh_private_token", "gh_app_id", "gh_installation_id"
)

self.assertEqual(result, dummy_token)


if __name__ == "__main__":
unittest.main()
2 changes: 1 addition & 1 deletion test_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ def setUp(self):
"GH_APP_ID",
"GH_APP_INSTALLATION_ID",
"GH_APP_PRIVATE_KEY",
"GH_TOKEN",
"GH_ENTERPRISE_URL",
"GH_TOKEN",
"GROUP_DEPENDENCIES",
"ORGANIZATION",
"PROJECT_ID",
Expand Down

0 comments on commit 70533c5

Please sign in to comment.