Skip to content

Commit f4cc226

Browse files
author
espanov
committed
added pr labels for gitlab and github, and merge parameters for gitlab
1 parent 98884a5 commit f4cc226

11 files changed

+150
-9
lines changed

.pylintrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ valid-metaclass-classmethod-first-arg=cls
435435
max-args=5
436436

437437
# Maximum number of attributes for a class (see R0902).
438-
max-attributes=12
438+
max-attributes=14
439439

440440
# Maximum number of boolean expressions in an if statement (see R0916).
441441
max-bool-expr=5

gitopscli/cliparser.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ def parse_args(raw_args: List[str]) -> Tuple[bool, CommandArgs]:
2525

2626
args = vars(parser.parse_args(raw_args))
2727
args = __deduce_empty_git_provider_from_git_provider_url(args, parser.error)
28+
__check_compatibility_args(args, parser.error)
2829

2930
verbose = args.pop("verbose", False)
3031
command_args = __create_command_args(args)
@@ -112,6 +113,15 @@ def __create_deploy_parser() -> ArgumentParser:
112113
type=__parse_bool,
113114
default=False,
114115
)
116+
parser.add_argument(
117+
"--pr-labels", help="JSON object pr labels (Gitlab, Github supported)", type=__parse_yaml, default=None
118+
)
119+
parser.add_argument(
120+
"--gitlab-merge-parameters",
121+
help="JSON object pr parameters (only Gitlab supported)",
122+
type=__parse_yaml,
123+
default=None,
124+
)
115125
__add_verbose_arg(parser)
116126
return parser
117127

