add initial version of “aur check” script
This commit is contained in:
parent
7b55a6e29f
commit
fff4d1296a
195
arch.py
Normal file
195
arch.py
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
|
import os
|
||||||
|
import tarfile
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ArchDatabase:
|
||||||
|
"""Representation of an Arch Linux package database."""
|
||||||
|
FILE_EXTENSION = ".db.tar.gz"
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, filename):
|
||||||
|
"""Construct a new database object and read all containing packages."""
|
||||||
|
self.filename = filename
|
||||||
|
self.packages = []
|
||||||
|
self._parse()
|
||||||
|
|
||||||
|
|
||||||
|
def get_fielname(self):
|
||||||
|
"""Get filename of database."""
|
||||||
|
return self.filename
|
||||||
|
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
"""Get simple name of database."""
|
||||||
|
return os.path.basename(self.filename)[0:-len(ArchDatabase.FILE_EXTENSION)]
|
||||||
|
|
||||||
|
|
||||||
|
def get_packages(self):
|
||||||
|
"""Get list of packages contained in the database."""
|
||||||
|
return self.packages
|
||||||
|
|
||||||
|
|
||||||
|
def _parse(self):
|
||||||
|
"""Read and parse database content."""
|
||||||
|
with tarfile.open(name=self.filename) as tf:
|
||||||
|
for f in tf:
|
||||||
|
if f.isfile() and os.path.basename(f.name) == "desc":
|
||||||
|
df = tf.extractfile(f)
|
||||||
|
s = df.read().decode('utf-8')
|
||||||
|
|
||||||
|
d = dict(item.split("\n")[:2] for item in s.split("\n\n")[:-1])
|
||||||
|
archPackage = ArchPackage(d["%NAME%"])
|
||||||
|
archPackage.set_filename(d["%FILENAME%"])
|
||||||
|
archPackage.set_version(d["%VERSION%"])
|
||||||
|
archPackage.set_desc(d["%DESC%"])
|
||||||
|
archPackage.set_csize(d["%CSIZE%"])
|
||||||
|
archPackage.set_isize(d["%ISIZE%"])
|
||||||
|
archPackage.set_url(d["%URL%"])
|
||||||
|
archPackage.set_license(d["%LICENSE%"])
|
||||||
|
archPackage.set_arch(d["%ARCH%"])
|
||||||
|
archPackage.set_builddate(d["%BUILDDATE%"])
|
||||||
|
archPackage.set_packager(d["%PACKAGER%"])
|
||||||
|
self.packages.append(archPackage)
|
||||||
|
|
||||||
|
|
||||||
|
def find_databases(directory):
|
||||||
|
"""Find all database files in a directory, including subdirectories."""
|
||||||
|
databases = []
|
||||||
|
for root, dirs, files in os.walk(directory):
|
||||||
|
for file in files:
|
||||||
|
if file.endswith(ArchDatabase.FILE_EXTENSION):
|
||||||
|
archDatabase = ArchDatabase(os.path.join(root, file))
|
||||||
|
databases.append(archDatabase)
|
||||||
|
return databases
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ArchPackage:
|
||||||
|
"""Representation of an Arch Linux package."""
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, name):
|
||||||
|
"""Construct a new package."""
|
||||||
|
self.name = name
|
||||||
|
self.filename = None
|
||||||
|
self.version = None
|
||||||
|
self.desc = None
|
||||||
|
self.csize = None
|
||||||
|
self.isize = None
|
||||||
|
self.url = None
|
||||||
|
self.license = None
|
||||||
|
self.arch = None
|
||||||
|
self.builddate = None
|
||||||
|
self.packager = None
|
||||||
|
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
"""Get the package name."""
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
def set_filename(self, filename):
|
||||||
|
"""Set package filename."""
|
||||||
|
self.filename = filename
|
||||||
|
|
||||||
|
|
||||||
|
def get_filename(self):
|
||||||
|
"""Get package filename."""
|
||||||
|
return self.filename
|
||||||
|
|
||||||
|
|
||||||
|
def set_version(self, version):
|
||||||
|
"""Set version."""
|
||||||
|
self.version = version
|
||||||
|
|
||||||
|
|
||||||
|
def get_version(self):
|
||||||
|
"""Get version."""
|
||||||
|
return self.version
|
||||||
|
|
||||||
|
|
||||||
|
def set_desc(self, desc):
|
||||||
|
"""Set description."""
|
||||||
|
self.desc = desc
|
||||||
|
|
||||||
|
|
||||||
|
def get_desc(self):
|
||||||
|
"""Get description."""
|
||||||
|
return self.desc
|
||||||
|
|
||||||
|
|
||||||
|
def set_csize(self, csize):
|
||||||
|
"""Set csize."""
|
||||||
|
self.csize = csize
|
||||||
|
|
||||||
|
|
||||||
|
def get_csize(self):
|
||||||
|
"""Get csize."""
|
||||||
|
return self.csize
|
||||||
|
|
||||||
|
|
||||||
|
def set_isize(self, isize):
|
||||||
|
"""Set isize."""
|
||||||
|
self.isize = isize
|
||||||
|
|
||||||
|
|
||||||
|
def get_isize(self):
|
||||||
|
"""Get isize."""
|
||||||
|
return self.isize
|
||||||
|
|
||||||
|
|
||||||
|
def set_url(self, url):
|
||||||
|
"""Set upstream URL."""
|
||||||
|
self.url = url
|
||||||
|
|
||||||
|
|
||||||
|
def get_url(self):
|
||||||
|
"""Get upstream URL."""
|
||||||
|
return self.url
|
||||||
|
|
||||||
|
|
||||||
|
def set_license(self, license):
|
||||||
|
"""Set license."""
|
||||||
|
self.license = license
|
||||||
|
|
||||||
|
|
||||||
|
def get_license(self):
|
||||||
|
"""Get license."""
|
||||||
|
return self.license
|
||||||
|
|
||||||
|
|
||||||
|
def set_arch(self, arch):
|
||||||
|
"""Set architecture."""
|
||||||
|
self.arch = arch
|
||||||
|
|
||||||
|
|
||||||
|
def get_arch(self):
|
||||||
|
"""Get architecture."""
|
||||||
|
return self.arch
|
||||||
|
|
||||||
|
|
||||||
|
def set_builddate(self, builddate):
|
||||||
|
"""Set build date."""
|
||||||
|
self.builddate = builddate
|
||||||
|
|
||||||
|
|
||||||
|
def get_builddate(self):
|
||||||
|
"""Get build date."""
|
||||||
|
return self.builddate
|
||||||
|
|
||||||
|
|
||||||
|
def set_packager(self, packager):
|
||||||
|
"""Set name of packager."""
|
||||||
|
self.packager = packager
|
||||||
|
|
||||||
|
|
||||||
|
def get_packager(self):
|
||||||
|
"""Get name of packager."""
|
||||||
|
return self.packager
|
82
aur-check.py
Executable file
82
aur-check.py
Executable file
|
@ -0,0 +1,82 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from arch import ArchDatabase, ArchPackage
|
||||||
|
from aur import AURPackage
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Styling:
|
||||||
|
"""Output styling constants."""
|
||||||
|
ENDC = '\033[0m'
|
||||||
|
BOLD = '\033[1m'
|
||||||
|
UPTODATE = '\033[32m'
|
||||||
|
NEEDS_UPDATE = '\033[31m'
|
||||||
|
FLAGGED = '\033[33m'
|
||||||
|
NEEDS_DOWNGRADE = '\033[34m'
|
||||||
|
URL = '\033[37m'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class AURChecker:
|
||||||
|
"""
|
||||||
|
Read local Arch Linux package databases/repositories and compare the
|
||||||
|
version of each containing package against the AUR package with the same
|
||||||
|
name to determine and print a status for each package.
|
||||||
|
"""
|
||||||
|
STATUS_UPTODATE = "uptodate"
|
||||||
|
STATUS_NEEDS_UPDATE = "needs update"
|
||||||
|
STATUS_NEEDS_DOWNGRADE = "needs downgrade"
|
||||||
|
|
||||||
|
|
||||||
|
def check(directory):
|
||||||
|
"""Check all databases/repositories in a directory."""
|
||||||
|
databases = ArchDatabase.find_databases(directory)
|
||||||
|
for database in databases:
|
||||||
|
AURChecker.check_database(database)
|
||||||
|
|
||||||
|
|
||||||
|
def check_database(database):
|
||||||
|
"""Check a database/repository."""
|
||||||
|
print(Styling.BOLD + "# repository {}".format(database.get_name()) + Styling.ENDC)
|
||||||
|
for package in database.get_packages():
|
||||||
|
aur_package = AURPackage(package.get_name())
|
||||||
|
status = AURChecker.compare(package, aur_package)
|
||||||
|
|
||||||
|
status_messages = {}
|
||||||
|
status_messages[AURChecker.STATUS_UPTODATE] = Styling.UPTODATE + "up-do-date" + Styling.ENDC
|
||||||
|
status_messages[AURChecker.STATUS_NEEDS_UPDATE] = Styling.NEEDS_UPDATE + "needs update to {}\n".format(aur_package.get_version()) + Styling.URL + " {}{}".format(AURPackage.AUR_URL, aur_package.get_url_path()) + Styling.ENDC
|
||||||
|
status_messages[AURChecker.STATUS_NEEDS_DOWNGRADE] = Styling.NEEDS_DOWNGRADE + "local is newer" + Styling.ENDC
|
||||||
|
|
||||||
|
message = " – {} {}: {}".format(package.get_name(), package.get_version(), status_messages[status])
|
||||||
|
if aur_package.get_out_of_date():
|
||||||
|
message = Styling.FLAGGED + "{} (flagged)".format(message) + Styling.ENDC
|
||||||
|
print(message)
|
||||||
|
|
||||||
|
|
||||||
|
def compare(package, aur_package):
|
||||||
|
"""Compare package two versions and return status."""
|
||||||
|
result = subprocess.check_output(["vercmp", package.get_version(), aur_package.get_version()])
|
||||||
|
result = int(result)
|
||||||
|
if result < 0:
|
||||||
|
return AURChecker.STATUS_NEEDS_UPDATE
|
||||||
|
elif result > 0:
|
||||||
|
return AURChecker.STATUS_NEEDS_DOWNGRADE
|
||||||
|
else:
|
||||||
|
return AURChecker.STATUS_UPTODATE
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser("Read local Arch Linux package databases/repositories and compare the version of each containing package against the AUR package with the same name to determine and print a status for each package.")
|
||||||
|
parser.add_argument('folder', help="source folder containing one or several databases/repositories (subdirectories possible)")
|
||||||
|
args = parser.parse_args()
|
||||||
|
AURChecker.check(args.folder)
|
118
aur.py
Normal file
118
aur.py
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
|
import certifi
|
||||||
|
import json
|
||||||
|
import urllib3
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class AURPackage:
|
||||||
|
"""Representation of an Arch Linux User Repsitory (AUR) package."""
|
||||||
|
"""URL of the [AUR] website"""
|
||||||
|
AUR_URL = 'https://aur.archlinux.org'
|
||||||
|
"""URL-path for packages"""
|
||||||
|
PACKAGE_PATH = 'packages'
|
||||||
|
"""URL-path of RPC"""
|
||||||
|
RPC_PATH = 'rpc.php'
|
||||||
|
"""Parameters of RPC"""
|
||||||
|
RPC_PARAMS = 'type=info&arg='
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, name):
|
||||||
|
"""Construct a new AUR package representation."""
|
||||||
|
self.name = name
|
||||||
|
self.version = None
|
||||||
|
self.desc = None
|
||||||
|
self.url = None
|
||||||
|
self.license = None
|
||||||
|
self.maintainer = None
|
||||||
|
self.votes = None
|
||||||
|
self.url_path = None
|
||||||
|
self.package_base = None
|
||||||
|
self.out_of_date = None
|
||||||
|
self.last_modified = None
|
||||||
|
self._load(name)
|
||||||
|
|
||||||
|
|
||||||
|
def _load(self, name):
|
||||||
|
"""Load package via API."""
|
||||||
|
# Construct URL
|
||||||
|
url = "{}/{}?{}{}".format(AURPackage.AUR_URL, AURPackage.RPC_PATH, AURPackage.RPC_PARAMS, name)
|
||||||
|
# Call API via https
|
||||||
|
https = urllib3.PoolManager(ca_certs=certifi.where())
|
||||||
|
# Get and parse response
|
||||||
|
response = https.request('GET', url)
|
||||||
|
if response.status == 200:
|
||||||
|
data = json.loads(response.data.decode('utf-8'))
|
||||||
|
if data['resultcount'] > 0:
|
||||||
|
values = data['results']
|
||||||
|
self.version = values['Version']
|
||||||
|
self.desc = values['Description']
|
||||||
|
self.url = values['URL']
|
||||||
|
self.license = values['License']
|
||||||
|
self.maintainer = values['Maintainer']
|
||||||
|
self.votes = values['NumVotes']
|
||||||
|
self.url_path = values['URLPath']
|
||||||
|
self.package_base = values['PackageBase']
|
||||||
|
self.out_of_date = values['OutOfDate']
|
||||||
|
self.last_modified = values['LastModified']
|
||||||
|
else:
|
||||||
|
print("error:", response.status)
|
||||||
|
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
"""Get name."""
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
def get_version(self):
|
||||||
|
"""Get version."""
|
||||||
|
return self.version
|
||||||
|
|
||||||
|
|
||||||
|
def get_desc(self):
|
||||||
|
"""Get description."""
|
||||||
|
return self.desc
|
||||||
|
|
||||||
|
|
||||||
|
def get_url(self):
|
||||||
|
"""Get URL."""
|
||||||
|
return self.url
|
||||||
|
|
||||||
|
|
||||||
|
def get_license(self):
|
||||||
|
"""Get license."""
|
||||||
|
return self.license
|
||||||
|
|
||||||
|
|
||||||
|
def get_maintainer(self):
|
||||||
|
"""Get maintainer."""
|
||||||
|
return self.maintainer
|
||||||
|
|
||||||
|
|
||||||
|
def get_votes(self):
|
||||||
|
"""Get number of votes."""
|
||||||
|
return self.votes
|
||||||
|
|
||||||
|
|
||||||
|
def get_url_path(self):
|
||||||
|
"""Get URL path."""
|
||||||
|
return self.url_path
|
||||||
|
|
||||||
|
|
||||||
|
def get_package_base(self):
|
||||||
|
"""Get package base."""
|
||||||
|
return self.package_base
|
||||||
|
|
||||||
|
|
||||||
|
def get_out_of_date(self):
|
||||||
|
"""Get date the package was flagged as out-of-date."""
|
||||||
|
return self.out_of_date
|
||||||
|
|
||||||
|
|
||||||
|
def get_last_modified(self):
|
||||||
|
"""Get date of last modification."""
|
||||||
|
return self.last_modified
|
Loading…
Reference in a new issue