Skip to content

Commit

Permalink
Merge pull request #91 from nollied/fix-init
Browse files Browse the repository at this point in the history
Add new functionality for creating merge requests and pull requests with custom titles and descriptions.
  • Loading branch information
steegecs authored Mar 8, 2023
2 parents 5d931d8 + 89b8812 commit 25d77e0
Show file tree
Hide file tree
Showing 10 changed files with 303 additions and 44 deletions.
2 changes: 1 addition & 1 deletion mindflow/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.3.11"
__version__ = "0.3.12"
2 changes: 2 additions & 0 deletions mindflow/cli/new_click_cli/cli_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from mindflow.cli.new_click_cli.commands.git.push import push
from mindflow.cli.new_click_cli.commands.git.commit import commit
from mindflow.cli.new_click_cli.commands.git.diff import diff
from mindflow.cli.new_click_cli.commands.git.mr import mr
from mindflow.cli.new_click_cli.commands.git.pr import pr

from mindflow.cli.new_click_cli.commands.chat import chat
Expand Down Expand Up @@ -38,6 +39,7 @@ def version():
mindflow_cli.add_command(index)
mindflow_cli.add_command(inspect)
mindflow_cli.add_command(login)
mindflow_cli.add_command(mr)
mindflow_cli.add_command(pr)
mindflow_cli.add_command(query)

Expand Down
7 changes: 6 additions & 1 deletion mindflow/cli/new_click_cli/commands/git/commit.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@


