diff --git a/.env-example b/.env-example index 9ee3a85..69fc2a4 100644 --- a/.env-example +++ b/.env-example @@ -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 = "" diff --git a/auth.py b/auth.py index bcce064..8ba8bcf 100644 --- a/auth.py +++ b/auth.py @@ -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( @@ -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") diff --git a/env.py b/env.py index 99f0c7c..9f01899 100644 --- a/env.py +++ b/env.py @@ -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, @@ -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) diff --git a/evergreen.py b/evergreen.py index f0859eb..017c199 100644 --- a/evergreen.py +++ b/evergreen.py @@ -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 diff --git a/test_auth.py b/test_auth.py index d859679..19efc06 100644 --- a/test_auth.py +++ b/test_auth.py @@ -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 @@ -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", @@ -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() diff --git a/test_env.py b/test_env.py index 40241bc..548a715 100644 --- a/test_env.py +++ b/test_env.py @@ -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",