Skip to content
This repository was archived by the owner on Apr 13, 2023. It is now read-only.

Commit 46fe193

Browse files
author
Thiago C. D'Ávila
authored
Merge pull request #91 from staticdev/github-refactor
Refactor
2 parents 3760c57 + ec8022c commit 46fe193

24 files changed

+738
-495
lines changed

src/git_portfolio/__main__.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,13 @@
1010
import click
1111

1212
import git_portfolio.config_manager as cm
13-
import git_portfolio.git_use_case as guc
1413
import git_portfolio.github_manager as ghm
1514
import git_portfolio.response_objects as res
15+
import git_portfolio.use_cases.gh_create_issue_use_case as ci
16+
import git_portfolio.use_cases.gh_create_pr_use_case as cpr
17+
import git_portfolio.use_cases.gh_delete_branch_use_case as dbr
18+
import git_portfolio.use_cases.gh_merge_pr_use_case as mpr
19+
import git_portfolio.use_cases.git_use_case as guc
1620

1721
F = TypeVar("F", bound=Callable[..., Any])
1822
CONFIG_MANAGER = cm.ConfigManager()
@@ -149,25 +153,29 @@ def config_repos() -> None:
149153
@create.command("issues")
150154
def create_issues() -> None:
151155
"""Create issues command."""
152-
ghm.GithubManager(CONFIG_MANAGER.config).create_issues()
156+
manager = ghm.GithubManager(CONFIG_MANAGER.config)
157+
ci.GhCreateIssueUseCase(manager).execute()
153158

154159

155160
@create.command("prs")
156161
def create_prs() -> None:
157162
"""Create prs command."""
158-
ghm.GithubManager(CONFIG_MANAGER.config).create_pull_requests()
163+
manager = ghm.GithubManager(CONFIG_MANAGER.config)
164+
cpr.GhCreatePrUseCase(manager).execute()
159165

160166

161167
@merge.command("prs")
162168
def merge_prs() -> None:
163169
"""Merge prs command."""
164-
ghm.GithubManager(CONFIG_MANAGER.config).merge_pull_requests()
170+
manager = ghm.GithubManager(CONFIG_MANAGER.config)
171+
mpr.GhMergePrUseCase(manager).execute()
165172

166173

167174
@delete.command("branches")
168175
def delete_branches() -> None:
169176
"""Delete branches command."""
170-
ghm.GithubManager(CONFIG_MANAGER.config).delete_branches()
177+
manager = ghm.GithubManager(CONFIG_MANAGER.config)
178+
dbr.GhDeleteBranchUseCase(manager).execute()
171179

172180

173181
main.add_command(configure)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Git portfolio domain models."""
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
"""Github connection settings model."""
2+
import collections
3+
4+
5+
GhConnectionSettings = collections.namedtuple(
6+
"ConnectGithub", ["github_access_token", "github_hostname"]
7+
)

src/git_portfolio/domain/issue.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"""Issue model."""
2+
import collections
3+
4+
5+
Issue = collections.namedtuple("Issue", ["title", "body", "labels"])
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
"""Pull request model."""
2+
import collections
3+
4+
PullRequest = collections.namedtuple(
5+
"PullRequest",
6+
[
7+
"title",
8+
"body",
9+
"labels",
10+
"confirmation",
11+
"link",
12+
"inherit_labels",
13+
"head",
14+
"base",
15+
"draft",
16+
],
17+
)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
"""Pull request merge model."""
2+
import collections
3+
4+
5+
PullRequestMerge = collections.namedtuple(
6+
"PullRequestMerge", ["base", "head", "prefix", "delete_branch"]
7+
)

src/git_portfolio/github_manager.py

Lines changed: 54 additions & 170 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44
from typing import Any
55
from typing import List
66
from typing import Optional
7-
from typing import Set
87
from typing import Union
98

109
import github
10+
import inquirer
1111
import requests
1212

1313
import 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
1718
FORMAT = "%(asctime)s %(message)s"
@@ -20,6 +21,54 @@
2021
LOGGER = 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 = "\nThe 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+
2372
class 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\nCloses {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

Comments
 (0)