Skip to content

Commit

Permalink
feat: implement auto-merge feature
Browse files Browse the repository at this point in the history
Automatically merge the created pull request when the `--auto-merge` option is given.

Issue: #18
  • Loading branch information
Joris Conijn committed Feb 5, 2022
1 parent 16c0421 commit 7565a0f
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 30 deletions.
9 changes: 7 additions & 2 deletions pull_request_codecommit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,18 @@

@click.command()
@click.option("-r", "--repository-path", default=None)
def main(repository_path: Optional[str]) -> None:
@click.option("--auto-merge/--no-auto-merge", default=False)
def main(repository_path: Optional[str], auto_merge: bool) -> None:
"""
pull-request-codecommit
"""
repo = __load_repository(repository_path)
__display_repository_information(repo)
__create_pull_request(repo)
pr = __create_pull_request(repo)

if auto_merge:
status = pr.merge()
click.echo(f"Auto merging resulted in: {status}")


def __load_repository(repository_path: Optional[str]) -> Repository:
Expand Down
41 changes: 28 additions & 13 deletions pull_request_codecommit/aws/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,33 @@ def create_pull_request(
source: str,
destination: str,
) -> dict:
command = [
"codecommit",
"create-pull-request",
"--title",
title,
"--description",
description,
"--targets",
f"repositoryName={repository}, sourceReference={source}, destinationReference={destination}",
]
# print(command)
# return {"pullRequestId": 1}
response = self.__execute(command)
response = self.__execute(
[
"codecommit",
"create-pull-request",
"--title",
title,
"--description",
description,
"--targets",
f"repositoryName={repository}, sourceReference={source}, destinationReference={destination}",
]
)
data = json.loads(response)

return data.get("pullRequest")

def merge_pull_request(self, repository: str, pull_request_id: int) -> dict:
response = self.__execute(
[
"codecommit",
"merge-branches-by-fast-forward",
"--pull-request-id",
str(pull_request_id),
"--repository-name",
repository,
]
)
data = json.loads(response)

return data.get("pullRequest")
30 changes: 25 additions & 5 deletions pull_request_codecommit/pull_request.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Optional

