ghot 0.1.0__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.
- ghot-0.1.0/.gitignore +42 -0
- ghot-0.1.0/PKG-INFO +70 -0
- ghot-0.1.0/README.md +51 -0
- ghot-0.1.0/ghot/auth.py +52 -0
- ghot-0.1.0/ghot/config.py +74 -0
- ghot-0.1.0/ghot/csv_loader.py +54 -0
- ghot-0.1.0/ghot/ghot.py +223 -0
- ghot-0.1.0/ghot/org_manager.py +472 -0
- ghot-0.1.0/ghot/pattern_formatter.py +102 -0
- ghot-0.1.0/ghot/user.py +20 -0
- ghot-0.1.0/pyproject.toml +114 -0
- ghot-0.1.0/tests/argparse/test_argparse_auth.py +35 -0
- ghot-0.1.0/tests/argparse/test_argparse_config.py +38 -0
- ghot-0.1.0/tests/argparse/test_argparse_issue_create.py +34 -0
- ghot-0.1.0/tests/argparse/test_argparse_repo_clone.py +50 -0
- ghot-0.1.0/tests/argparse/test_argparse_repo_create.py +49 -0
- ghot-0.1.0/tests/argparse/test_argparse_repo_delete.py +40 -0
- ghot-0.1.0/tests/argparse/test_argparse_repo_invite.py +35 -0
- ghot-0.1.0/tests/argparse/test_argparse_repo_pull.py +46 -0
- ghot-0.1.0/tests/argparse/test_argparse_user_invite.py +34 -0
- ghot-0.1.0/tests/argparse/test_argparse_user_remove.py +39 -0
- ghot-0.1.0/tests/conftest.py +58 -0
- ghot-0.1.0/tests/org_manager/test_issue_create.py +95 -0
- ghot-0.1.0/tests/org_manager/test_repo_clone.py +138 -0
- ghot-0.1.0/tests/org_manager/test_repo_create.py +66 -0
- ghot-0.1.0/tests/org_manager/test_repo_delete.py +81 -0
- ghot-0.1.0/tests/org_manager/test_repo_invite.py +102 -0
- ghot-0.1.0/tests/org_manager/test_repo_pull.py +113 -0
- ghot-0.1.0/tests/org_manager/test_user_invite.py +83 -0
- ghot-0.1.0/tests/org_manager/test_user_remove.py +107 -0
- ghot-0.1.0/tests/pattern_formatter/test_apply_pattern.py +116 -0
- ghot-0.1.0/tests/pattern_formatter/test_resolve_field.py +67 -0
- ghot-0.1.0/tests/test_csv_loader.py +32 -0
ghot-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
venv/
|
|
2
|
+
.venv/
|
|
3
|
+
|
|
4
|
+
# Byte-compiled / optimized / DLL files
|
|
5
|
+
__pycache__/
|
|
6
|
+
*.py[cod]
|
|
7
|
+
*$py.class
|
|
8
|
+
|
|
9
|
+
# C extensions
|
|
10
|
+
*.so
|
|
11
|
+
|
|
12
|
+
# Distribution / packaging
|
|
13
|
+
.Python
|
|
14
|
+
build/
|
|
15
|
+
develop-eggs/
|
|
16
|
+
dist/
|
|
17
|
+
downloads/
|
|
18
|
+
eggs/
|
|
19
|
+
.eggs/
|
|
20
|
+
lib/
|
|
21
|
+
lib64/
|
|
22
|
+
parts/
|
|
23
|
+
sdist/
|
|
24
|
+
var/
|
|
25
|
+
wheels/
|
|
26
|
+
share/python-wheels/
|
|
27
|
+
*.egg-info/
|
|
28
|
+
.installed.cfg
|
|
29
|
+
*.egg
|
|
30
|
+
MANIFEST
|
|
31
|
+
|
|
32
|
+
# PyInstaller
|
|
33
|
+
# Usually these files are written by a python script from a template
|
|
34
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
35
|
+
*.manifest
|
|
36
|
+
*.spec
|
|
37
|
+
|
|
38
|
+
.coverage
|
|
39
|
+
|
|
40
|
+
# Mkdocs
|
|
41
|
+
.cache
|
|
42
|
+
site/
|
ghot-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ghot
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: GitHub Organization Tools
|
|
5
|
+
Project-URL: Documentation, https://joapuiib.github.io/github-organization-tools
|
|
6
|
+
Project-URL: Download, https://github.com/joapuiib/github-organization-tools/releases
|
|
7
|
+
Project-URL: Homepage, https://joapuiib.github.io/github-organization-tools
|
|
8
|
+
Project-URL: Source, https://github.com/joapuiib/github-organization-tools
|
|
9
|
+
Project-URL: Tracker, https://github.com/joapuiib/github-organization-tools/issues
|
|
10
|
+
Author-email: Joan Puigcerver <joapuiib@gmail.com>
|
|
11
|
+
License-Expression: MIT
|
|
12
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
13
|
+
Requires-Python: >=3.8
|
|
14
|
+
Requires-Dist: colorama
|
|
15
|
+
Requires-Dist: gitpython
|
|
16
|
+
Requires-Dist: keyring
|
|
17
|
+
Requires-Dist: pygithub
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# GitHub Organization Tools
|
|
21
|
+
|
|
22
|
+
__GitHub Organization Tools (`ghot`)__ is a CLI tool designed to simplify the management of users and repositories
|
|
23
|
+
within a GitHub organization.
|
|
24
|
+
|
|
25
|
+
__Features__:
|
|
26
|
+
|
|
27
|
+
- Invite and remove users from your organization.
|
|
28
|
+
- Create, clone, pull or delete repositories.
|
|
29
|
+
- Create issues to multiple repositories.
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
This tool can be installed via `pip`:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install ghot
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Quick Start Example
|
|
39
|
+
- Create a new [organization][org] in GitHub.
|
|
40
|
+
|
|
41
|
+
[org]: https://docs.github.com/articles/creating-a-new-organization-from-scratch
|
|
42
|
+
|
|
43
|
+
- Define a CSV file with the users and repositories.
|
|
44
|
+
```csv
|
|
45
|
+
id,username,repo
|
|
46
|
+
id1,user1,user1-repo
|
|
47
|
+
id2,user2,user2-repo
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
> - `id` is a custom identifier for the user.
|
|
51
|
+
> - `username` is the GitHub username.
|
|
52
|
+
> - `repo` is the repository name in the organization.
|
|
53
|
+
>
|
|
54
|
+
> Check the [documentation](https://joapuiib.github.io/github-organization-tools/) for more details!
|
|
55
|
+
|
|
56
|
+
- Invite users to the organization:
|
|
57
|
+
```bash
|
|
58
|
+
ghot user invite my-org users.csv
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- Let users accept the invitation and create their repositories — Or do it for them!
|
|
62
|
+
```bash
|
|
63
|
+
ghot repo create my-org users.csv
|
|
64
|
+
ghot repo invite my-org users.csv
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
- And clone the repositories!
|
|
68
|
+
```bash
|
|
69
|
+
ghot repo clone my-org users.csv
|
|
70
|
+
```
|
ghot-0.1.0/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# GitHub Organization Tools
|
|
2
|
+
|
|
3
|
+
__GitHub Organization Tools (`ghot`)__ is a CLI tool designed to simplify the management of users and repositories
|
|
4
|
+
within a GitHub organization.
|
|
5
|
+
|
|
6
|
+
__Features__:
|
|
7
|
+
|
|
8
|
+
- Invite and remove users from your organization.
|
|
9
|
+
- Create, clone, pull or delete repositories.
|
|
10
|
+
- Create issues to multiple repositories.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
This tool can be installed via `pip`:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install ghot
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start Example
|
|
20
|
+
- Create a new [organization][org] in GitHub.
|
|
21
|
+
|
|
22
|
+
[org]: https://docs.github.com/articles/creating-a-new-organization-from-scratch
|
|
23
|
+
|
|
24
|
+
- Define a CSV file with the users and repositories.
|
|
25
|
+
```csv
|
|
26
|
+
id,username,repo
|
|
27
|
+
id1,user1,user1-repo
|
|
28
|
+
id2,user2,user2-repo
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
> - `id` is a custom identifier for the user.
|
|
32
|
+
> - `username` is the GitHub username.
|
|
33
|
+
> - `repo` is the repository name in the organization.
|
|
34
|
+
>
|
|
35
|
+
> Check the [documentation](https://joapuiib.github.io/github-organization-tools/) for more details!
|
|
36
|
+
|
|
37
|
+
- Invite users to the organization:
|
|
38
|
+
```bash
|
|
39
|
+
ghot user invite my-org users.csv
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
- Let users accept the invitation and create their repositories — Or do it for them!
|
|
43
|
+
```bash
|
|
44
|
+
ghot repo create my-org users.csv
|
|
45
|
+
ghot repo invite my-org users.csv
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
- And clone the repositories!
|
|
49
|
+
```bash
|
|
50
|
+
ghot repo clone my-org users.csv
|
|
51
|
+
```
|
ghot-0.1.0/ghot/auth.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import getpass
|
|
2
|
+
import keyring
|
|
3
|
+
from github import Github, Auth
|
|
4
|
+
|
|
5
|
+
SERVICE_NAME = "github_pat"
|
|
6
|
+
|
|
7
|
+
class AuthManager:
|
|
8
|
+
def __init__(self):
|
|
9
|
+
self.system_user = getpass.getuser()
|
|
10
|
+
self._load_token()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _load_token(self):
|
|
14
|
+
self.token = keyring.get_password(SERVICE_NAME, self.system_user)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def init(self):
|
|
18
|
+
if not self.token:
|
|
19
|
+
self.token = getpass.getpass("Enter your GitHub Personal Access Token: ").strip()
|
|
20
|
+
if input("Save this token for future use? (y/n): ").strip().lower() == 'y':
|
|
21
|
+
keyring.set_password(SERVICE_NAME, self.system_user, self.token)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def has_token(self):
|
|
25
|
+
return self.token is not None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def client(self):
|
|
29
|
+
if not self.token:
|
|
30
|
+
return None
|
|
31
|
+
|
|
32
|
+
auth = Auth.Token(self.token)
|
|
33
|
+
return Github(auth=auth)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def print_token(self):
|
|
37
|
+
if not self.token:
|
|
38
|
+
print("No token found.")
|
|
39
|
+
return
|
|
40
|
+
|
|
41
|
+
print(f"Token for user '{self.system_user}': {self.token}")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def remove_token(self):
|
|
45
|
+
if not self.has_token():
|
|
46
|
+
print("No token found.")
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
response = input("Are you sure you want to remove the stored key? (y/N): ")
|
|
50
|
+
if response.lower() == "y":
|
|
51
|
+
keyring.delete_password(SERVICE_NAME, self.system_user)
|
|
52
|
+
print(f"Removed token for user '{self.system_user}'.")
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import configparser
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
def load_config():
|
|
5
|
+
paths = [os.path.expanduser('~/.ghot')]
|
|
6
|
+
if os.path.exists('.ghot'):
|
|
7
|
+
paths.append(os.path.abspath('.ghot'))
|
|
8
|
+
|
|
9
|
+
config = configparser.ConfigParser(interpolation=None)
|
|
10
|
+
config.read(paths)
|
|
11
|
+
return config
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def apply_config_defaults(parser, config):
|
|
15
|
+
def set_str(default, section, key):
|
|
16
|
+
if config.has_option(section, key):
|
|
17
|
+
parser.set_defaults(**{default: config.get(section, key)})
|
|
18
|
+
|
|
19
|
+
def set_bool(default, section, key):
|
|
20
|
+
if config.has_option(section, key):
|
|
21
|
+
parser.set_defaults(**{default: config.getboolean(section, key)})
|
|
22
|
+
|
|
23
|
+
set_str('pattern_id', 'csv', 'pattern.id')
|
|
24
|
+
set_str('pattern_username', 'csv', 'pattern.username')
|
|
25
|
+
set_str('pattern_repo', 'csv', 'pattern.repo')
|
|
26
|
+
set_str('pattern_description', 'csv', 'pattern.description')
|
|
27
|
+
set_bool('lower_id', 'csv', 'lower.id')
|
|
28
|
+
set_bool('remove_accents', 'csv', 'remove.accents')
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def write_config(key, value, global_scope=False):
|
|
32
|
+
config_path = os.path.expanduser("~/.ghot") if global_scope else ".ghot"
|
|
33
|
+
config = configparser.ConfigParser(interpolation=None)
|
|
34
|
+
config.read(config_path)
|
|
35
|
+
|
|
36
|
+
section, key = key.split(".", 1)
|
|
37
|
+
|
|
38
|
+
if not config.has_section(section):
|
|
39
|
+
config.add_section(section)
|
|
40
|
+
|
|
41
|
+
config.set(section, key, value)
|
|
42
|
+
|
|
43
|
+
with open(config_path, "w") as configfile:
|
|
44
|
+
config.write(configfile)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def show_config(key=None):
|
|
48
|
+
config = load_config()
|
|
49
|
+
|
|
50
|
+
if key is None:
|
|
51
|
+
for section in config.sections():
|
|
52
|
+
print(f"[{section}]")
|
|
53
|
+
for key, value in config.items(section):
|
|
54
|
+
print(f"{key} = {value}")
|
|
55
|
+
print()
|
|
56
|
+
return
|
|
57
|
+
|
|
58
|
+
else:
|
|
59
|
+
section = ""
|
|
60
|
+
if "." in key:
|
|
61
|
+
section, key = key.split(".", 1)
|
|
62
|
+
|
|
63
|
+
if not config.has_option(section, key):
|
|
64
|
+
if section:
|
|
65
|
+
print(f"Config '{section}.{key}' not found.")
|
|
66
|
+
else:
|
|
67
|
+
print(f"Config '{key}' not found.")
|
|
68
|
+
return
|
|
69
|
+
|
|
70
|
+
value = config.get(section, key)
|
|
71
|
+
if section:
|
|
72
|
+
print(f"{section}.{key} = {value}")
|
|
73
|
+
else:
|
|
74
|
+
print(f"{key} = {value}")
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import csv
|
|
2
|
+
|
|
3
|
+
from .user import User
|
|
4
|
+
from .pattern_formatter import PatternFormatter
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class CSVUserLoader:
|
|
8
|
+
def __init__(
|
|
9
|
+
self,
|
|
10
|
+
pattern_id="",
|
|
11
|
+
pattern_username="",
|
|
12
|
+
pattern_repo="",
|
|
13
|
+
pattern_description="",
|
|
14
|
+
):
|
|
15
|
+
self.pattern_id = pattern_id
|
|
16
|
+
self.pattern_username = pattern_username
|
|
17
|
+
self.pattern_repo = pattern_repo
|
|
18
|
+
self.pattern_description = pattern_description
|
|
19
|
+
|
|
20
|
+
self.schema = {}
|
|
21
|
+
self.formatter = PatternFormatter()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def __repr__(self):
|
|
25
|
+
return f"CSVUserLoader(pattern_id={self.pattern_id}, pattern_username={self.pattern_username}, pattern_repo={self.pattern_repo}, pattern_description={self.pattern_description}, lower_id={self.lower_id}, remove_accents={self.remove_accents})"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def load(self, path):
|
|
29
|
+
try:
|
|
30
|
+
with open(path, newline='') as f:
|
|
31
|
+
reader = csv.reader(f, delimiter=',')
|
|
32
|
+
header = next(reader)
|
|
33
|
+
self.load_schema(header)
|
|
34
|
+
return [ self.map(row) for row in reader ]
|
|
35
|
+
except FileNotFoundError:
|
|
36
|
+
print(f"Could not find file: {path}")
|
|
37
|
+
exit(1)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def load_schema(self, header):
|
|
41
|
+
self.schema = {name: idx for idx, name in enumerate(header)}
|
|
42
|
+
self.formatter.schema = self.schema
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def map(self, row):
|
|
46
|
+
"""
|
|
47
|
+
Maps a CSV line to a User object.
|
|
48
|
+
"""
|
|
49
|
+
return User(**{
|
|
50
|
+
"id": self.formatter.format(self.pattern_id, row),
|
|
51
|
+
"username": self.formatter.format(self.pattern_username, row),
|
|
52
|
+
"repo": self.formatter.format(self.pattern_repo, row),
|
|
53
|
+
"description": self.formatter.format(self.pattern_description, row),
|
|
54
|
+
})
|
ghot-0.1.0/ghot/ghot.py
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
from .auth import AuthManager
|
|
5
|
+
from .config import load_config, apply_config_defaults, write_config, show_config
|
|
6
|
+
from .csv_loader import CSVUserLoader
|
|
7
|
+
from .org_manager import OrgManager
|
|
8
|
+
|
|
9
|
+
__version__ = "0.1.0"
|
|
10
|
+
|
|
11
|
+
def build_parser():
|
|
12
|
+
config = load_config()
|
|
13
|
+
def add_csv_options(parser):
|
|
14
|
+
parser.add_argument('csv', help='CSV file')
|
|
15
|
+
parser.add_argument('--pattern-id', default='{f0}', help='Pattern for user ID')
|
|
16
|
+
parser.add_argument('--pattern-username', default='{f1}', help='Pattern for username')
|
|
17
|
+
parser.add_argument('--pattern-repo', default='{f2}', help='Pattern for repository name')
|
|
18
|
+
parser.add_argument('--pattern-description', default='', help='Pattern for repository description')
|
|
19
|
+
apply_config_defaults(parser, config)
|
|
20
|
+
|
|
21
|
+
def add_dry_option(parser):
|
|
22
|
+
parser.add_argument('--dry', action='store_true', help='Dry run mode')
|
|
23
|
+
|
|
24
|
+
parser = argparse.ArgumentParser(description='Git tools.')
|
|
25
|
+
commands = parser.add_subparsers(title="commands", dest="commands")
|
|
26
|
+
|
|
27
|
+
# ghot auth
|
|
28
|
+
auth_p = commands.add_parser('auth')
|
|
29
|
+
## ghot auth check|print|remove
|
|
30
|
+
auth_p.add_argument('auth_commands', choices=['check', 'remove', 'print'], help='Authentication command')
|
|
31
|
+
|
|
32
|
+
# ghot config
|
|
33
|
+
config_p = commands.add_parser('config')
|
|
34
|
+
config_commands = config_p.add_subparsers(title="config_commands", dest="config_commands", required=True)
|
|
35
|
+
## ghot config [set] [--global] <key> <value>
|
|
36
|
+
config_set_p = config_commands.add_parser('set')
|
|
37
|
+
config_set_p.add_argument('--global', dest="_global", action='store_true', help='Set the config globally')
|
|
38
|
+
config_set_p.add_argument('key', help='Config key to set')
|
|
39
|
+
config_set_p.add_argument('value', help='Config value to set')
|
|
40
|
+
## ghot config show <key>
|
|
41
|
+
config_show_p = config_commands.add_parser('show')
|
|
42
|
+
config_show_p.add_argument('key', help='Config key to show', nargs='?', default=None)
|
|
43
|
+
|
|
44
|
+
# ghot user
|
|
45
|
+
user_p = commands.add_parser('user')
|
|
46
|
+
user_commands = user_p.add_subparsers(title="user_commands", dest="user_commands", required=True)
|
|
47
|
+
## ghot user invite <org> <csv>
|
|
48
|
+
user_invite_p = user_commands.add_parser('invite')
|
|
49
|
+
user_invite_p.add_argument("org", help='Organization name')
|
|
50
|
+
add_csv_options(user_invite_p)
|
|
51
|
+
add_dry_option(user_invite_p)
|
|
52
|
+
## ghot user remove <org> <csv> [-f|--force]
|
|
53
|
+
user_remove_p = user_commands.add_parser('remove')
|
|
54
|
+
user_remove_p.add_argument("org", help='Organization name')
|
|
55
|
+
add_csv_options(user_remove_p)
|
|
56
|
+
add_dry_option(user_remove_p)
|
|
57
|
+
user_remove_p.add_argument("-f", "--force", action="store_true", default=False, help='Force removal')
|
|
58
|
+
|
|
59
|
+
# ghot repo
|
|
60
|
+
repo_p = commands.add_parser('repo')
|
|
61
|
+
repo_commands = repo_p.add_subparsers(title="repo_commands", dest="repo_commands", required=True)
|
|
62
|
+
## ghot repo create [--public] [--private] <org> <csv>
|
|
63
|
+
repo_create_p = repo_commands.add_parser('create')
|
|
64
|
+
repo_create_p.add_argument("org", help='Organization name')
|
|
65
|
+
add_csv_options(repo_create_p)
|
|
66
|
+
add_dry_option(repo_create_p)
|
|
67
|
+
repo_create_p.add_argument('--public', action='store_true', help='Create public repositories')
|
|
68
|
+
repo_create_p.add_argument('--private', action='store_true', help='Create private repositories')
|
|
69
|
+
## ghot repo clone [-d|--destination <path>] <csv>
|
|
70
|
+
repo_clone_p = repo_commands.add_parser('clone')
|
|
71
|
+
repo_clone_p.add_argument("org", help='Organization name')
|
|
72
|
+
add_csv_options(repo_clone_p)
|
|
73
|
+
add_dry_option(repo_clone_p)
|
|
74
|
+
repo_clone_p.add_argument('-d', '--destination', help='Destination directory where the repositoiry will be cloned')
|
|
75
|
+
repo_clone_p.add_argument('--ssh', action='store_true', help='Use SSH for cloning')
|
|
76
|
+
## ghot repo pull [-d|--destination <path>] <csv>
|
|
77
|
+
repo_pull_p = repo_commands.add_parser('pull')
|
|
78
|
+
add_csv_options(repo_pull_p)
|
|
79
|
+
add_dry_option(repo_pull_p)
|
|
80
|
+
repo_pull_p.add_argument('-d', '--destination', help='Destination directory where the repositoiry will be pulled')
|
|
81
|
+
## ghot repo delete [-f|--force] <org> <csv>
|
|
82
|
+
repo_delete_p = repo_commands.add_parser('delete')
|
|
83
|
+
repo_delete_p.add_argument('org', help='Organization name')
|
|
84
|
+
add_csv_options(repo_delete_p)
|
|
85
|
+
add_dry_option(repo_delete_p)
|
|
86
|
+
repo_delete_p.add_argument('-f', '--force', action='store_true', default=False, help='Force deletion')
|
|
87
|
+
## ghot repo invite <org> <csv>
|
|
88
|
+
repo_invite_p = repo_commands.add_parser('invite')
|
|
89
|
+
repo_invite_p.add_argument('org', help='Organization name')
|
|
90
|
+
add_csv_options(repo_invite_p)
|
|
91
|
+
add_dry_option(repo_invite_p)
|
|
92
|
+
|
|
93
|
+
# ghot issue
|
|
94
|
+
issue_p = commands.add_parser('issue')
|
|
95
|
+
issue_commands = issue_p.add_subparsers(title="issue_commands", dest="issue_commands", required=True)
|
|
96
|
+
## ghot issue create <org> <csv> <title> <body>
|
|
97
|
+
issue_create_p = issue_commands.add_parser('create')
|
|
98
|
+
issue_create_p.add_argument('org', help='Organization name')
|
|
99
|
+
add_csv_options(issue_create_p)
|
|
100
|
+
issue_create_p.add_argument('title', help='Issue title')
|
|
101
|
+
issue_create_p.add_argument('body', help='Issue body')
|
|
102
|
+
add_dry_option(issue_create_p)
|
|
103
|
+
|
|
104
|
+
return parser
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def preprocess_args(argv):
|
|
108
|
+
if len(argv) > 2:
|
|
109
|
+
if argv[1] == "config" and argv[2] not in ["set", "show"]:
|
|
110
|
+
argv.insert(2, "set")
|
|
111
|
+
|
|
112
|
+
return argv
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def handle_config(args):
|
|
116
|
+
# Default config action
|
|
117
|
+
if args.config_commands is None:
|
|
118
|
+
args.config_commands = "set"
|
|
119
|
+
|
|
120
|
+
match args.config_commands:
|
|
121
|
+
case "set":
|
|
122
|
+
write_config(args.key, args.value, global_scope=args._global)
|
|
123
|
+
case "show":
|
|
124
|
+
show_config(args.key)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def handle_auth(args):
|
|
128
|
+
auth = AuthManager()
|
|
129
|
+
match args.auth_commands:
|
|
130
|
+
case "check":
|
|
131
|
+
auth.init()
|
|
132
|
+
print(f"Authenticated as {auth.client().get_user().login}")
|
|
133
|
+
case "print":
|
|
134
|
+
auth.print_token()
|
|
135
|
+
case "remove":
|
|
136
|
+
auth.remove_token()
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def handle_user(args):
|
|
140
|
+
org_manager = init_org_manager(args)
|
|
141
|
+
users = load_users(args)
|
|
142
|
+
|
|
143
|
+
match args.user_commands:
|
|
144
|
+
case "invite":
|
|
145
|
+
org_manager.user_invite(args.org, users, dry=args.dry)
|
|
146
|
+
case "remove":
|
|
147
|
+
org_manager.user_remove(args.org, users, dry=args.dry, force=args.force)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def handle_repo(args):
|
|
151
|
+
org_manager = init_org_manager(args)
|
|
152
|
+
users = load_users(args)
|
|
153
|
+
|
|
154
|
+
match args.repo_commands:
|
|
155
|
+
case "create":
|
|
156
|
+
private = True
|
|
157
|
+
if args.public and not args.private:
|
|
158
|
+
private = False
|
|
159
|
+
|
|
160
|
+
org_manager.repo_create(args.org, users, private=private, dry=args.dry)
|
|
161
|
+
|
|
162
|
+
case "clone":
|
|
163
|
+
org_manager.repo_clone(args.org, users, destination=args.destination, dry=args.dry, ssh=args.ssh)
|
|
164
|
+
case "pull":
|
|
165
|
+
org_manager.repo_pull(users, destination=args.destination, dry=args.dry)
|
|
166
|
+
case "delete":
|
|
167
|
+
org_manager.repo_delete(args.org, users, dry=args.dry, force=args.force)
|
|
168
|
+
case "invite":
|
|
169
|
+
org_manager.repo_invite(args.org, users, dry=args.dry)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def handle_issue(args):
|
|
173
|
+
org_manager = init_org_manager(args)
|
|
174
|
+
users = load_users(args)
|
|
175
|
+
|
|
176
|
+
match args.issue_commands:
|
|
177
|
+
case "create":
|
|
178
|
+
org_manager.issue_create(args.org, users, args.title, args.body, dry=args.dry)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def init_org_manager(args):
|
|
182
|
+
auth = AuthManager()
|
|
183
|
+
auth.init()
|
|
184
|
+
org_manager = OrgManager(auth.client())
|
|
185
|
+
return org_manager
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def load_users(args):
|
|
189
|
+
csv_loader = CSVUserLoader(
|
|
190
|
+
pattern_id=args.pattern_id,
|
|
191
|
+
pattern_username=args.pattern_username,
|
|
192
|
+
pattern_repo=args.pattern_repo,
|
|
193
|
+
pattern_description=args.pattern_description,
|
|
194
|
+
)
|
|
195
|
+
users = csv_loader.load(args.csv)
|
|
196
|
+
return users
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def main():
|
|
200
|
+
sys.argv = preprocess_args(sys.argv)
|
|
201
|
+
parser = build_parser()
|
|
202
|
+
args = parser.parse_args()
|
|
203
|
+
|
|
204
|
+
args = parser.parse_args()
|
|
205
|
+
|
|
206
|
+
try:
|
|
207
|
+
match args.commands:
|
|
208
|
+
case "auth":
|
|
209
|
+
handle_auth(args)
|
|
210
|
+
case "config":
|
|
211
|
+
handle_config(args)
|
|
212
|
+
case "user":
|
|
213
|
+
handle_user(args)
|
|
214
|
+
case "repo":
|
|
215
|
+
handle_repo(args)
|
|
216
|
+
case "issue":
|
|
217
|
+
handle_issue(args)
|
|
218
|
+
|
|
219
|
+
except KeyboardInterrupt:
|
|
220
|
+
print("\nCancelled by user.")
|
|
221
|
+
except ValueError as e:
|
|
222
|
+
# print on stderr
|
|
223
|
+
print(e, file=sys.stderr)
|