Skip to content

Commit

Permalink
Merge pull request #155 from ricardojdsilva87/feat/support-github-ent…
Browse files Browse the repository at this point in the history
…erprise-api

feat: support github enterprise api
  • Loading branch information
jmeridth authored Nov 1, 2024
2 parents ca34478 + e2d2cd3 commit 33fb3a9
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 38 deletions.
1 change: 1 addition & 0 deletions .env-example
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ REPOSITORY = "" # comma separated list of repositories in the format org/repo
GH_APP_ID = ""
GH_INSTALLATION_ID = ""
GH_PRIVATE_KEY = ""
GITHUB_APP_ENTERPRISE_ONLY = ""

# OPTIONAL SETTINGS
BODY = ""
Expand Down
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,12 @@ This action can be configured to authenticate with GitHub App Installation or Pe

##### GitHub App Installation

| field | required | default | description |
| ------------------------ | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GH_APP_ID` | True | `""` | GitHub Application ID. See [documentation](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/about-authentication-with-a-github-app) for more details. |
| `GH_APP_INSTALLATION_ID` | True | `""` | GitHub Application Installation ID. See [documentation](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/about-authentication-with-a-github-app) for more details. |
| `GH_APP_PRIVATE_KEY` | True | `""` | GitHub Application Private Key. See [documentation](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/about-authentication-with-a-github-app) for more details. |
| field | required | default | description |
| ---------------------------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GH_APP_ID` | True | `""` | GitHub Application ID. See [documentation](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/about-authentication-with-a-github-app) for more details. |
| `GH_APP_INSTALLATION_ID` | True | `""` | GitHub Application Installation ID. See [documentation](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/about-authentication-with-a-github-app) for more details. |
| `GH_APP_PRIVATE_KEY` | True | `""` | GitHub Application Private Key. See [documentation](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/about-authentication-with-a-github-app) for more details. |
| `GITHUB_APP_ENTERPRISE_ONLY` | False | `false` | Set this input to `true` if your app is created in GHE and communicates with GHE. |

##### Personal Access Token (PAT)

Expand Down
22 changes: 13 additions & 9 deletions auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,38 @@


def auth_to_github(
gh_app_id: str,
gh_app_installation_id: int,
gh_app_private_key_bytes: bytes,
token: str,
gh_app_id: int | None,
gh_app_installation_id: int | None,
gh_app_private_key_bytes: bytes,
ghe: str,
gh_app_enterprise_only: bool,
) -> github3.GitHub:
"""
Connect to GitHub.com or GitHub Enterprise, depending on env variables.
Args:
gh_app_id (str): the GitHub App ID
gh_installation_id (int): the GitHub App Installation ID
gh_app_private_key (bytes): the GitHub App Private Key
token (str): the GitHub personal access token
gh_app_id (int | None): the GitHub App ID
gh_app_installation_id (int | None): the GitHub App Installation ID
gh_app_private_key_bytes (bytes): the GitHub App Private Key
ghe (str): the GitHub Enterprise URL
gh_app_enterprise_only (bool): Set this to true if the GH APP is created on GHE and needs to communicate with GHE api only
Returns:
github3.GitHub: the GitHub connection object
"""

if gh_app_id and gh_app_private_key_bytes and gh_app_installation_id:
gh = github3.github.GitHub()
if ghe and gh_app_enterprise_only:
gh = github3.github.GitHubEnterprise(url=ghe)
else:
gh = github3.github.GitHub()
gh.login_as_app_installation(
gh_app_private_key_bytes, gh_app_id, gh_app_installation_id
)
github_connection = gh
elif ghe and token:
github_connection = github3.github.GitHubEnterprise(ghe, token=token)
github_connection = github3.github.GitHubEnterprise(url=ghe, token=token)
elif token:
github_connection = github3.login(token=token)
else:
Expand Down
15 changes: 11 additions & 4 deletions cleanowners.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def main(): # pragma: no cover
gh_app_id,
gh_app_installation_id,
gh_app_private_key_bytes,
gh_app_enterprise_only,
token,
ghe,
exempt_repositories_list,
Expand All @@ -39,8 +40,14 @@ def main(): # pragma: no cover

