find-work/find_work/cli/pgo.py
Anna “CyberTailor” 4935f3fe45
All checks were successful
continuous-integration/drone/push Build is passing
cli: add version part filter
Bug: https://bugs.sysrq.in/show_bug.cgi?id=4
2024-04-23 23:23:55 +05:00

199 lines
6.0 KiB
Python

# SPDX-License-Identifier: WTFPL
# SPDX-FileCopyrightText: 2024 Anna <cyber@sysrq.in>
# No warranty
""" CLI subcommands for Gentoo Packages website. """
import asyncio
from collections.abc import Iterable
import click
import gentoopm
from sortedcontainers import SortedDict, SortedSet
from tabulate import tabulate
from find_work.cache import (
read_json_cache,
write_json_cache,
)
from find_work.cli import Message, Options, ProgressDots
from find_work.constants import PGO_BASE_URL, PGO_API_URL
from find_work.types import VersionBump, VersionPart
from find_work.utils import aiohttp_session
async def _fetch_outdated() -> list[dict]:
query = """query {
outdatedPackages{
Atom
GentooVersion
NewestVersion
}
}"""
async with aiohttp_session() as session:
async with session.post(PGO_API_URL, json={"query": query},
raise_for_status=True) as response:
data = await response.json()
return data.get("data", {}).get("outdatedPackages", [])
def _collect_version_bumps(data: Iterable[dict],
options: Options) -> SortedSet[VersionBump]:
if options.only_installed:
pm = gentoopm.get_package_manager()
result: SortedSet[VersionBump] = SortedSet()
for item in data:
bump = VersionBump(item["Atom"],
item.get("GentooVersion", "(unknown)"),
item.get("NewestVersion", "(unknown)"))
if options.only_installed and bump.atom not in pm.installed:
continue
result.add(bump)
return result
async def _outdated(options: Options) -> None:
if options.maintainer:
raise NotImplementedError(
"Filtering by maintainer is not implemented for this command"
)
if (extra_options := options.pgo.extra_options) is not None:
version_part: VersionPart | None = extra_options.get("version_part")
dots = ProgressDots(options.verbose)
options.say(Message.CACHE_LOAD)
with dots():
data = read_json_cache(options.cache_key)
if data is None:
options.vecho("Fetching data from Gentoo Packages API",
nl=False, err=True)
with dots():
data = await _fetch_outdated()
if len(data) == 0:
options.say(Message.EMPTY_RESPONSE)
return
options.say(Message.CACHE_WRITE)
with dots():
write_json_cache(data, options.cache_key)
no_work = True
for bump in _collect_version_bumps(data, options):
if version_part and not bump.changed(version_part):
continue
options.echo(bump.atom + " ", nl=False)
options.secho(bump.old_version, fg="red", nl=False)
options.echo("", nl=False)
options.secho(bump.new_version, fg="green")
no_work = False
if no_work:
options.say(Message.NO_WORK)
async def _fetch_maintainer_stabilization(maintainer: str) -> list[dict]:
url = f"{PGO_BASE_URL}/maintainer/{maintainer}/stabilization.json"
async with aiohttp_session() as session:
async with session.get(url, raise_for_status=True) as response:
data = await response.json()
# bring data to a common structure
return [
{
"Atom": f"{item['category']}/{item['package']}",
"Version": item["version"],
"Message": item["message"],
}
for item in data
]
async def _fetch_all_stabilization() -> list[dict]:
query = """query {
pkgCheckResults(Class: "StableRequest") {
Atom
Version
Message
}
}"""
async with aiohttp_session() as session:
async with session.post(PGO_API_URL, json={"query": query},
raise_for_status=True) as response:
data = await response.json()
return data.get("data", {}).get("pkgCheckResults", [])
async def _fetch_stabilization(options: Options) -> list[dict]:
if options.maintainer:
return await _fetch_maintainer_stabilization(options.maintainer)
return await _fetch_all_stabilization()
def _collect_stable_candidates(data: list[dict],
options: Options) -> SortedDict[str, str]:
if options.only_installed:
pm = gentoopm.get_package_manager()
result: SortedDict[str, str] = SortedDict()
for item in data:
if options.only_installed and item["Atom"] not in pm.installed:
continue
key = "-".join([item["Atom"], item["Version"]])
result[key] = item["Message"]
return result
async def _stabilization(options: Options) -> None:
dots = ProgressDots(options.verbose)
options.say(Message.CACHE_LOAD)
with dots():
data = read_json_cache(options.cache_key)
if data is None:
options.vecho("Fetching data from Gentoo Packages API",
nl=False, err=True)
with dots():
data = await _fetch_stabilization(options)
if len(data) == 0:
options.say(Message.EMPTY_RESPONSE)
return
options.say(Message.CACHE_WRITE)
with dots():
write_json_cache(data, options.cache_key)
candidates = _collect_stable_candidates(data, options)
if len(candidates) != 0:
options.echo(tabulate(candidates.items(), tablefmt="plain"))
else:
options.say(Message.NO_WORK)
@click.command()
@click.option("-F", "--filter", "version_part",
type=click.Choice(["major", "minor", "patch"]),
help="Version part filter.")
@click.pass_obj
def outdated(options: Options, version_part: VersionPart | None = None) -> None:
"""
Find outdated packages.
"""
options.cache_key.feed("outdated")
options.pgo.extra_options = {"version_part": version_part}
asyncio.run(_outdated(options))
@click.command()
@click.pass_obj
def stabilization(options: Options) -> None:
""" Find outdated packages. """
options.cache_key.feed("stabilization")
asyncio.run(_stabilization(options))