Skip to content

Commit 70533c5

Browse files
authored
Merge pull request #103 from github/jm-use-app-token-in-graphql-when-using-github-app
fix: use GitHub App token when authed with GitHub App
2 parents 50f0f0b + 888d006 commit 70533c5

File tree

6 files changed

+72
-10
lines changed

6 files changed

+72
-10
lines changed

.env-example

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
1-
GH_TOKEN = " "
2-
ORGANIZATION = "organization"
1+
BATCH_SIZE = ""
2+
BODY = ""
3+
COMMIT_MESSAGE = ""
4+
CREATED_AFTER_DATE = ""
5+
DRY_RUN = ""
6+
ENABLE_SECURITY_UPDATES = ""
7+
EXEMPT_ECOSYSTEMS = ""
8+
EXEMPT_REOPS = ""
9+
FILTER_VISIBILITY = ""
10+
GH_TOKEN = ""
11+
GROUP_DEPENDENCIES = ""
12+
ORGANIZATION = ""
13+
PROJECT_ID = ""
14+
REPOSITORY = ""
15+
TITLE = ""
16+
TYPE = ""
317

418
# GITHUB APP
519
GH_APP_ID = ""

auth.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""This is the module that contains functions related to authenticating to GitHub with a personal access token."""
22

33
import github3
4+
import requests
45

56

67
def auth_to_github(
@@ -41,3 +42,29 @@ def auth_to_github(
4142
if not github_connection:
4243
raise ValueError("Unable to authenticate to GitHub")
4344
return github_connection # type: ignore
45+
46+
47+
def get_github_app_installation_token(
48+
gh_app_id: str, gh_app_private_key_bytes: bytes, gh_app_installation_id: str
49+
) -> str | None:
50+
"""
51+
Get a GitHub App Installation token.
52+
53+
Args:
54+
gh_app_id (str): the GitHub App ID
55+
gh_app_private_key_bytes (bytes): the GitHub App Private Key
56+
gh_app_installation_id (str): the GitHub App Installation ID
57+
58+
Returns:
59+
str: the GitHub App token
60+
"""
61+
jwt_headers = github3.apps.create_jwt_headers(gh_app_private_key_bytes, gh_app_id)
62+
url = f"https://api.github.com/app/installations/{gh_app_installation_id}/access_tokens"
63+
64+
try:
65+
response = requests.post(url, headers=jwt_headers, json=None, timeout=5)
66+
response.raise_for_status()
67+
except requests.exceptions.RequestException as e:
68+
print(f"Request failed: {e}")
69+
return None
70+
return response.json().get("token")

env.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,7 @@ def get_int_env_var(env_var_name: str) -> int | None:
4343
return None
4444

4545

46-
def get_env_vars(
47-
test: bool = False,
48-
) -> tuple[
46+
def get_env_vars(test: bool = False) -> tuple[
4947
str | None,
5048
list[str],
5149
int | None,
@@ -95,7 +93,7 @@ def get_env_vars(
9593
exempt_ecosystems_list (list[str]): A list of package ecosystems to exempt from the action
9694
"""
9795
if not test:
98-
# Load from .env file if it exists
96+
# Load from .env file if it exists and not testing
9997
dotenv_path = join(dirname(__file__), ".env")
10098
load_dotenv(dotenv_path)
10199

evergreen.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ def main(): # pragma: no cover
4242
token, gh_app_id, gh_app_installation_id, gh_app_private_key, ghe
4343
)
4444

45+
if not token and gh_app_id and gh_app_installation_id and gh_app_private_key:
46+
token = auth.get_github_app_installation_token(
47+
gh_app_id, gh_app_private_key, gh_app_installation_id
48+
)
49+
4550
# If Project ID is set lookup the global project ID
4651
if project_id:
4752
# Check Organization is set as it is required for linking to a project

test_auth.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Test cases for the auth module."""
22

33
import unittest
4-
from unittest.mock import patch
4+
from unittest.mock import MagicMock, patch
55

66
import auth
77

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

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

48+
@patch("github3.apps.create_jwt_headers", MagicMock(return_value="gh_token"))
49+
@patch("requests.post")
50+
def test_get_github_app_installation_token(self, mock_post):
51+
"""
52+
Test the get_github_app_installation_token function.
53+
"""
54+
dummy_token = "dummytoken"
55+
mock_response = MagicMock()
56+
mock_response.raise_for_status.return_value = None
57+
mock_response.json.return_value = {"token": dummy_token}
58+
mock_post.return_value = mock_response
59+
60+
result = auth.get_github_app_installation_token(
61+
b"gh_private_token", "gh_app_id", "gh_installation_id"
62+
)
63+
64+
self.assertEqual(result, dummy_token)
65+
4866

4967
if __name__ == "__main__":
5068
unittest.main()

test_env.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ def setUp(self):
2020
"GH_APP_ID",
2121
"GH_APP_INSTALLATION_ID",
2222
"GH_APP_PRIVATE_KEY",
23-
"GH_TOKEN",
2423
"GH_ENTERPRISE_URL",
24+
"GH_TOKEN",
2525
"GROUP_DEPENDENCIES",
2626
"ORGANIZATION",
2727
"PROJECT_ID",

0 commit comments

Comments
 (0)