cli/pkgcheck: new module
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Anna “CyberTailor” 2024-01-14 21:18:28 +05:00
parent a5beab6117
commit fdf9a35866
Signed by: CyberTaIlor
GPG Key ID: E7B76EDC50864BB1
9 changed files with 165 additions and 29 deletions

View File

@ -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
-----

View File

@ -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)

View File

@ -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.

View File

@ -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
View 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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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