# Auth to GitHub.com or GHE
github_connection = auth.auth_to_github(
gh_app_id, gh_app_installation_id, gh_app_private_key_bytes, token, ghe
token,
gh_app_id,
gh_app_installation_id,
gh_app_private_key_bytes,
ghe,
gh_app_enterprise_only,
)

pull_count = 0
eligble_for_pr_count = 0
no_codeowners_count = 0
Expand Down Expand Up @@ -240,12 +247,12 @@ def commit_changes(
commit_message,
codeowners_filepath,
):
"""Commit the changes to the repo and open a pull reques and return the pull request object"""
"""Commit the changes to the repo and open a pull request and return the pull request object"""
default_branch = repo.default_branch
# Get latest commit sha from default branch
default_branch_commit = repo.ref("heads/" + default_branch).object.sha
default_branch_commit = repo.ref(f"heads/{default_branch}").object.sha
front_matter = "refs/heads/"
branch_name = "codeowners-" + str(uuid.uuid4())
branch_name = f"codeowners-{str(uuid.uuid4())}"
repo.create_ref(front_matter + branch_name, default_branch_commit)
repo.file_contents(codeowners_filepath).update(
message=commit_message,
Expand Down
4 changes: 4 additions & 0 deletions env.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ def get_env_vars(
int | None,
int | None,
bytes,
bool,
str | None,
str,
list[str],
Expand All @@ -71,6 +72,7 @@ def get_env_vars(
gh_app_id (int | None): The GitHub App ID to use for authentication
gh_app_installation_id (int | None): The GitHub App Installation ID to use for authentication
gh_app_private_key_bytes (bytes): The GitHub App Private Key as bytes to use for authentication
gh_app_enterprise_only (bool): Set this to true if the GH APP is created on GHE and needs to communicate with GHE api only
token (str | None): The GitHub token to use for authentication
ghe (str): The GitHub Enterprise URL to use for authentication
exempt_repositories_list (list[str]): A list of repositories to exempt from the action
Expand Down Expand Up @@ -109,6 +111,7 @@ def get_env_vars(
gh_app_id = get_int_env_var("GH_APP_ID")
gh_app_private_key_bytes = os.environ.get("GH_APP_PRIVATE_KEY", "").encode("utf8")
gh_app_installation_id = get_int_env_var("GH_APP_INSTALLATION_ID")
gh_app_enterprise_only = get_bool_env_var("GITHUB_APP_ENTERPRISE_ONLY")

if gh_app_id and (not gh_app_private_key_bytes or not gh_app_installation_id):
raise ValueError(
Expand Down Expand Up @@ -174,6 +177,7 @@ def get_env_vars(
gh_app_id,
gh_app_installation_id,
gh_app_private_key_bytes,
gh_app_enterprise_only,
token,
ghe,
exempt_repositories_list,
Expand Down
64 changes: 44 additions & 20 deletions test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,50 +4,74 @@
from unittest.mock import MagicMock, patch

import auth
import github3.github


class TestAuth(unittest.TestCase):
"""
Test case for the auth module.
"""

@patch("github3.github.GitHub.login_as_app_installation")
def test_auth_to_github_with_github_app(self, mock_login):
"""
Test the auth_to_github function when GitHub app
parameters provided.
"""
mock_login.return_value = MagicMock()
result = auth.auth_to_github(12345, 678910, b"hello", "", "")

self.assertIsInstance(result, github3.github.GitHub)

def test_auth_to_github_with_token(self):
@patch("github3.login")
def test_auth_to_github_with_token(self, mock_login):
"""
Test the auth_to_github function when the token is provided.
"""
result = auth.auth_to_github(None, None, b"", "token", "")
mock_login.return_value = "Authenticated to GitHub.com"

self.assertIsInstance(result, github3.github.GitHub)
result = auth.auth_to_github("token", "", "", b"", "", False)

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

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):
auth.auth_to_github(None, None, b"", "", "")
with self.assertRaises(ValueError) as context_manager:
auth.auth_to_github("", "", "", b"", "", False)
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",
)

def test_auth_to_github_with_ghe(self):
@patch("github3.github.GitHubEnterprise")
def test_auth_to_github_with_ghe(self, mock_ghe):
"""
Test the auth_to_github function when the GitHub Enterprise URL is provided.
"""
mock_ghe.return_value = "Authenticated to GitHub Enterprise"
result = auth.auth_to_github(
"token", "", "", b"", "https://github.example.com", False
)

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

@patch("github3.github.GitHubEnterprise")
def test_auth_to_github_with_ghe_and_ghe_app(self, mock_ghe):
"""
Test the auth_to_github function when the GitHub Enterprise URL is provided and the app was created in GitHub Enterprise URL.
"""
mock = mock_ghe.return_value
mock.login_as_app_installation = MagicMock(return_value=True)
result = auth.auth_to_github(
None, None, b"", "token", "https://github.example.com"
"", "123", "123", b"123", "https://github.example.com", True
)
mock.login_as_app_installation.assert_called_once()
self.assertEqual(result, mock)

self.assertIsInstance(result, github3.github.GitHubEnterprise)
@patch("github3.github.GitHub")
def test_auth_to_github_with_app(self, mock_gh):
"""
Test the auth_to_github function when app credentials are provided
"""
mock = mock_gh.return_value
mock.login_as_app_installation = MagicMock(return_value=True)
result = auth.auth_to_github(
"", "123", "123", b"123", "https://github.example.com", False
)
mock.login_as_app_installation.assert_called_once()
self.assertEqual(result, mock)


if __name__ == "__main__":
Expand Down
26 changes: 26 additions & 0 deletions test_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ def test_get_env_vars_with_org(self):
None,
None,
b"",
False,
TOKEN,
"",
["repo4", "repo5"],
Expand Down Expand Up @@ -99,6 +100,7 @@ def test_get_env_vars_with_github_app_and_repos(self):
12345,
678910,
b"hello",
False,
"",
"",
["repo4", "repo5"],
Expand All @@ -111,6 +113,27 @@ def test_get_env_vars_with_github_app_and_repos(self):
result = get_env_vars(True)
self.assertEqual(result, expected_result)

@patch.dict(
os.environ,
{
"ORGANIZATION": "my_organization",
"GH_APP_ID": "12345",
"GH_APP_INSTALLATION_ID": "",
"GH_APP_PRIVATE_KEY": "",
"GH_TOKEN": "",
},
clear=True,
)
def test_get_env_vars_auth_with_github_app_installation_missing_inputs(self):
"""Test that an error is raised there are missing inputs for the gh app"""
with self.assertRaises(ValueError) as context_manager:
get_env_vars(True)
the_exception = context_manager.exception
self.assertEqual(
str(the_exception),
"GH_APP_ID set and GH_APP_INSTALLATION_ID or GH_APP_PRIVATE_KEY variable not set",
)

@patch.dict(
os.environ,
{
Expand All @@ -137,6 +160,7 @@ def test_get_env_vars_with_token_and_repos(self):
None,
None,
b"",
False,
TOKEN,
"",
["repo4", "repo5"],
Expand Down Expand Up @@ -171,6 +195,7 @@ def test_get_env_vars_optional_values(self):
None,
None,
b"",
False,
TOKEN,
"",
[],
Expand Down Expand Up @@ -220,6 +245,7 @@ def test_get_env_vars_with_repos_no_dry_run(self):
None,
None,
b"",
False,
TOKEN,
"",
[],
Expand Down

0 comments on commit 33fb3a9

Please sign in to comment.