44from typing import Any
55from typing import List
66from typing import Optional
7- from typing import Set
87from typing import Union
98
109import github
10+ import inquirer
1111import requests
1212
1313import git_portfolio .config_manager as cm
14- import git_portfolio .prompt as prompt
14+ import git_portfolio .prompt_validation as val
15+ from git_portfolio .domain .gh_connection_settings import GhConnectionSettings
1516
1617# starting log
1718FORMAT = "%(asctime)s %(message)s"
2021LOGGER = logging .getLogger (__name__ )
2122
2223
24+ def prompt_connect_github (github_access_token : str ) -> GhConnectionSettings :
25+ """Prompt questions to connect to Github."""
26+ questions = [
27+ inquirer .Password (
28+ "github_access_token" ,
29+ message = "GitHub access token" ,
30+ validate = val .not_empty_validation ,
31+ default = github_access_token ,
32+ ),
33+ inquirer .Text (
34+ "github_hostname" ,
35+ message = "GitHub hostname (change ONLY if you use GitHub Enterprise)" ,
36+ ),
37+ ]
38+ answers = inquirer .prompt (questions )
39+ return GhConnectionSettings (
40+ answers ["github_access_token" ], answers ["github_hostname" ]
41+ )
42+
43+
44+ def prompt_new_repos (github_selected_repos : List [str ]) -> Any :
45+ """Prompt question to know if you want to select new repositories."""
46+ message = "\n The configured repos will be used:\n "
47+ for repo in github_selected_repos :
48+ message += f" * { repo } \n "
49+ message += "Do you want to select new repositories?"
50+ answer = inquirer .prompt ([inquirer .Confirm ("" , message = message )])["" ]
51+ return answer
52+
53+
54+ def prompt_select_repos (repo_names : List [str ]) -> Any :
55+ """Prompt questions to select new repositories."""
56+ while True :
57+ selected = inquirer .prompt (
58+ [
59+ inquirer .Checkbox (
60+ "github_repos" ,
61+ message = "Which repos are you working on? (Select pressing space)" ,
62+ choices = repo_names ,
63+ )
64+ ]
65+ )["github_repos" ]
66+ if selected :
67+ return selected
68+ else :
69+ print ("Please select with `space` at least one repo.\n " )
70+
71+
2372class GithubManager :
2473 """Github manager class."""
2574
@@ -31,171 +80,6 @@ def __init__(self, config: cm.Config) -> None:
3180 else :
3281 self .config_ini ()
3382
34- def create_issues (
35- self , issue : Optional [prompt .Issue ] = None , github_repo : str = ""
36- ) -> None :
37- """Create issues."""
38- if not issue :
39- issue = prompt .create_issues (self .config .github_selected_repos )
40- labels = (
41- [label .strip () for label in issue .labels .split ("," )] if issue .labels else []
42- )
43-
44- if github_repo :
45- self ._create_issue_from_repo (github_repo , issue , labels )
46- else :
47- for github_repo in self .config .github_selected_repos :
48- self ._create_issue_from_repo (github_repo , issue , labels )
49-
50- def _create_issue_from_repo (
51- self , github_repo : str , issue : Any , labels : List [str ]
52- ) -> None :
53- """Create issue from one repository."""
54- repo = self .github_connection .get_repo (github_repo )
55- try :
56- repo .create_issue (title = issue .title , body = issue .body , labels = labels )
57- print (f"{ github_repo } : issue created successfully." )
58- except github .GithubException as github_exception :
59- if github_exception .data ["message" ] == "Issues are disabled for this repo" :
60- print (
61- (
62- f"{ github_repo } : { github_exception .data ['message' ]} . "
63- "It may be a fork."
64- )
65- )
66- else :
67- print (f"{ github_repo } : { github_exception .data ['message' ]} ." )
68-
69- def create_pull_requests (
70- self , pr : Optional [prompt .PullRequest ] = None , github_repo : str = ""
71- ) -> None :
72- """Create pull requests."""
73- if not pr :
74- pr = prompt .create_pull_requests (self .config .github_selected_repos )
75-
76- if github_repo :
77- self ._create_pull_request_from_repo (github_repo , pr )
78- else :
79- for github_repo in self .config .github_selected_repos :
80- self ._create_pull_request_from_repo (github_repo , pr )
81-
82- def _create_pull_request_from_repo (self , github_repo : str , pr : Any ) -> None :
83- """Create pull request from one repository."""
84- repo = self .github_connection .get_repo (github_repo )
85- body = pr .body
86- labels = (
87- set (label .strip () for label in pr .labels .split ("," )) if pr .labels else set ()
88- )
89- if pr .confirmation :
90- body = self ._link_issues (body , labels , pr , repo )
91- try :
92- created_pr = repo .create_pull (
93- title = pr .title ,
94- body = body ,
95- head = pr .head ,
96- base = pr .base ,
97- draft = pr .draft ,
98- )
99- print (f"{ github_repo } : PR created successfully." )
100- # PyGithub does not support a list of strings for adding (only one str)
101- for label in labels :
102- created_pr .add_to_labels (label )
103- except github .GithubException as github_exception :
104- extra = ""
105- for error in github_exception .data ["errors" ]:
106- if "message" in error :
107- extra += f"{ error ['message' ]} " # type: ignore
108- else :
109- extra += f"Invalid field { error ['field' ]} . " # type: ignore
110- print (f"{ github_repo } : { github_exception .data ['message' ]} . { extra } " )
111-
112- def _link_issues (self , body : str , labels : Set [Any ], pr : Any , repo : Any ) -> Any :
113- """Return body message linking issues."""
114- issues = repo .get_issues (state = "open" )
115- closes = ""
116- for issue in issues :
117- if pr .link in issue .title :
118- closes += f"#{ issue .number } "
119- if pr .inherit_labels :
120- issue_labels = [label .name for label in issue .get_labels ()]
121- labels .update (issue_labels )
122- closes = closes .strip ()
123- if closes :
124- body += f"\n \n Closes { closes } "
125- return body
126-
127- def merge_pull_requests (
128- self , pr_merge : Optional [prompt .PullRequestMerge ] = None , github_repo : str = ""
129- ) -> None :
130- """Merge pull requests."""
131- if not pr_merge :
132- pr_merge = prompt .merge_pull_requests (
133- self .github_username , self .config .github_selected_repos
134- )
135- # Important note: base and head arguments have different import formats.
136- # https://developer.github.com/v3/pulls/#list-pull-requests
137- # head needs format "user/org:branch"
138- head = f"{ pr_merge .prefix } :{ pr_merge .head } "
139- state = "open"
140-
141- if github_repo :
142- self ._merge_pull_request_from_repo (github_repo , head , pr_merge , state )
143- else :
144- for github_repo in self .config .github_selected_repos :
145- self ._merge_pull_request_from_repo (github_repo , head , pr_merge , state )
146-
147- def _merge_pull_request_from_repo (
148- self , github_repo : str , head : str , pr_merge : Any , state : str
149- ) -> None :
150- """Merge pull request from one repository."""
151- repo = self .github_connection .get_repo (github_repo )
152- pulls = repo .get_pulls (state = state , base = pr_merge .base , head = head )
153- if pulls .totalCount == 1 :
154- pull = pulls [0 ]
155- if pull .mergeable :
156- try :
157- pull .merge ()
158- print (f"{ github_repo } : PR merged successfully." )
159- if pr_merge .delete_branch :
160- self .delete_branches (pr_merge .head , github_repo )
161- except github .GithubException as github_exception :
162- print (f"{ github_repo } : { github_exception .data ['message' ]} ." )
163- else :
164- print (
165- (
166- f"{ github_repo } : PR not mergeable, GitHub checks may be "
167- "running."
168- )
169- )
170- else :
171- print (
172- (
173- f"{ github_repo } : no open PR found for { pr_merge .base } :"
174- f"{ pr_merge .head } ."
175- )
176- )
177-
178- def delete_branches (self , branch : str = "" , github_repo : str = "" ) -> None :
179- """Delete branches."""
180- if not branch :
181- branch = prompt .delete_branches (self .config .github_selected_repos )
182-
183- if github_repo :
184- self ._delete_branch_from_repo (github_repo , branch )
185- else :
186- for github_repo in self .config .github_selected_repos :
187- self ._delete_branch_from_repo (github_repo , branch )
188-
189- def _delete_branch_from_repo (self , github_repo : str , branch : str ) -> None :
190- """Delete a branch from one repository."""
191- repo = self .github_connection .get_repo (github_repo )
192- try :
193- git_ref = repo .get_git_ref (f"heads/{ branch } " )
194- git_ref .delete ()
195- print (f"{ github_repo } : branch deleted successfully." )
196- except github .GithubException as github_exception :
197- print (f"{ github_repo } : { github_exception .data ['message' ]} ." )
198-
19983 def get_github_connection (self ) -> github .Github :
20084 """Get Github connection."""
20185 # GitHub Enterprise
@@ -240,7 +124,7 @@ def config_ini(self) -> None:
240124 # only config if gets a valid connection
241125 valid = False
242126 while not valid :
243- answers = prompt . connect_github (self .config .github_access_token )
127+ answers = prompt_connect_github (self .config .github_access_token )
244128 self .config .github_access_token = answers .github_access_token .strip ()
245129 self .config .github_hostname = answers .github_hostname
246130 valid = self ._github_setup ()
@@ -251,12 +135,12 @@ def config_ini(self) -> None:
251135 def config_repos (self ) -> Optional [cm .Config ]:
252136 """Configure repos in use."""
253137 if self .config .github_selected_repos :
254- new_repos = prompt . new_repos (self .config .github_selected_repos )
138+ new_repos = prompt_new_repos (self .config .github_selected_repos )
255139 if not new_repos :
256140 return None
257141
258142 repo_names = [repo .full_name for repo in self .github_repos ]
259- self .config .github_selected_repos = prompt . select_repos (repo_names )
143+ self .config .github_selected_repos = prompt_select_repos (repo_names )
260144 return self .config
261145
262146 def _github_setup (self ) -> bool :
0 commit comments