cli/pkgcheck: new module
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
a5beab6117
commit
fdf9a35866
|
@ -5,6 +5,17 @@
|
|||
Release Notes
|
||||
=============
|
||||
|
||||
0.5.0
|
||||
-----
|
||||
|
||||
* **New:** Scan repository for QA issues (command: ``pkgcheck scan``).
|
||||
|
||||
* Fix caching with maintainer filter applied.
|
||||
|
||||
* Dependencies introduced:
|
||||
|
||||
* :pypi:`pkgcheck`
|
||||
|
||||
0.4.0
|
||||
-----
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ from click_aliases import ClickAliasedGroup
|
|||
import find_work.cli.bugzilla
|
||||
import find_work.cli.execute
|
||||
import find_work.cli.pgo
|
||||
import find_work.cli.pkgcheck
|
||||
import find_work.cli.repology
|
||||
from find_work.cli import Options
|
||||
from find_work.constants import VERSION
|
||||
|
@ -41,7 +42,7 @@ def cli(ctx: click.Context, maintainer: str | None,
|
|||
options.cache_key.feed(date.today().toordinal())
|
||||
if maintainer:
|
||||
options.maintainer = maintainer
|
||||
options.cache_key.feed(maintainer=maintainer)
|
||||
options.cache_key.feed_option("maintainer", maintainer)
|
||||
|
||||
|
||||
@cli.group(aliases=["bug", "b"], cls=ClickAliasedGroup)
|
||||
|
@ -69,6 +70,11 @@ def bugzilla(options: Options, component: str | None, product: str | None,
|
|||
options.cache_key.feed_option(key, options.bugzilla[key])
|
||||
|
||||
|
||||
@cli.group(aliases=["exec", "e"], cls=ClickAliasedGroup)
|
||||
def execute() -> None:
|
||||
""" Execute a custom command. """
|
||||
|
||||
|
||||
@cli.group(aliases=["p"], cls=ClickAliasedGroup)
|
||||
@click.pass_obj
|
||||
def pgo(options: Options) -> None:
|
||||
|
@ -77,6 +83,21 @@ def pgo(options: Options) -> None:
|
|||
options.cache_key.feed("pgo")
|
||||
|
||||
|
||||
@cli.group(aliases=["chk", "c"], cls=ClickAliasedGroup)
|
||||
@click.option("-r", "--repo", metavar="REPO", required=True,
|
||||
help="Repository name or absolute path.")
|
||||
@click.pass_obj
|
||||
def pkgcheck(options: Options, repo: str) -> None:
|
||||
""" Use pkgcheck to find work. """
|
||||
|
||||
options.cache_key.feed("pkgcheck")
|
||||
|
||||
options.pkgcheck.repo = repo
|
||||
|
||||
for key in options.pkgcheck.cache_order:
|
||||
options.cache_key.feed_option(key, options.pkgcheck[key])
|
||||
|
||||
|
||||
@cli.group(aliases=["rep", "r"], cls=ClickAliasedGroup)
|
||||
@click.option("-r", "--repo", metavar="NAME", required=True,
|
||||
help="Repository name on Repology.")
|
||||
|
@ -92,16 +113,13 @@ def repology(options: Options, repo: str) -> None:
|
|||
options.cache_key.feed_option(key, options.repology[key])
|
||||
|
||||
|
||||
@cli.group(aliases=["exec", "e"], cls=ClickAliasedGroup)
|
||||
def execute() -> None:
|
||||
""" Execute a custom command. """
|
||||
|
||||
|
||||
bugzilla.add_command(find_work.cli.bugzilla.ls, aliases=["ls", "l"])
|
||||
|
||||
pgo.add_command(find_work.cli.pgo.outdated, aliases=["out", "o"])
|
||||
pgo.add_command(find_work.cli.pgo.stabilization, aliases=["stab", "s"])
|
||||
|
||||
pkgcheck.add_command(find_work.cli.pkgcheck.scan, aliases=["s"])
|
||||
|
||||
repology.add_command(find_work.cli.repology.outdated, aliases=["out", "o"])
|
||||
|
||||
find_work.cli.execute.load_aliases(execute)
|
||||
|
|
|
@ -71,7 +71,7 @@ class CacheKey:
|
|||
case _:
|
||||
raise cls._unsupported_type(value)
|
||||
|
||||
def feed(self, *args: Any, **kwargs: Any) -> bool:
|
||||
def feed(self, *args: Any) -> bool:
|
||||
"""
|
||||
Update the key with new data.
|
||||
|
||||
|
|
|
@ -73,18 +73,6 @@ class ModuleOptionsBase(OptionsBase, ABC):
|
|||
...
|
||||
|
||||
|
||||
@dataclass
|
||||
class RepologyOptions(ModuleOptionsBase):
|
||||
""" Repology subcommand options. """
|
||||
|
||||
# Repository name.
|
||||
repo: str = ""
|
||||
|
||||
@cached_property
|
||||
def cache_order(self) -> list[str]:
|
||||
return ["repo"]
|
||||
|
||||
|
||||
@dataclass
|
||||
class BugzillaOptions(ModuleOptionsBase):
|
||||
""" Bugzilla subcommand options. """
|
||||
|
@ -106,8 +94,33 @@ class BugzillaOptions(ModuleOptionsBase):
|
|||
return ["chronological_sort", "short_desc", "product", "component"]
|
||||
|
||||
|
||||
@dataclass
|
||||
class PkgcheckOptions(ModuleOptionsBase):
|
||||
""" pkgcheck subcommand options. """
|
||||
|
||||
# Repository name or absolute path.
|
||||
repo: str = ""
|
||||
|
||||
@cached_property
|
||||
def cache_order(self) -> list[str]:
|
||||
return ["repo"]
|
||||
|
||||
|
||||
@dataclass
|
||||
class RepologyOptions(ModuleOptionsBase):
|
||||
""" Repology subcommand options. """
|
||||
|
||||
# Repository name.
|
||||
repo: str = ""
|
||||
|
||||
@cached_property
|
||||
def cache_order(self) -> list[str]:
|
||||
return ["repo"]
|
||||
|
||||
|
||||
class Message(Enum):
|
||||
""" Typical messages. """
|
||||
|
||||
CACHE_READ = auto()
|
||||
CACHE_LOAD = auto()
|
||||
CACHE_WRITE = auto()
|
||||
|
@ -136,8 +149,9 @@ class Options(OptionsBase):
|
|||
cache_key: CacheKey = field(default_factory=CacheKey)
|
||||
|
||||
# Subcommand options.
|
||||
repology: RepologyOptions = field(default_factory=RepologyOptions)
|
||||
bugzilla: BugzillaOptions = field(default_factory=BugzillaOptions)
|
||||
pkgcheck: PkgcheckOptions = field(default_factory=PkgcheckOptions)
|
||||
repology: RepologyOptions = field(default_factory=RepologyOptions)
|
||||
|
||||
@staticmethod
|
||||
def echo(*args: Any, **kwargs: Any) -> None:
|
||||
|
|
65
find_work/cli/pkgcheck.py
Normal file
65
find_work/cli/pkgcheck.py
Normal file
|
@ -0,0 +1,65 @@
|
|||
# SPDX-License-Identifier: WTFPL
|
||||
# SPDX-FileCopyrightText: 2024 Anna <cyber@sysrq.in>
|
||||
# No warranty
|
||||
|
||||
""" CLI subcommands for everything pkgcheck. """
|
||||
|
||||
import click
|
||||
import gentoopm
|
||||
import pkgcheck
|
||||
from sortedcontainers import SortedDict, SortedSet
|
||||
|
||||
from find_work.cli import Message, Options, ProgressDots
|
||||
from find_work.constants import PKGCHECK_MIN_PACKAGE_SCOPE
|
||||
|
||||
|
||||
def _do_scan(options: Options) -> SortedDict[str, SortedSet]:
|
||||
if options.only_installed or options.maintainer:
|
||||
pm = gentoopm.get_package_manager()
|
||||
if options.maintainer:
|
||||
repo_obj = pm.repositories[options.pkgcheck.repo]
|
||||
|
||||
cli_opts = [
|
||||
"-r", options.pkgcheck.repo,
|
||||
"-f", "latest", # TODO: become version-aware
|
||||
]
|
||||
data: SortedDict[str, SortedSet] = SortedDict()
|
||||
for result in pkgcheck.scan(cli_opts):
|
||||
if result.scope.level < PKGCHECK_MIN_PACKAGE_SCOPE:
|
||||
continue
|
||||
|
||||
package = "/".join([result.category, result.package])
|
||||
if options.only_installed and package not in pm.installed:
|
||||
continue
|
||||
if options.maintainer:
|
||||
for maint in repo_obj.select(package).maintainers:
|
||||
if maint.email == options.maintainer:
|
||||
break
|
||||
else:
|
||||
continue
|
||||
data.setdefault(package, SortedSet(key=str)).add(result)
|
||||
return data
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.pass_obj
|
||||
def scan(options: Options) -> None:
|
||||
options.cache_key.feed("scan")
|
||||
dots = ProgressDots(options.verbose)
|
||||
|
||||
options.vecho("Scouring the neighborhood", nl=False, err=True)
|
||||
with dots():
|
||||
data = _do_scan(options)
|
||||
|
||||
if len(data) == 0:
|
||||
options.say(Message.NO_WORK)
|
||||
return
|
||||
|
||||
for package, results in data.items():
|
||||
options.echo()
|
||||
options.secho(package, fg="cyan", bold=True)
|
||||
for item in results:
|
||||
options.echo("\t", nl=False)
|
||||
options.secho(item.name, fg=item.color, nl=False)
|
||||
options.echo(": ", nl=False)
|
||||
options.echo(item.desc)
|
|
@ -19,6 +19,9 @@ ENTITY = "sysrq.in"
|
|||
# Application's User-agent header.
|
||||
USER_AGENT = f"Mozilla/5.0 (compatible; {PACKAGE}/{VERSION}; +{HOMEPAGE})"
|
||||
|
||||
# Default config file name.
|
||||
DEFAULT_CONFIG = "default_config.toml"
|
||||
|
||||
# Gentoo Bugzilla location.
|
||||
BUGZILLA_URL = "https://bugs.gentoo.org"
|
||||
|
||||
|
@ -28,5 +31,5 @@ PGO_BASE_URL = "https://packages.gentoo.org"
|
|||
# Gentoo Packages API location.
|
||||
PGO_API_URL = f"{PGO_BASE_URL}/api/graphql/"
|
||||
|
||||
# Default config file name.
|
||||
DEFAULT_CONFIG = "default_config.toml"
|
||||
# Scope level for `pkgcheck.base.package_scope`.
|
||||
PKGCHECK_MIN_PACKAGE_SCOPE = 3
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
# SPDX-FileCopyrightText: 2024 Anna <cyber@sysrq.in>
|
||||
# No warranty
|
||||
|
||||
""" Type definitions for the application, implemented as Pydantic models. """
|
||||
"""
|
||||
Type definitions for the application, implemented (mostly) as Pydantic models.
|
||||
"""
|
||||
|
||||
from dataclasses import field
|
||||
from enum import Enum, auto
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
.Ar module
|
||||
.Op Ar arg ...
|
||||
.Ar command
|
||||
.Op Ar arg ...
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
is a fun little tool that lets you discover what you can do for Gentoo as a package mantainer.
|
||||
|
@ -45,7 +44,7 @@ are as follows:
|
|||
.Bl -tag -width repology
|
||||
.It Xo
|
||||
.Cm bugzilla
|
||||
.Ol Fl t
|
||||
.Op Fl t
|
||||
.Op Fl c Ar name
|
||||
.Op Fl p Ar name
|
||||
.Ar command
|
||||
|
@ -58,9 +57,7 @@ This module uses Gentoo Bugzilla to find work.
|
|||
can be one of the following:
|
||||
.Bl -tag -width Ds
|
||||
.It Ic list Pq alias: Ic ls , Ic l
|
||||
Find all open bugs.
|
||||
.It Ic outdated Pq alias: Ic out , Ic o
|
||||
Find open version bump requests.
|
||||
List all open bugs.
|
||||
.El
|
||||
.Pp
|
||||
The options for
|
||||
|
@ -75,7 +72,7 @@ Product name on Bugzilla.
|
|||
Sort by time last modified (most recently modified first).
|
||||
.El
|
||||
.
|
||||
.It Cm execute
|
||||
.It Cm execute Ar command
|
||||
.Dl Pq alias: Cm exec , Cm e
|
||||
This module executes custom commands.
|
||||
.
|
||||
|
@ -94,6 +91,29 @@ Find stabilization candidates.
|
|||
.El
|
||||
.
|
||||
.It Xo
|
||||
.Cm pkgcheck
|
||||
.Fl r Ar repo
|
||||
.Ar command
|
||||
.Xc
|
||||
.Dl Pq alias: Cm chk , Cm c
|
||||
.Pp
|
||||
.Ar command
|
||||
can be one of the following:
|
||||
.Bl -tag -width Ds
|
||||
.It Ic scan Pq alias: Ic s
|
||||
Scan a repository for QA issues.
|
||||
Results with scopes outside of package level are ignored.
|
||||
.El
|
||||
.Pp
|
||||
The options for
|
||||
.Cm find-work pkgcheck
|
||||
are as follows:
|
||||
.Bl -tag -width Ds
|
||||
.It Fl r Ar repo , Fl -repo Ar repo
|
||||
Repository name or absolute path.
|
||||
.El
|
||||
.
|
||||
.It Xo
|
||||
.Cm repology
|
||||
.Fl r Ar repo
|
||||
.Ar command
|
||||
|
@ -128,6 +148,7 @@ Some examples for Gentoo include:
|
|||
.Ql gentoo_ovl_science
|
||||
.El
|
||||
.El
|
||||
.El
|
||||
.Sh ENVIRONMENT
|
||||
.Bl -tag -width NO_COLOR
|
||||
.It Ev NO_COLOR
|
||||
|
|
|
@ -21,6 +21,7 @@ dependencies = [
|
|||
"click-aliases",
|
||||
"deepmerge",
|
||||
"gentoopm<2",
|
||||
"pkgcheck",
|
||||
"platformdirs<5,>=4",
|
||||
"pydantic<3,>=2",
|
||||
"python-bugzilla",
|
||||
|
@ -99,5 +100,6 @@ module = [
|
|||
"deepmerge",
|
||||
"gentoopm",
|
||||
"gentoopm.*",
|
||||
"pkgcheck",
|
||||
]
|
||||
ignore_missing_imports = true
|
||||
|
|
Loading…
Reference in New Issue
Block a user