Skip to content

Commit

Permalink
refactor: use click (#534)
Browse files Browse the repository at this point in the history
This introduces a CLI interface breakage, due to how click work

This does not work anymore

$ mergify stack push --token foobar

Token (and other global args) are to be passed like this:

$ mergify --token foobar stack push
  • Loading branch information
sileht authored Nov 14, 2024
1 parent 3f920fb commit 5184ed0
Show file tree
Hide file tree
Showing 7 changed files with 347 additions and 327 deletions.
317 changes: 34 additions & 283 deletions mergify_cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,35 +15,18 @@

from __future__ import annotations

import argparse
import asyncio
import os
import sys
import typing
from urllib import parse

import click
import click.decorators
import click_default_group

from mergify_cli import VERSION
from mergify_cli import console
from mergify_cli import utils
from mergify_cli.stack import checkout
from mergify_cli.stack import edit
from mergify_cli.stack import github_action_auto_rebase
from mergify_cli.stack import push
from mergify_cli.stack import setup


def trunk_type(trunk: str) -> tuple[str, str]:
result = trunk.split("/", maxsplit=1)
if len(result) != 2:
msg = "Trunk is invalid. It must be origin/branch-name [/]"
raise argparse.ArgumentTypeError(msg)
return result[0], result[1]


def GitHubToken(v: str) -> str: # noqa: N802
if not v:
raise ValueError
return v
from mergify_cli.stack import cli as stack_cli_mod


async def get_default_github_server() -> str:
Expand All @@ -62,19 +45,6 @@ async def get_default_github_server() -> str:
return url.geturl()


async def get_default_keep_pr_title_body() -> bool:
try:
result = await utils.git(
"config",
"--get",
"mergify-cli.stack-keep-pr-title-body",
)
except utils.CommandError:
return False

return result == "true"


async def get_default_token() -> str:
token = os.environ.get("GITHUB_TOKEN", "")
if not token:
Expand All @@ -90,257 +60,38 @@ async def get_default_token() -> str:
return token


async def _stack_push(args: argparse.Namespace) -> None:
if args.setup:
# backward compat
await setup.stack_setup()
return

await push.stack_push(
args.github_server,
args.token,
args.skip_rebase,
args.next_only,
args.branch_prefix,
args.dry_run,
args.trunk,
args.draft,
args.keep_pull_request_title_and_body,
args.only_update_existing_pulls,
args.author,
)


async def _stack_checkout(args: argparse.Namespace) -> None:
user, repo = args.repository.split("/")

await checkout.stack_checkout(
args.github_server,
args.token,
user,
repo,
args.branch_prefix,
args.branch,
args.author,
args.trunk,
args.dry_run,
)


def register_stack_setup_parser(
sub_parsers: argparse._SubParsersAction[typing.Any],
) -> None:
parser = sub_parsers.add_parser(
"setup",
description="Configure the git hooks",
help="Initial installation of the required git commit-msg hook",
)
parser.set_defaults(func=lambda _: setup.stack_setup)


def register_stack_edit_parser(
sub_parsers: argparse._SubParsersAction[typing.Any],
) -> None:
parser = sub_parsers.add_parser(
"edit",
description="Edit the stack history",
help="Edit the stack history",
)
parser.set_defaults(func=lambda _: edit.stack_edit)


async def _stack_github_action_auto_rebase(args: argparse.Namespace) -> None:
await github_action_auto_rebase.stack_github_action_auto_rebase(
args.github_server,
args.token,
)


def register_stack_github_action_autorebase(
sub_parsers: argparse._SubParsersAction[typing.Any],
) -> None:
parser = sub_parsers.add_parser(
"github-action-auto-rebase",
description="Autorebase a pull requests stack",
help="Checkout a pull requests stack",
)
parser.set_defaults(func=_stack_github_action_auto_rebase)


async def register_stack_checkout_parser(
sub_parsers: argparse._SubParsersAction[typing.Any],
@click.group(
cls=click_default_group.DefaultGroup,
default="stack",
default_if_no_args=True,
)
@click.option("--debug", is_flag=True, default=False, help="debug mode")
@click.version_option(VERSION)
@click.option(
"--github-server",
default=asyncio.run(get_default_github_server()),
)
@click.option(
"--token",
default=asyncio.run(get_default_token()),
help="GitHub personal access token",
)
@click.pass_context
def cli(
ctx: click.Context,
debug: bool,
github_server: str,
token: str,
) -> None:
parser = sub_parsers.add_parser(
"checkout",
description="Checkout a pull requests stack",
help="Checkout a pull requests stack",
)
parser.set_defaults(func=_stack_checkout)
parser.add_argument(
"--author",
help="Set the author of the stack (default: the author of the token)",
)
parser.add_argument(
"--repository",
"--repo",
help="Set the repository where the stack is located (eg: owner/repo)",
)
parser.add_argument(
"--branch",
help="Branch used to create stacked PR.",
)
parser.add_argument(
"--branch-prefix",
default=None,
help="Branch prefix used to create stacked PR. "
"Default fetched from git config if added with `git config --add mergify-cli.stack-branch-prefix some-prefix`",
)
parser.add_argument(
"--dry-run",
"-n",
action="store_true",
help="Only show what is going to be done",
)
parser.add_argument(
"--trunk",
"-t",
type=trunk_type,
default=await utils.get_trunk(),
help="Change the target branch of the stack.",
)


async def register_stack_push_parser(
sub_parsers: argparse._SubParsersAction[typing.Any],
) -> None:
parser = sub_parsers.add_parser(
"push",
description="Push/sync the pull requests stack",
help="Push/sync the pull requests stack",
)
parser.set_defaults(func=_stack_push)

# Backward compat
parser.add_argument(
"--setup",
action="store_true",
help="Initial installation of the required git commit-msg hook",
)

parser.add_argument(
"--dry-run",
"-n",
action="store_true",
help="Only show what is going to be done",
)
parser.add_argument(
"--next-only",
"-x",
action="store_true",
help="Only rebase and update the next pull request of the stack",
)
parser.add_argument(
"--skip-rebase",
"-R",
action="store_true",
help="Skip stack rebase",
)
parser.add_argument(
"--draft",
"-d",
action="store_true",
help="Create stacked pull request as draft",
)
parser.add_argument(
"--keep-pull-request-title-and-body",
"-k",
action="store_true",
default=await get_default_keep_pr_title_body(),
help="Don't update the title and body of already opened pull requests. "
"Default fetched from git config if added with `git config --add mergify-cli.stack-keep-pr-title-body true`",
)
parser.add_argument(
"--author",
help="Set the author of the stack (default: the author of the token)",
)

parser.add_argument(
"--trunk",
"-t",
type=trunk_type,
default=await utils.get_trunk(),
help="Change the target branch of the stack.",
)
parser.add_argument(
"--branch-prefix",
default=None,
help="Branch prefix used to create stacked PR. "
"Default fetched from git config if added with `git config --add mergify-cli.stack-branch-prefix some-prefix`",
)
parser.add_argument(
"--only-update-existing-pulls",
"-u",
action="store_true",
help="Only update existing pull requests, do not create new ones",
)


async def parse_args(args: typing.MutableSequence[str]) -> argparse.Namespace:
parser = argparse.ArgumentParser()
parser.add_argument(
"--version",
"-V",
action="version",
version=f"%(prog)s {VERSION}",
help="display version",
)
parser.add_argument("--debug", action="store_true", help="debug mode")
parser.add_argument(
"--token",
default=await get_default_token(),
type=GitHubToken,
help="GitHub personal access token",
)
parser.add_argument("--dry-run", "-n", action="store_true")
parser.add_argument(
"--github-server",
action="store_true",
default=await get_default_github_server(),
)

sub_parsers = parser.add_subparsers(dest="action")

stack_parser = sub_parsers.add_parser(
"stack",
description="Stacked Pull Requests CLI",
help="Create a pull requests stack",
)
stack_sub_parsers = stack_parser.add_subparsers(dest="stack_action")
await register_stack_push_parser(stack_sub_parsers)
await register_stack_checkout_parser(stack_sub_parsers)
register_stack_edit_parser(stack_sub_parsers)
register_stack_setup_parser(stack_sub_parsers)
register_stack_github_action_autorebase(stack_sub_parsers)

known_args, _ = parser.parse_known_args(args)

# Default
if known_args.action is None:
args.insert(0, "stack")

known_args, _ = parser.parse_known_args(args)

if known_args.action == "stack" and known_args.stack_action is None:
args.insert(1, "push")

return parser.parse_args(args)
ctx.obj = {
"debug": debug,
"github_server": github_server,
"token": token,
}


async def async_main() -> None:
args = await parse_args(sys.argv[1:])
utils.set_debug(args.debug)
await args.func(args)
cli.add_command(stack_cli_mod.stack)


def main() -> None:
asyncio.run(async_main())
cli()
7 changes: 6 additions & 1 deletion mergify_cli/stack/checkout.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,15 @@ async def stack_checkout( # noqa: PLR0913, PLR0917
repo: str,
branch_prefix: str | None,
branch: str,
author: str,
author: str | None,
trunk: tuple[str, str],
dry_run: bool,
) -> None:
if author is None:
async with utils.get_github_http_client(github_server, token) as client:
r_author = await client.get("/user")
author = r_author.json()["login"]

if branch_prefix is None:
branch_prefix = await utils.get_default_branch_prefix(author)

Expand Down
Loading

0 comments on commit 5184ed0

Please sign in to comment.