from .repository import Repository
from pull_request_codecommit.git import Commits
from .aws import Client as AwsClient
Expand All @@ -8,39 +10,57 @@ class PullRequest:
Understands pull requests
"""

__aws_client: Optional[AwsClient] = None

def __init__(self, repo: Repository) -> None:
self.__repo: Repository = repo
self.__commits: Commits = self.__repo.commits()
self.__title: str = ""
self.__description: str = ""
self.__link: str = ""
self.__pull_request_id: int = 0

@property
def __client(self) -> AwsClient:
if not self.__aws_client:
self.__aws_client = AwsClient(
profile=self.__repo.remote.profile, region=self.__repo.remote.region
)

return self.__aws_client

def update(self, title: str, description: str) -> None:
self.__title = title
self.__description = description

def create(self) -> None:
self.__repo.push()
client = AwsClient(
profile=self.__repo.remote.profile, region=self.__repo.remote.region
)
response = client.create_pull_request(
response = self.__client.create_pull_request(
title=self.title,
description=self.description,
repository=self.__repo.remote.name,
source=self.__repo.branch,
destination=self.__repo.destination,
)

self.__pull_request_id = int(response.get("pullRequestId", 0))

self.__link = "".join(
[
f"https://{self.__repo.remote.region}.console.aws.amazon.com/",
"codesuite/codecommit/repositories/",
f"pull-requests/{response.get('pullRequestId')}/details",
f"pull-requests/{self.__pull_request_id}/details",
f"?region={self.__repo.remote.region}",
]
)

def merge(self) -> str:
response = self.__client.merge_pull_request(
repository=self.__repo.remote.name, pull_request_id=self.__pull_request_id
)

return response.get("pullRequestStatus", "")

@property
def has_changes(self) -> bool:
return True if self.__commits.first else False
Expand Down
109 changes: 99 additions & 10 deletions tests/test_command.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import json
from io import TextIOWrapper, BytesIO
from typing import List
from unittest.mock import patch, MagicMock

import pytest
Expand All @@ -14,71 +15,166 @@ def edit_message(message: str) -> str:
return message


def execute(parameters, stdout):
assert -1 == stdout
mock_stdout = MagicMock()

if "create-pull-request" in parameters:
data = {"pullRequest": {"pullRequestId": 1}}

elif "merge-branches-by-fast-forward" in parameters:
data = {"pullRequest": {"pullRequestStatus": "CLOSED"}}

mock_stdout.stdout = bytes(json.dumps(data), "utf-8")
return mock_stdout


@pytest.mark.parametrize(
"remote, region, profile, config, commits",
"remote, region, profile, config, commits, parameters",
[
(
"codecommit::eu-west-1://my-profile@my-repository",
"eu-west-1",
"my-profile",
b"[default]\nbranch: my-main\n[profile my-profile]\nbranch: my-master",
COMMITS,
[],
),
(
"codecommit::eu-west-1://my-profile@my-repository",
"eu-central-1",
"my-profile",
b"[default]\nbranch: my-main\n[profile my-profile]\nbranch: my-master",
COMMITS_NO_ISSUES,
[],
),
(
"codecommit::eu-west-1://my-profile@my-repository",
"eu-west-1",
"my-other-profile",
b"[default]\nbranch: my-main\n[profile my-profile]\nbranch: my-master",
COMMITS_NO_ISSUES,
[],
),
(
"codecommit::eu-west-1://my-profile@my-repository",
"eu-central-1",
"my-other-profile",
b"[default]\nbranch: my-main\n[profile my-profile]\nbranch: my-master",
COMMITS,
[],
),
(
"codecommit::eu-west-1://my-repository",
"eu-central-1",
None,
b"[default]\nbranch: my-main\n[profile my-profile]\nbranch: my-master",
COMMITS,
[],
),
(
"codecommit::://my-profile@my-repository",
None,
"my-profile",
b"[default]\nbranch: my-main\n[profile my-profile]\nbranch: my-master",
COMMITS,
[],
),
(
"codecommit::://my-repository",
None,
None,
b"[default]\nbranch: my-main\n[profile my-profile]\nbranch: my-master",
COMMITS,
[],
),
(
"codecommit://my-profile@my-repository",
None,
"my-profile",
b"[default]\nbranch: my-main\n[profile my-profile]\nbranch: my-master",
COMMITS,
[],
),
(
"codecommit://my-repository",
None,
None,
b"[default]\nbranch: my-main\n[profile my-profile]\nbranch: my-master",
COMMITS,
[],
),
(
"codecommit::eu-west-1://my-profile@my-repository",
"eu-west-1",
"my-profile",
b"[default]\nbranch: my-main\n[profile my-profile]\nbranch: my-master",
COMMITS,
["--auto-merge"],
),
(
"codecommit::eu-west-1://my-profile@my-repository",
"eu-central-1",
"my-profile",
b"[default]\nbranch: my-main\n[profile my-profile]\nbranch: my-master",
COMMITS_NO_ISSUES,
["--auto-merge"],
),
(
"codecommit::eu-west-1://my-profile@my-repository",
"eu-west-1",
"my-other-profile",
b"[default]\nbranch: my-main\n[profile my-profile]\nbranch: my-master",
COMMITS_NO_ISSUES,
["--auto-merge"],
),
(
"codecommit::eu-west-1://my-profile@my-repository",
"eu-central-1",
"my-other-profile",
b"[default]\nbranch: my-main\n[profile my-profile]\nbranch: my-master",
COMMITS,
["--auto-merge"],
),
(
"codecommit::eu-west-1://my-repository",
"eu-central-1",
None,
b"[default]\nbranch: my-main\n[profile my-profile]\nbranch: my-master",
COMMITS,
["--auto-merge"],
),
(
"codecommit::://my-profile@my-repository",
None,
"my-profile",
b"[default]\nbranch: my-main\n[profile my-profile]\nbranch: my-master",
COMMITS,
["--auto-merge"],
),
(
"codecommit::://my-repository",
None,
None,
b"[default]\nbranch: my-main\n[profile my-profile]\nbranch: my-master",
COMMITS,
["--auto-merge"],
),
(
"codecommit://my-profile@my-repository",
None,
"my-profile",
b"[default]\nbranch: my-main\n[profile my-profile]\nbranch: my-master",
COMMITS,
["--auto-merge"],
),
(
"codecommit://my-repository",
None,
None,
b"[default]\nbranch: my-main\n[profile my-profile]\nbranch: my-master",
COMMITS,
["--auto-merge"],
),
],
)
Expand All @@ -94,25 +190,18 @@ def test_invoke(
profile: str,
config: bytes,
commits: str,
parameters: List[str],
) -> None:
mock_edit.side_effect = edit_message
mock_git_client.return_value.get_commit_messages.return_value = Commits(commits)

mock_git_client.return_value.remote.return_value = remote
mock_git_client.return_value.current_branch.return_value = "feat/my-feature"
configparser.open = MagicMock(return_value=TextIOWrapper(BytesIO(config))) # type: ignore

def execute(parameters, stdout):
assert -1 == stdout
mock_stdout = MagicMock()
data = {"pullRequest": {"pullRequestId": 1}}
mock_stdout.stdout = bytes(json.dumps(data), "utf-8")
return mock_stdout

mock_aws_client.side_effect = execute

runner = CliRunner()
result = runner.invoke(main, [])
result = runner.invoke(main, parameters)
assert result.exit_code == 0


Expand Down

0 comments on commit 7565a0f

Please sign in to comment.