#!/usr/bin/env python3
"""
Einreichung fuer:
https://gnulinux.ch/programmierwettbewerb-wer-liefert-am-schnellsten
von: geronymo
"""
import json
import re
import logging
import argparse
from urllib import request
from urllib.error import HTTPError, URLError
from abc import abstractmethod
# https://dateutil.readthedocs.io/en/stable/index.html
#from dateutil.parser import isoparse
def _url_content(url) -> bytes:
try:
with request.urlopen(url) as response:
content = response.read()
except HTTPError:
return None
except URLError:
logging.warning("invalid url: " + url)
return None
return content
class Package:
"""Base class for all Packages"""
def __init__(self, repository: str, name: str, distro='', distro_release=''):
self.repository = repository
self.name = name
self.distro = distro
self.distro_release = distro_release
self.version = ''
self.last_updated = ''
self.update()
@abstractmethod
def update(self):
pass
class PackageArchLinux(Package):
def __init__(self, repository: str, name: str):
super().__init__(repository, name, distro="ArchLinux", distro_release="")
def update(self):
url = "https://archlinux.org/packages/"
url += str(self.repository)
url += "/x86_64/"
url += str(self.name)
url += "/json/"
package_info = _url_content(url)
if package_info is None:
return
package_info = json.loads(package_info)
self.version = package_info["pkgver"]
self.last_updated = package_info["last_update"].strip()
class PackageDebian(Package):
def __init__(self, repository: str, name: str):
super().__init__(repository, name, distro="Debian")
def update(self):
#url = f'https://tracker.debian.org/pkg/{self.name}'
url = f'https://packages.debian.org/source/{self.repository}/{self.name}'
html = _url_content(url)
if html is None:
return
# regular expression to find version in html
vp = f'
Source Package: {self.name} \((.*?)\)\n
'
vp = bytes(vp, 'utf-8')
match = re.search(vp, html)
if match:
self.version = match.group(1).decode()
#TODO: find out when this package was added to unstable
class PackageFedora(Package):
def __init__(self, repository: str, name: str, distro_release="37", source_package=None):
if source_package is not None:
self.source_package = source_package
else:
self.source_package = name
super().__init__(repository, name, distro="Fedora", distro_release=distro_release)
def update(self):
url = f'https://packages.fedoraproject.org/pkgs/{self.source_package}/{self.name}/'
html = _url_content(url)
if html is None:
return
# regular expression to find version in html
vp = f'(.*?)'
vp = bytes(vp, 'utf-8')
match = re.search(vp, html)
if match:
self.version = match.group(1).decode()
#TODO: find out when this package was added to unstable
class PackageFlatPackFlatHub(Package):
def __init__(self, name: str):
super().__init__('flathub', name, distro='flatpak')
def update(self):
url = f'https://beta.flathub.org/apps/details/{self.name}'
html = _url_content(url)
if html is None:
return
# regular expression to find version in html
vp = rb'Changes in version (.*?)
'
match = re.search(vp, html)
if match:
self.version = match.group(1).decode()
up = rb'.*?
'
match = re.search(up, html)
if match:
month = match.group(1).decode()
day = match.group(2).decode()
year = match.group(3).decode()
self.last_updated = year + '-' + month + '-' + day
class PackageReptology(Package):
def update(self):
pass
def __eq__(self, other):
equal = self.name == other.name
equal = equal and (self.version == other.version)
equal = equal and (self.distro == other.distro)
equal = equal and (self.repository == other.repository)
def _pprint(packages: list[Package]):
hline = '-' * (15+15+15+15)
print('{:<15}{:<15}{:<15}{:<15}'.format(
'distro',
'repository',
'version',
'last updated',
)
)
for p in packages:
print(hline)
print('{:<15}{:<15}{:<15}{:<15}'.format(
str(p.distro) + " " + str(p.distro_release),
str(p.repository),
str(p.version),
str(p.last_updated),
)
)
def main():
SOFTWARE = ['firefox', 'thunderbird', 'libreoffice', 'inkscape', 'mesa']
parser = argparse.ArgumentParser(
description='Get information about the package versions of software')
parser.add_argument(
'-p', '--packages',
nargs='+',
choices=SOFTWARE,
metavar='SOFTWARE',
help='Space separated list of software-packages that should be'
+ ' displayed.'
+ f' Supported parameters are: {SOFTWARE}',
)
parser.add_argument(
'-a', '--all',
action='store_true',
help='Select all packages',
)
parser.add_argument(
'-s', '--search',
nargs=1,
help='Search for a software using'
+ ' the same package name for all distros.'
+ ' This is different from "-p" because you can try any search term.'
+ ' This ignores the fact that software can have different names'
+ ' in different distros/repositorys.'
+ ' Distros with no result my still have the package but under a'
+ ' different name.',
)
parser.add_argument(
'-r', '--reptology',
nargs=1,
help='Search for a package using reptology',
)
args = parser.parse_args()
selected_packages = []
if args.packages:
selected_packages = args.packages
if args.all:
selected_packages = SOFTWARE
if args.search:
_pprint([
PackageArchLinux("core", args.search[0]),
PackageArchLinux("extra", args.search[0]),
PackageArchLinux("community", args.search[0]),
PackageArchLinux("multilib", args.search[0]),
PackageArchLinux("testing", args.search[0]),
PackageArchLinux("community-testing", args.search[0]),
PackageDebian("unstable", args.search[0]),
PackageDebian("testing", args.search[0]),
PackageDebian("stable", args.search[0]),
PackageFedora("stable", args.search[0]),
PackageFedora("stable", args.search[0], distro_release='36'),
PackageFlatPackFlatHub(args.search[0]),
])
if args.reptology:
packages = []
searchterm = args.reptology[0]
url = 'https://repology.org/api/v1/projects/?search='
url += searchterm
url += '&maintainer=&category=&inrepo=¬inrepo=&repos=&families='
url += '&repos_newest=&families_newest=&newest=on'
package_info = _url_content(url)
if package_info is None:
return
package_info = json.loads(package_info)
for k in package_info.keys():
if k == searchterm:
for p in package_info[k]:
try:
name = p.get('name')
if name is None:
name = p.get('srcname')
if name is None:
name = p.get('binname')
#if name != searchterm:
# continue
distro = p['repo']
repository = p.get('subrepo', '')
version = p['version']
package = PackageReptology(repository, name, distro=distro)
package.version = version
if package not in packages:
packages.append(package)
except KeyError:
continue
_pprint(packages)
if 'firefox' in selected_packages:
print("\nFirefox:")
_pprint([
PackageArchLinux("extra", "firefox"),
PackageDebian("unstable", "firefox"),
PackageDebian("testing", "firefox"),
PackageDebian("stable", "firefox"),
PackageFedora("stable", "firefox"),
PackageFlatPackFlatHub("org.mozilla.firefox"),
])
if 'thunderbird' in selected_packages:
print("\nThunderbird:")
_pprint([
PackageArchLinux("extra", "thunderbird"),
PackageDebian("unstable", "thunderbird"),
PackageDebian("testing", "thunderbird"),
PackageDebian("stable", "thunderbird"),
PackageFedora("stable", "thunderbird"),
PackageFlatPackFlatHub("org.mozilla.Thunderbird"),
])
if 'libreoffice' in selected_packages:
print("\nLibreOffice:")
_pprint([
PackageArchLinux("extra", "libreoffice-fresh"),
PackageArchLinux("extra", "libreoffice-still"),
PackageDebian("unstable", "libreoffice"),
PackageDebian("testing", "libreoffice"),
PackageDebian("stable", "libreoffice"),
PackageFedora("stable", "libreoffice"),
PackageFlatPackFlatHub("org.libreoffice.LibreOffice"),
])
if 'inkscape' in selected_packages:
print("\nInkscape:")
_pprint([
PackageArchLinux("extra", "inkscape"),
PackageDebian("unstable", "inkscape"),
PackageDebian("testing", "inkscape"),
PackageDebian("stable", "inkscape"),
PackageFedora("stable", "inkscape"),
PackageFlatPackFlatHub("org.inkscape.Inkscape"),
])
if 'mesa' in selected_packages:
print("\nMesa:")
_pprint([
PackageArchLinux("extra", "mesa"),
PackageDebian("unstable", "mesa"),
PackageDebian("testing", "mesa"),
PackageDebian("stable", "mesa"),
# seems like Fedora has no package called mesa, and splits mesa in a lot
# of different packages.
PackageFedora("stable", "mesa-libGL", source_package='mesa'),
])
if __name__ == "__main__":
main()