@@ -301,6 +311,15 @@ def __deduce_empty_git_provider_from_git_provider_url(
301311
return updated_args
302312

303313

314+
def __check_compatibility_args(args: Dict[str, Any], error: Callable[[str], NoReturn]) -> None:
315+
if "git_provider" not in args:
316+
return
317+
if args.get("pr_labels") and args["git_provider"] == GitProvider.BITBUCKET:
318+
error("Cannot use --pr-labels with --git-provider bitbucket-server")
319+
if args.get("gitlab_merge_parameters") and args["git_provider"] != GitProvider.GITLAB:
320+
error("Can use --gitlab_merge_parameters only with --git-provider gitlab")
321+
322+
304323
def __create_command_args(args: Dict[str, Any]) -> CommandArgs:
305324
args = dict(args)
306325
command = args.pop("command")

gitopscli/commands/deploy.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ class Args(GitApiConfig):
2828
auto_merge: bool
2929
json: bool
3030

31+
pr_labels: Any
32+
gitlab_merge_parameters: Any
3133
merge_method: Literal["squash", "rebase", "merge"] = "merge"
3234

3335
def __init__(self, args: Args) -> None:
@@ -53,9 +55,15 @@ def execute(self) -> None:
5355
if self.__args.create_pr:
5456
title, description = self.__create_pull_request_title_and_description(updated_values)
5557
pr_id = git_repo_api.create_pull_request_to_default_branch(pr_branch, title, description).pr_id
56-
58+
if self.__args.pr_labels:
59+
git_repo_api.add_pull_request_label(pr_id, self.__args.pr_labels)
5760
if self.__args.auto_merge:
58-
git_repo_api.merge_pull_request(pr_id, self.__args.merge_method)
61+
if self.__args.gitlab_merge_parameters:
62+
git_repo_api.merge_pull_request_with_parameters(
63+
pr_id, self.__args.gitlab_merge_parameters, self.__args.merge_method
64+
)
65+
else:
66+
git_repo_api.merge_pull_request(pr_id, self.__args.merge_method)
5967
git_repo_api.delete_branch(pr_branch)
6068

6169
if self.__args.json:

gitopscli/git_api/bitbucket_git_repo_api_adapter.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Optional, Literal
1+
from typing import Dict, Any, Optional, Literal
22
import requests
33

44
from atlassian import Bitbucket
@@ -108,3 +108,14 @@ def get_pull_request_branch(self, pr_id: int) -> str:
108108
def __get_default_branch(self) -> str:
109109
default_branch = self.__bitbucket.get_default_branch(self.__organisation, self.__repository_name)
110110
return str(default_branch["id"])
111+
112+
def merge_pull_request_with_parameters(
113+
self,
114+
pr_id: int,
115+
gitlab_merge_parameters: Dict[str, Any],
116+
merge_method: Literal["squash", "rebase", "merge"] = "merge",
117+
) -> None:
118+
pass
119+
120+
def add_pull_request_label(self, pr_id: int, pr_labels: Dict[str, Any]) -> None:
121+
pass

gitopscli/git_api/git_repo_api.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from abc import ABCMeta, abstractmethod
2-
from typing import NamedTuple, Optional, Literal
2+
from typing import Any, Dict, NamedTuple, Optional, Literal
33

44

55
class GitRepoApi(metaclass=ABCMeta):
@@ -50,3 +50,16 @@ def get_branch_head_hash(self, branch: str) -> str:
5050
@abstractmethod
5151
def get_pull_request_branch(self, pr_id: int) -> str:
5252
...
53+
54+
@abstractmethod
55+
def add_pull_request_label(self, pr_id: int, pr_labels: Dict[str, Any]) -> None:
56+
...
57+
58+
@abstractmethod
59+
def merge_pull_request_with_parameters(
60+
self,
61+
pr_id: int,
62+
gitlab_merge_parameters: Dict[str, Any],
63+
merge_method: Literal["squash", "rebase", "merge"] = "merge",
64+
) -> None:
65+
...

gitopscli/git_api/git_repo_api_logging_proxy.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import logging
2-
from typing import Optional, Literal
2+
from typing import Any, Dict, Optional, Literal
33
from .git_repo_api import GitRepoApi
44

55

@@ -50,3 +50,16 @@ def get_branch_head_hash(self, branch: str) -> str:
5050

5151
def get_pull_request_branch(self, pr_id: int) -> str:
5252
return self.__api.get_pull_request_branch(pr_id)
53+
54+
def add_pull_request_label(self, pr_id: int, pr_labels: Dict[str, Any]) -> None:
55+
logging.info("Adding labels for pull request %s with content: %s", pr_id, pr_labels)
56+
self.__api.add_pull_request_label(pr_id, pr_labels)
57+
58+
def merge_pull_request_with_parameters(
59+
self,
60+
pr_id: int,
61+
gitlab_merge_parameters: Dict[str, Any],
62+
merge_method: Literal["squash", "rebase", "merge"] = "merge",
63+
) -> None:
64+
logging.info("Merging pull request %s with parameters %s", pr_id, gitlab_merge_parameters)
65+
self.__api.merge_pull_request_with_parameters(pr_id, gitlab_merge_parameters, merge_method=merge_method)

gitopscli/git_api/github_git_repo_api_adapter.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Optional, Literal
1+
from typing import Any, Dict, Union, Optional, Literal
22

33
from github import (
44
Github,
@@ -88,3 +88,15 @@ def __get_repo(self) -> Repository.Repository:
8888
raise GitOpsException(
8989
f"Repository '{self.__organisation}/{self.__repository_name}' does not exist."
9090
) from ex
91+
92+
def add_pull_request_label(self, pr_id: int, pr_labels: Union[str, Any]) -> None:
93+
pull_request = self.__get_pull_request(pr_id)
94+
pull_request.set_labels(pr_labels)
95+
96+
def merge_pull_request_with_parameters(
97+
self,
98+
pr_id: int,
99+
gitlab_merge_parameters: Dict[str, Any],
100+
merge_method: Literal["squash", "rebase", "merge"] = "merge",
101+
) -> None:
102+
pass

gitopscli/git_api/gitlab_git_repo_api_adapter.py

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Optional, Literal
1+
from typing import Any, Dict, Optional, Literal
22
import logging
33
import time
44
import requests
@@ -97,8 +97,40 @@ def get_pull_request_branch(self, pr_id: int) -> str:
9797
return str(merge_request.source_branch)
9898

9999
def __get_default_branch(self) -> str:
100-
branches = self.__project.branches.list()
100+
branches = self.__project.branches.list(all=True)
101101
default_branch = next(filter(lambda x: x.default, branches), None)
102102
if default_branch is None:
103103
raise GitOpsException("Default branch does not exist")
104104
return str(default_branch.name)
105+
106+
def add_pull_request_label(self, pr_id: int, pr_labels: Dict[str, Any]) -> None:
107+
merge_request = self.__project.mergerequests.get(pr_id)
108+
merge_request.labels = pr_labels
109+
merge_request.save()
110+
111+
def merge_pull_request_with_parameters(
112+
self,
113+
pr_id: int,
114+
gitlab_merge_parameters: Dict[str, Any],
115+
merge_method: Literal["squash", "rebase", "merge"] = "merge",
116+
) -> None:
117+
merge_request = self.__project.mergerequests.get(pr_id)
118+
119+
max_retries = MAX_MERGE_RETRIES
120+
while max_retries > 0:
121+
try:
122+
if merge_method == "rebase":
123+
merge_request.rebase(gitlab_merge_parameters)
124+
return
125+
merge_request.merge(gitlab_merge_parameters)
126+
return
127+
except gitlab.exceptions.GitlabMRClosedError as ex:
128+
# "Branch cannot be merged" error can occur if the server
129+
# is still processing the merge request internally
130+
max_retries -= 1
131+
logging.warning(
132+
"Retry merging pull request. Attempts: (%s/%s)", MAX_MERGE_RETRIES - max_retries, MAX_MERGE_RETRIES
133+
)
134+
if max_retries == 0:
135+
raise GitOpsException("Error merging pull request: 'Branch cannot be merged'") from ex
136+
time.sleep(2.5)

mypy.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ exclude = (?x)(
1717
build/lib/*/*
1818
| tests/*
1919
| venv/Scripts/*
20+
| venv/bin/*
2021
)
2122

2223

tests/commands/test_deploy.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ def test_happy_flow(self, mock_print):
6868
git_provider_url=None,
6969
commit_message=None,
7070
json=False,
71+
pr_labels=None,
72+
gitlab_merge_parameters=None,
7173
)
7274
DeployCommand(args).execute()
7375

@@ -106,6 +108,8 @@ def test_create_pr_single_value_change_happy_flow_with_output(self, mock_print):
106108
git_provider_url=None,
107109
commit_message=None,
108110
json=True,
111+
pr_labels=None,
112+
gitlab_merge_parameters=None,
109113
)
110114
DeployCommand(args).execute()
111115

@@ -156,6 +160,8 @@ def test_create_pr_multiple_value_changes_happy_flow_with_output(self, mock_prin
156160
git_provider_url=None,
157161
commit_message=None,
158162
json=True,
163+
pr_labels=None,
164+
gitlab_merge_parameters=None,
159165
)
160166
DeployCommand(args).execute()
161167

@@ -212,6 +218,8 @@ def test_create_pr_and_merge_happy_flow(self, mock_print):
212218
git_provider_url=None,
213219
commit_message=None,
214220
json=False,
221+
pr_labels=None,
222+
gitlab_merge_parameters=None,
215223
)
216224
DeployCommand(args).execute()
217225

@@ -259,6 +267,8 @@ def test_single_commit_happy_flow(self, mock_print):
259267
git_provider_url=None,
260268
commit_message=None,
261269
json=False,
270+
pr_labels=None,
271+
gitlab_merge_parameters=None,
262272
)
263273
DeployCommand(args).execute()
264274

@@ -300,6 +310,8 @@ def test_single_commit_single_value_change_happy_flow(self, mock_print):
300310
git_provider_url=None,
301311
commit_message=None,
302312
json=False,
313+
pr_labels=None,
314+
gitlab_merge_parameters=None,
303315
)
304316
DeployCommand(args).execute()
305317

@@ -335,6 +347,8 @@ def test_commit_message_multiple_value_changes_happy_flow(self, mock_print):
335347
git_provider_url=None,
336348
commit_message="testcommit",
337349
json=False,
350+
pr_labels=None,
351+
gitlab_merge_parameters=None,
338352
)
339353
DeployCommand(args).execute()
340354

@@ -374,6 +388,8 @@ def test_clone_error(self):
374388
git_provider_url=None,
375389
commit_message=None,
376390
json=False,
391+
pr_labels=None,
392+
gitlab_merge_parameters=None,
377393
)
378394
with pytest.raises(GitOpsException) as ex:
379395
DeployCommand(args).execute()
@@ -404,6 +420,8 @@ def test_file_not_found(self):
404420
git_provider_url=None,
405421
commit_message=None,
406422
json=False,
423+
pr_labels=None,
424+
gitlab_merge_parameters=None,
407425
)
408426
with pytest.raises(GitOpsException) as ex:
409427
DeployCommand(args).execute()
@@ -436,6 +454,8 @@ def test_file_parse_error(self):
436454
git_provider_url=None,
437455
commit_message=None,
438456
json=False,
457+
pr_labels=None,
458+
gitlab_merge_parameters=None,
439459
)
440460
with pytest.raises(GitOpsException) as ex:
441461
DeployCommand(args).execute()
@@ -468,6 +488,8 @@ def test_key_not_found(self):
468488
git_provider_url=None,
469489
commit_message=None,
470490
json=False,
491+
pr_labels=None,
492+
gitlab_merge_parameters=None,
471493
)
472494
with pytest.raises(GitOpsException) as ex:
473495
DeployCommand(args).execute()
@@ -500,6 +522,8 @@ def test_nothing_to_update(self):
500522
git_provider_url=None,
501523
commit_message=None,
502524
json=False,
525+
pr_labels=None,
526+
gitlab_merge_parameters=None,
503527
)
504528
DeployCommand(args).execute()
505529

0 commit comments

Comments
 (0)