@passthrough_command(help="Generate a git commit response by feeding git diff to gpt")
# @overloaded_option(
# "-m",
# "--message",
# help="Don't use mindflow to generate a commit message, use this one instead.",
# default=None,
# )
@click.option(
"-m",
"--message",
Expand All @@ -17,7 +23,6 @@ def commit(args: Tuple[str], message: Optional[str] = None):
"""
Commit command.
"""

if message is not None:
click.echo(
f"Warning: Using message '{message}' instead of mindflow generated message."
Expand Down
50 changes: 50 additions & 0 deletions mindflow/cli/new_click_cli/commands/git/mr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from typing import Optional, Tuple

import click
from mindflow.cli.new_click_cli.util import passthrough_command
from mindflow.core.git.mr import run_mr


@click.group()
def mr():
"""
MR command.
"""
pass


@passthrough_command(help="Generate a git pr response by feeding git diff to gpt")
@click.option(
"-t",
"--title",
help="Don't use mindflow to generate a pr title, use this one instead.",
default=None,
)
@click.option(
"-d",
"--description",
help="Don't use mindflow to generate a pr body, use this one instead.",
default=None,
)
def create(
args: Tuple[str], title: Optional[str] = None, description: Optional[str] = None
):
"""
PR command.
"""
if title is not None:
click.echo(
f"Warning: Using message '{title}' instead of mindflow generated message."
)
click.echo("It's recommended that you don't use the -t/--title flag.")

if description is not None:
click.echo(
f"Warning: Using message '{description}' instead of mindflow generated message."
)
click.echo("It's recommended that you don't use the -d/--description flag.")

run_mr(args, title=title, description=description)


mr.add_command(create)
43 changes: 40 additions & 3 deletions mindflow/cli/new_click_cli/commands/git/pr.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,48 @@
import click
from typing import Optional, Tuple

import click
from mindflow.cli.new_click_cli.util import passthrough_command
from mindflow.core.git.pr import run_pr


@click.command(help="Generate a git pr response by feeding git diff to gpt")
@click.group()
def pr():
"""
PR command.
"""
run_pr()
pass


@passthrough_command(help="Generate a git pr response by feeding git diff to gpt")
@click.option(
"-t",
"--title",
help="Don't use mindflow to generate a pr title, use this one instead.",
default=None,
)
@click.option(
"-b",
"--body",
help="Don't use mindflow to generate a pr body, use this one instead.",
default=None,
)
def create(args: Tuple[str], title: Optional[str] = None, body: Optional[str] = None):
"""
PR command.
"""
if title is not None:
click.echo(
f"Warning: Using message '{title}' instead of mindflow generated message."
)
click.echo("It's recommended that you don't use the -t/--title flag.")

if body is not None:
click.echo(
f"Warning: Using message '{body}' instead of mindflow generated message."
)
click.echo("It's recommended that you don't use the -d/--description flag.")

run_pr(args, title=title, body=body)


pr.add_command(create)
41 changes: 40 additions & 1 deletion mindflow/cli/new_click_cli/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@


def passthrough_command(*command_args, **command_kwargs):
# this function is a decorator that takes the input function and wraps it
"""Just like the @click.command decorator, but allows all args to pass through (for wrapper cli commands)."""

def _decorator(func):
context_settings = command_kwargs.get("context_settings", {})

Expand All @@ -24,3 +25,41 @@ def _decorator(func):
return func

return _decorator


# def overloaded_option(*option_args, **option_kwargs):
# if message is not None:
# click.echo(
# f"Warning: Using message '{message}' instead of mindflow generated message."
# )
# click.echo("It's recommended that you don't use the -m/--message flag.")


# @click.option(
# "-m",
# "--message",
# help="Don't use mindflow to generate a commit message, use this one instead.",
# default=None,
# )

# def _decorator(func):
# dec1 = click.option(*option_args, **option_kwargs)

# func = dec1(func)
# print(func)
# # option_name = func.params[-1].name
# option_name = func.__click_params__[-1].name

# def wrapped_func(**kwargs):
# if option_name in kwargs:
# v = kwargs[option_name]
# click.echo(
# f"Warning: MindFlow overrides {option_name}, but since you passed in it's value as '{v}', that will be used instead."
# )
# click.echo("We recommend not overriding options, but we understand sometimes it's necessary.")

# return func(**kwargs)

# return wrapped_func

# return _decorator
50 changes: 50 additions & 0 deletions mindflow/core/git/mr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import subprocess
from typing import Optional, Tuple, List

from mindflow.core.git.pr import create_title_and_body, is_valid_pr
from mindflow.utils.command_parse import get_flag_value


def run_mr(
args: Tuple[str], title: Optional[str] = None, description: Optional[str] = None
):
base_branch = get_flag_value(args, ["--target-branch", "-b"])
head_branch = get_flag_value(args, ["--source-branch", "-s"])

if base_branch is None:
# Determine the name of the default branch
base_branch = (
subprocess.check_output(["git", "symbolic-ref", "refs/remotes/origin/HEAD"])
.decode()
.strip()
.split("/")[-1]
)

if head_branch is None:
# Get the name of the current branch
head_branch = (
subprocess.check_output(["git", "symbolic-ref", "--short", "HEAD"])
.decode("utf-8")
.strip()
)

if not is_valid_pr(base_branch, head_branch):
return

if not title or not description:
title, description = create_title_and_body(base_branch, title, description)

create_merge_request(args, title, description)


def create_merge_request(args: Tuple[str], title: str, description: str):
command: List[str] = ["glab", "mr", "create"] + list(args) + ["--title", title, "--description", description] # type: ignore
pr_result = subprocess.check_output(command).decode("utf-8")
if "https://" in pr_result:
print("Merge request created successfully")
print(pr_result)
else:
print(
"Failed to create pull request. Please raise an issue at: https://github.com/nollied/mindflow-cli/issues"
)
exit()
104 changes: 67 additions & 37 deletions mindflow/core/git/pr.py
Original file line number Diff line number Diff line change
@@ -1,61 +1,91 @@
import concurrent.futures
import subprocess
from typing import List
from typing import List, Optional, Tuple

from mindflow.core.git.diff import run_diff
from mindflow.settings import Settings
from mindflow.utils.command_parse import get_flag_value
from mindflow.utils.prompt_builders import build_context_prompt
from mindflow.utils.prompts import PR_BODY_PREFIX
from mindflow.utils.prompts import PR_TITLE_PREFIX


def run_pr():
# Get the name of the current branch
current_branch = (
subprocess.check_output(["git", "symbolic-ref", "--short", "HEAD"])
.decode("utf-8")
.strip()
)
def run_pr(args: Tuple[str], title: Optional[str] = None, body: Optional[str] = None):
base_branch = get_flag_value(args, ["--base", "-B"])
head_branch = get_flag_value(args, ["--head", "-H"])

# Determine the name of the default branch
default_branch = (
subprocess.check_output(["git", "symbolic-ref", "refs/remotes/origin/HEAD"])
.decode()
.strip()
.split("/")[-1]
)
if base_branch is None:
# Determine the name of the default branch
base_branch = (
subprocess.check_output(["git", "symbolic-ref", "refs/remotes/origin/HEAD"])
.decode()
.strip()
.split("/")[-1]
)

if current_branch == default_branch:
print("Cannot create pull request from default branch")
if head_branch is None:
# Get the name of the current branch
head_branch = (
subprocess.check_output(["git", "symbolic-ref", "--short", "HEAD"])
.decode("utf-8")
.strip()
)

if not is_valid_pr(base_branch, head_branch):
return

if not has_remote_branch(current_branch):
if not title or not body:
title, body = create_title_and_body(base_branch, title, body)

create_pull_request(args, title, body)


def is_valid_pr(head_branch: str, base_branch: str) -> bool:
if head_branch == base_branch:
print("Cannot create pull request from default branch")
return False

if not has_remote_branch(head_branch):
print("No remote branch for current branch")
return
return False

if needs_push():
print("Current branch needs to be pushed to remote repository")
return
return False

return True


def create_title_and_body(
base_branch, title: Optional[str], body: Optional[str]
) -> Tuple[str, str]:
settings = Settings()

diff_output = run_diff((default_branch,))
diff_output = run_diff((base_branch,))

pr_title_prompt = build_context_prompt(PR_TITLE_PREFIX, diff_output)
pr_body_prompt = build_context_prompt(PR_BODY_PREFIX, diff_output)
if title is None and body is None:
pr_title_prompt = build_context_prompt(PR_TITLE_PREFIX, diff_output)
pr_body_prompt = build_context_prompt(PR_BODY_PREFIX, diff_output)

with concurrent.futures.ThreadPoolExecutor() as executor:
future_title = executor.submit(
settings.mindflow_models.query.model, pr_title_prompt
)
future_body = executor.submit(
settings.mindflow_models.query.model, pr_body_prompt
)
with concurrent.futures.ThreadPoolExecutor() as executor:
future_title = executor.submit(
settings.mindflow_models.query.model, pr_title_prompt
)
future_body = executor.submit(
settings.mindflow_models.query.model, pr_body_prompt
)

pr_title = future_title.result()
pr_body = future_body.result()
title = future_title.result()
body = future_body.result()
else:
if title is None:
pr_title_prompt = build_context_prompt(PR_TITLE_PREFIX, diff_output)
title = settings.mindflow_models.query.model(pr_title_prompt)
if body is None:
pr_body_prompt = build_context_prompt(PR_BODY_PREFIX, diff_output)
body = settings.mindflow_models.query.model(pr_body_prompt)

create_pull_request(pr_title, pr_body)
return title, body


def needs_push() -> bool:
Expand All @@ -69,22 +99,22 @@ def needs_push() -> bool:
return "Your branch is ahead of" in git_status


def has_remote_branch(current_branch: str) -> bool:
def has_remote_branch(head_branch: str) -> bool:
"""
Returns True if there is a remote branch for the current branch, False otherwise.
"""
# Check if there is a remote branch for the current branch
try:
subprocess.check_output(
["git", "ls-remote", "--exit-code", "--heads", "origin", current_branch]
["git", "ls-remote", "--exit-code", "--heads", "origin", head_branch]
)
return True
except subprocess.CalledProcessError:
return False


def create_pull_request(title, body):
command: List[str] = ["gh", "pr", "create", "--title", title, "--body", body] # type: ignore
def create_pull_request(args: Tuple[str], title: str, body: str):
command: List[str] = ["gh", "pr", "create"] + list(args) + ["--title", title, "--body", body] # type: ignore
pr_result = subprocess.check_output(command).decode("utf-8")
if "https://" in pr_result:
print("Pull request created successfully")
Expand Down
Loading

0 comments on commit 25d77e0

Please sign in to comment.