github-org-manager 0.5.2__tar.gz → 0.5.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {github_org_manager-0.5.2 → github_org_manager-0.5.4}/PKG-INFO +15 -1
- {github_org_manager-0.5.2 → github_org_manager-0.5.4}/README.md +14 -0
- {github_org_manager-0.5.2 → github_org_manager-0.5.4}/gh_org_mgr/_gh_api.py +16 -19
- {github_org_manager-0.5.2 → github_org_manager-0.5.4}/gh_org_mgr/_gh_org.py +40 -11
- {github_org_manager-0.5.2 → github_org_manager-0.5.4}/gh_org_mgr/manage.py +6 -1
- {github_org_manager-0.5.2 → github_org_manager-0.5.4}/pyproject.toml +1 -1
- {github_org_manager-0.5.2 → github_org_manager-0.5.4}/LICENSE.txt +0 -0
- {github_org_manager-0.5.2 → github_org_manager-0.5.4}/LICENSES/Apache-2.0.txt +0 -0
- {github_org_manager-0.5.2 → github_org_manager-0.5.4}/LICENSES/CC-BY-4.0.txt +0 -0
- {github_org_manager-0.5.2 → github_org_manager-0.5.4}/LICENSES/CC0-1.0.txt +0 -0
- {github_org_manager-0.5.2 → github_org_manager-0.5.4}/LICENSES/MIT.txt +0 -0
- {github_org_manager-0.5.2 → github_org_manager-0.5.4}/gh_org_mgr/__init__.py +0 -0
- {github_org_manager-0.5.2 → github_org_manager-0.5.4}/gh_org_mgr/_config.py +0 -0
- {github_org_manager-0.5.2 → github_org_manager-0.5.4}/gh_org_mgr/_setup_team.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: github-org-manager
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.4
|
|
4
4
|
Summary: Manage a GitHub Organization, its teams, repository permissions, and more
|
|
5
5
|
Home-page: https://github.com/OpenRailAssociation/github-org-manager
|
|
6
6
|
License: Apache-2.0
|
|
@@ -83,6 +83,20 @@ Inside [`config/example`](./config/example), you can find an example configurati
|
|
|
83
83
|
|
|
84
84
|
You may also be interested in the [live configuration of the OpenRail Association's organization](https://github.com/OpenRailAssociation/openrail-org-config).
|
|
85
85
|
|
|
86
|
+
### Authentication via token or app
|
|
87
|
+
|
|
88
|
+
As this tool issues many API requests (both on REST and GraphQL API), authentication is highly recommended. This is supported via personal access tokens of a user (PAT) or a GitHub App which you can setup yourself.
|
|
89
|
+
|
|
90
|
+
Access tokens and apps need the following permissions:
|
|
91
|
+
* Repository permissions
|
|
92
|
+
* Administration: read and write
|
|
93
|
+
* Metadata: read
|
|
94
|
+
* Organization permissions:
|
|
95
|
+
* Administration: read and write
|
|
96
|
+
* Members: read and write
|
|
97
|
+
|
|
98
|
+
You can set the required secrets in `config/app.yaml` or via environment variables (`GITHUB_TOKEN` or `GITHUB_APP_ID` and `GITHUB_APP_PRIVATE_KEY`).
|
|
99
|
+
|
|
86
100
|
## Run the program
|
|
87
101
|
|
|
88
102
|
You can execute the program using the command `gh-org-mgr`. `gh-org-mgr --help` shows all available arguments and options.
|
|
@@ -54,6 +54,20 @@ Inside [`config/example`](./config/example), you can find an example configurati
|
|
|
54
54
|
|
|
55
55
|
You may also be interested in the [live configuration of the OpenRail Association's organization](https://github.com/OpenRailAssociation/openrail-org-config).
|
|
56
56
|
|
|
57
|
+
### Authentication via token or app
|
|
58
|
+
|
|
59
|
+
As this tool issues many API requests (both on REST and GraphQL API), authentication is highly recommended. This is supported via personal access tokens of a user (PAT) or a GitHub App which you can setup yourself.
|
|
60
|
+
|
|
61
|
+
Access tokens and apps need the following permissions:
|
|
62
|
+
* Repository permissions
|
|
63
|
+
* Administration: read and write
|
|
64
|
+
* Metadata: read
|
|
65
|
+
* Organization permissions:
|
|
66
|
+
* Administration: read and write
|
|
67
|
+
* Members: read and write
|
|
68
|
+
|
|
69
|
+
You can set the required secrets in `config/app.yaml` or via environment variables (`GITHUB_TOKEN` or `GITHUB_APP_ID` and `GITHUB_APP_PRIVATE_KEY`).
|
|
70
|
+
|
|
57
71
|
## Run the program
|
|
58
72
|
|
|
59
73
|
You can execute the program using the command `gh-org-mgr`. `gh-org-mgr --help` shows all available arguments and options.
|
|
@@ -13,20 +13,15 @@ import sys
|
|
|
13
13
|
import requests
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
def
|
|
17
|
-
"""Get
|
|
18
|
-
if
|
|
19
|
-
logging.debug("GitHub
|
|
20
|
-
|
|
21
|
-
elif
|
|
22
|
-
logging.debug("GitHub
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"No token set for GitHub authentication! Set it in config/app_config.yaml "
|
|
26
|
-
"or via environment variable GITHUB_TOKEN"
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
return token
|
|
16
|
+
def get_github_secrets_from_env(env_variable: str, secret: str | int) -> str:
|
|
17
|
+
"""Get GitHub secrets from config or environment, while environment overrides"""
|
|
18
|
+
if env_variable in os.environ and os.environ[env_variable]:
|
|
19
|
+
logging.debug("GitHub secret taken from environment variable %s", env_variable)
|
|
20
|
+
secret = os.environ[env_variable]
|
|
21
|
+
elif secret:
|
|
22
|
+
logging.debug("GitHub secret taken from app configuration file")
|
|
23
|
+
|
|
24
|
+
return str(secret)
|
|
30
25
|
|
|
31
26
|
|
|
32
27
|
# Function to execute GraphQL query
|
|
@@ -51,10 +46,12 @@ def run_graphql_query(query, variables, token):
|
|
|
51
46
|
return json_return
|
|
52
47
|
|
|
53
48
|
# Debug information in case of errors
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
49
|
+
logging.error(
|
|
50
|
+
"Query failed with HTTP error code '%s' when running this query: %s\n"
|
|
51
|
+
"Return: %s\nHeaders: %s",
|
|
52
|
+
request.status_code,
|
|
53
|
+
query,
|
|
54
|
+
json_return,
|
|
55
|
+
request.headers,
|
|
59
56
|
)
|
|
60
57
|
sys.exit(1)
|
|
@@ -8,13 +8,19 @@ import logging
|
|
|
8
8
|
import sys
|
|
9
9
|
from dataclasses import asdict, dataclass, field
|
|
10
10
|
|
|
11
|
-
from github import
|
|
11
|
+
from github import (
|
|
12
|
+
Auth,
|
|
13
|
+
Github,
|
|
14
|
+
GithubException,
|
|
15
|
+
GithubIntegration,
|
|
16
|
+
UnknownObjectException,
|
|
17
|
+
)
|
|
12
18
|
from github.NamedUser import NamedUser
|
|
13
19
|
from github.Organization import Organization
|
|
14
20
|
from github.Repository import Repository
|
|
15
21
|
from github.Team import Team
|
|
16
22
|
|
|
17
|
-
from ._gh_api import
|
|
23
|
+
from ._gh_api import get_github_secrets_from_env, run_graphql_query
|
|
18
24
|
|
|
19
25
|
|
|
20
26
|
@dataclass
|
|
@@ -24,6 +30,8 @@ class GHorg: # pylint: disable=too-many-instance-attributes, too-many-lines
|
|
|
24
30
|
gh: Github = None # type: ignore
|
|
25
31
|
org: Organization = None # type: ignore
|
|
26
32
|
gh_token: str = ""
|
|
33
|
+
gh_app_id: str | int = ""
|
|
34
|
+
gh_app_private_key: str = ""
|
|
27
35
|
default_repository_permission: str = ""
|
|
28
36
|
current_org_owners: list[NamedUser] = field(default_factory=list)
|
|
29
37
|
configured_org_owners: list[str] = field(default_factory=list)
|
|
@@ -56,18 +64,39 @@ class GHorg: # pylint: disable=too-many-instance-attributes, too-many-lines
|
|
|
56
64
|
# supported, or multiple spaces etc.
|
|
57
65
|
return team.replace(" ", "-")
|
|
58
66
|
|
|
59
|
-
def login(
|
|
60
|
-
""
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
67
|
+
def login(
|
|
68
|
+
self, orgname: str, token: str = "", app_id: str | int = "", app_private_key: str = ""
|
|
69
|
+
) -> None:
|
|
70
|
+
"""Login to GH via PAT or App, gather org data"""
|
|
71
|
+
# Get all login data from config and environment
|
|
72
|
+
self.gh_token = get_github_secrets_from_env(env_variable="GITHUB_TOKEN", secret=token)
|
|
73
|
+
self.gh_app_id = get_github_secrets_from_env(env_variable="GITHUB_APP_ID", secret=app_id)
|
|
74
|
+
self.gh_app_private_key = get_github_secrets_from_env(
|
|
75
|
+
env_variable="GITHUB_APP_PRIVATE_KEY", secret=app_private_key
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Decide how to login. If app set, prefer this
|
|
79
|
+
if self.gh_app_id and self.gh_app_private_key:
|
|
80
|
+
logging.debug("Logged in via app %s", self.gh_app_id)
|
|
81
|
+
auth = Auth.AppAuth(app_id=self.gh_app_id, private_key=self.gh_app_private_key)
|
|
82
|
+
app = GithubIntegration(auth=auth)
|
|
83
|
+
installation = app.get_installations()[0]
|
|
84
|
+
self.gh = installation.get_github_for_installation()
|
|
85
|
+
elif self.gh_token:
|
|
86
|
+
logging.debug("Logging in as user with PAT")
|
|
87
|
+
self.gh = Github(auth=Auth.Token(self.gh_token))
|
|
88
|
+
logging.debug("Logged in as %s", self.gh.get_user().login)
|
|
89
|
+
else:
|
|
90
|
+
logging.error("No GitHub token or App ID+private key provided")
|
|
91
|
+
sys.exit(1)
|
|
92
|
+
|
|
64
93
|
self.org = self.gh.get_organization(orgname)
|
|
65
94
|
logging.debug("Gathered data from organization '%s' (%s)", self.org.login, self.org.name)
|
|
66
95
|
|
|
67
96
|
def ratelimit(self):
|
|
68
|
-
"""
|
|
97
|
+
"""Print current rate limit"""
|
|
69
98
|
core = self.gh.get_rate_limit().core
|
|
70
|
-
logging.
|
|
99
|
+
logging.info(
|
|
71
100
|
"Current rate limit: %s/%s (reset: %s)", core.remaining, core.limit, core.reset
|
|
72
101
|
)
|
|
73
102
|
|
|
@@ -917,7 +946,7 @@ class GHorg: # pylint: disable=too-many-instance-attributes, too-many-lines
|
|
|
917
946
|
permissions using the GraphQL API"""
|
|
918
947
|
# TODO: Consider doing this for all repositories at once, but calculate
|
|
919
948
|
# costs beforehand
|
|
920
|
-
|
|
949
|
+
graphql_query = """
|
|
921
950
|
query($owner: String!, $name: String!, $cursor: String) {
|
|
922
951
|
repository(owner: $owner, name: $name) {
|
|
923
952
|
collaborators(first: 100, after: $cursor) {
|
|
@@ -944,7 +973,7 @@ class GHorg: # pylint: disable=too-many-instance-attributes, too-many-lines
|
|
|
944
973
|
|
|
945
974
|
while has_next_page:
|
|
946
975
|
logging.debug("Requesting collaborators for %s", repo.name)
|
|
947
|
-
result = run_graphql_query(
|
|
976
|
+
result = run_graphql_query(graphql_query, variables, self.gh_token)
|
|
948
977
|
try:
|
|
949
978
|
collaborators.extend(result["data"]["repository"]["collaborators"]["edges"])
|
|
950
979
|
has_next_page = result["data"]["repository"]["collaborators"]["pageInfo"][
|
|
@@ -112,7 +112,12 @@ def main():
|
|
|
112
112
|
)
|
|
113
113
|
|
|
114
114
|
# Login to GitHub with token, get GitHub organisation
|
|
115
|
-
org.login(
|
|
115
|
+
org.login(
|
|
116
|
+
orgname=cfg_org.get("org_name", ""),
|
|
117
|
+
token=cfg_app.get("github_token", ""),
|
|
118
|
+
app_id=cfg_app.get("github_app_id", ""),
|
|
119
|
+
app_private_key=cfg_app.get("github_app_private_key", ""),
|
|
120
|
+
)
|
|
116
121
|
# Get current rate limit
|
|
117
122
|
org.ratelimit()
|
|
118
123
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|