Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Closes tags with custom Bugzilla resolution #190

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions data/share/bash-completion/completions/pkgdev
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ source "/usr/share/bash-completion/helpers/gentoo-common.sh"

_pkgdev() {
local i=1 cmd cur prev words cword split
_init_completion || return
_comp_initialize -n : "$@" || return

local subcommands="
bugs
Expand Down Expand Up @@ -79,7 +79,23 @@ _pkgdev() {
"

case "${prev}" in
-[bcTm] | --bug | --closes | --tag | --message)
-c | --closes)
local resolutions=(
fixed
obsolete
pkgremoved
)

local bug="${cur%:*}"
if [[ ${bug} != ${cur} && ${bug} != http?(s) ]]; then
local bugres="${resolutions[*]/#/${bug}:}"
COMPREPLY=($(compgen -W "${bugres}" -- "${cur}"))
_comp_ltrim_colon_completions "$cur"
else
COMPREPLY=()
fi
;;
-[bTm] | --bug | --tag | --message)
COMPREPLY=()
;;
-M | --message-template)
Expand Down
51 changes: 49 additions & 2 deletions src/pkgdev/scripts/pkgdev_commit.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import textwrap
from collections import defaultdict, deque, UserDict
from dataclasses import dataclass
from enum import Enum
from functools import partial
from itertools import chain

Expand Down Expand Up @@ -57,13 +58,17 @@ def parse_known_args(self, args=None, namespace=None):
class BugTag(argparse.Action):
"""Register bug-related tag to inject into the commit message footer."""

def __call__(self, parser, namespace, value, option_string=None):
def parse_url(self, value):
try:
url = f"https://bugs.gentoo.org/{int(value)}"
except ValueError:
url = value
if not url.startswith(("https://", "http://")):
raise argparse.ArgumentError(self, f"invalid URL: {url}")
return url

def __call__(self, parser, namespace, value, option_string=None):
url = self.parse_url(value)
namespace.footer.add((self.dest.capitalize(), url))


Expand All @@ -80,6 +85,39 @@ def __call__(self, parser, namespace, value, option_string=None):
namespace.footer.add((name.capitalize(), val))


class BugzillaAwareBugTag(BugTag):
"""Register bug-related tag and resolution to inject into the commit message footer."""

class Resolution(Enum):
FIXED = "fixed"
OBSOLETE = "obsolete"
PKGREMOVED = "pkgremoved"

def __call__(self, parser, namespace, values, option_string=None):
has_resolution = False
try:
bug, val = values.rsplit(":", 1)
if not bug:
raise argparse.ArgumentError(self, f"invalid commit tag: {values!r}")
if not val.startswith("//"):
has_resolution = True
res = self.Resolution(val.lower()) if val else self.Resolution.FIXED
except ValueError:
if has_resolution:
err = f"{val!r} should be one of: {', '.join([m.value for m in self.Resolution])}"
raise argparse.ArgumentError(self, err)

if not has_resolution:
super().__call__(parser, namespace, values, option_string)
return
url = self.parse_url(bug)
is_bgo = "bugs.gentoo.org" in url
if is_bgo and not res is self.Resolution.FIXED:
url = f"{url} ({res.value})"

namespace.footer.add((self.dest.capitalize(), url))


commit = ArgumentParser(
prog="pkgdev commit",
description="create git commit",
Expand All @@ -92,7 +130,16 @@ def __call__(self, parser, namespace, value, option_string=None):
"-b", "--bug", action=BugTag, help="add Bug tag for a given Gentoo or upstream bug"
)
commit_opts.add_argument(
"-c", "--closes", action=BugTag, help="add Closes tag for a given Gentoo bug or upstream PR URL"
"-c",
"--closes",
action=BugzillaAwareBugTag,
metavar="CLOSES[:RESOLUTION]",
help="add Closes tag and optionally a resolution for a given Gentoo bug or upstream PR URL",
docs="""
Indicate that a bug or PR may be closed. The optional resolution string
for Gentoo's Bugzilla describes what happened to a bug. It is
case-insensitive and must be one of FIXED, OBSOLETE or PKGREMOVED.
""",
)
commit_opts.add_argument(
"-T", "--tag", action=CommitTag, metavar="NAME:VALUE", help="add commit tag"
Expand Down
30 changes: 30 additions & 0 deletions tests/scripts/test_pkgdev_commit.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,36 @@ def test_commit_tags(self, capsys, repo, make_git_repo, tool):
options, _ = tool.parse_args(["commit", opt, "https://bugs.gentoo.org/2"])
assert options.footer == {("Closes", "https://bugs.gentoo.org/2")}

# bug IDs and URLs with good resolutions
for opt in ("-c", "--closes"):
for values, expected in (
(("", "FIXED", "fiXed"), ""),
(("PKGREMOVED", "pkgRemovEd"), " (pkgremoved)"),
(("OBSOLETE", "obSoleTe"), " (obsolete)"),
):
for value in values:
for bug in "1", "https://bugs.gentoo.org/1":
options, _ = tool.parse_args(["commit", opt, f"{bug}:{value}"])
assert options.footer == {
("Closes", f"https://bugs.gentoo.org/1{expected}")
}

# bad bug-resolution pair
for opt in ("-c", "--closes"):
for value, expected in (
(":", "invalid commit tag"),
(":1", "invalid commit tag"),
(":fixed", "invalid commit tag"),
("1:invalid", "should be one of"),
("https://bugs.gentoo.org/1:invalid", "should be one of"),
):
with pytest.raises(SystemExit) as excinfo:
options, _ = tool.parse_args(["commit", opt, value])
assert excinfo.value.code == 2
out, err = capsys.readouterr()
assert not out
assert expected in err

# bad URL
for opt in ("-b", "-c"):
with pytest.raises(SystemExit) as excinfo:
Expand Down
Loading