# -*- encoding: utf-8 -*-
# PKGBUILDer v4.3.2
# An AUR helper (and library) in Python 3.
# Copyright © 2011-2023, Chris Warrick.
# See /LICENSE for licensing information.
"""
Tools for performing upgrades of AUR packages.
:Copyright: © 2011-2023, Chris Warrick.
:License: BSD (see /LICENSE).
"""
from . import DS, _
import pkgbuilder.build
import pkgbuilder.ui
import pkgbuilder.utils
import pyalpm
import datetime
__all__ = ('gather_foreign_pkgs', 'list_upgradable', 'auto_upgrade')
[docs]def gather_foreign_pkgs():
"""Gather a list of all foreign packages."""
localdb = DS.pyc.get_localdb()
# Based on paconky.py.
installed = [p for p in localdb.pkgcache]
repo = []
aur = []
syncdbs = DS.pyc.get_syncdbs()
for sdb in syncdbs:
for ipkg in installed:
if sdb.get_pkg(ipkg.name):
repo.append(ipkg)
aur = set(set(installed) - set(repo))
# Return foreign packages.
return dict([(p.name, p) for p in aur])
[docs]def list_upgradable(pkglist, vcsup=False, aurcache=None, ignorelist=None):
"""Compare package versions and returns upgradable ones.
.. versionchanged:: 4.2.9
"""
localdb = DS.pyc.get_localdb()
if ignorelist is None:
ignorelist = []
if aurcache:
aurlist = aurcache
else:
aurlist = pkgbuilder.utils.info(pkglist)
# It’s THAT easy. Oh, and by the way: it is much, MUCH faster than
# others. It makes only a handful of multiinfo requests (1-2 on most
# systems) rather than len(installed_packages) info requests.
upgradable = []
downgradable = []
ignored = []
for rpkg in aurlist:
lpkg = localdb.get_pkg(rpkg.name)
if lpkg is not None:
vc = pyalpm.vercmp(rpkg.version, lpkg.version)
if vc > 0 and rpkg.name not in ignorelist:
upgradable.append([rpkg.name, lpkg.version, rpkg.version])
elif vc > 0 and rpkg.name in ignorelist:
DS.log.warning("{0} ignored for upgrade.".format(rpkg.name))
ignored.append([rpkg.name, lpkg.version, rpkg.version])
elif vc < 0:
# If the package version is a date or the name ends in
# -{git,hg,bzr,svn,cvs,darcs}, do not mark it as downgradable.
# BTW: the above is yours truly’s list of VCS preference, if
# you added big a gap between git and hg and then HUGE gaps
# between everything else.
try:
# For epoch packages. Also, cheating here.
v = rpkg.version.split(':')[1]
except IndexError:
v = rpkg.version
try:
datetime.datetime.strptime(v.split('-')[0], '%Y%m%d')
datever = True
except Exception:
datever = False
dt = datetime.date.today().strftime('%Y%m%d-1')
if (rpkg.name.endswith(('git', 'hg', 'bzr', 'svn', 'cvs',
'darcs'))):
if vcsup and rpkg.name not in ignorelist:
upgradable.append([rpkg.name, lpkg.version, dt])
elif rpkg.name in ignorelist:
DS.log.warning(
"{0} ignored for upgrade/downgrade.".format(
rpkg.name))
else:
DS.log.warning('{0} is -[vcs], ignored for '
'downgrade.'.format(rpkg.name))
elif datever:
if vcsup and rpkg.name not in ignorelist:
upgradable.append([rpkg.name, lpkg.version, dt])
elif rpkg.name in ignorelist:
DS.log.warning(
"{0} ignored for upgrade/downgrade.".format(
rpkg.name))
else:
DS.log.warning('{0} version is a date, ignored '
'for downgrade.'.format(rpkg.name))
else:
downgradable.append([rpkg.name, lpkg.version,
rpkg.version])
return [upgradable, downgradable, ignored]
[docs]def auto_upgrade(downgrade=False, vcsup=False, fetchonly=False,
ignorelist=None):
"""
Human friendly upgrade question and output.
Returns packages — should be passed over to builder functions.
"""
DS.log.info('Ran auto_upgrade.')
print(':: ' + _('Synchronizing package databases...'))
foreign = gather_foreign_pkgs()
upgradable, downgradable, ignored = list_upgradable(
foreign.keys(), vcsup, ignorelist=ignorelist)
print(':: ' + _('Starting full system upgrade...'))
for i in ignored:
print(_("warning: {0}: ignoring package upgrade ({1} => {2})").format(
*i))
if downgradable:
for i in downgradable:
if downgrade:
msg = _('warning: {0}: downgrading from version {1} '
'to version {2}').format(*i)
else:
msg = _('warning: {0}: local ({1}) is newer than aur '
'({2})').format(*i)
print(msg)
if downgrade:
upgradable = upgradable + downgradable
if not upgradable:
print(' ' + _('there is nothing to do'))
return []
upgnames = [i[0] for i in upgradable]
upgstrings = [i[0] + '-' + i[2] for i in upgradable]
verbosepkglists = DS.config.getboolean('options', 'verbosepkglists')
if upgradable:
targetstring = _('Targets ({0}):').format(len(upgradable)) + ' '
termwidth = pkgbuilder.ui.get_termwidth()
if termwidth is None and verbosepkglists:
# Pacman doesn’t allow tables if the terminal is too small.
# And since we don’t know the size, better safe than sorry.
verbosepkglists = False
DS.log.warning('VerbosePkgLists disabled, cannot '
'determine terminal width')
termwidth = termwidth or 9001
if verbosepkglists:
headers = [_('Name'), _('Old Version'), _('New Version')]
items = upgradable # Magical.
sizes = [len(i) for i in headers]
for n, ov, nv in items:
if len(n) > sizes[0]:
sizes[0] = len(n)
if len(ov) > sizes[1]:
sizes[1] = len(ov)
if len(nv) > sizes[2]:
sizes[2] = len(nv)
fstring = ('{{i[0]:<{s[0]}}} {{i[1]:<{s[1]}}} '
'{{i[2]:<{s[2]}}}').format(s=sizes)
if len(fstring.format(i=4 * ['n'])) > termwidth:
verbosepkglists = False
DS.log.warning('VerbosePkgLists disabled, terminal is '
'not wide enough')
# string stolen from pacman
print(_('warning: insufficient columns available for '
'table display'))
else:
print('\n{0}\n'.format(targetstring.strip()))
print(fstring.format(i=headers))
print()
for i in items:
print(fstring.format(i=i))
# Not using else because there is a fallback if the terminal
# is too small.
if not verbosepkglists:
print(pkgbuilder.ui.hanging_indent(
' '.join(upgstrings), targetstring, termwidth, True))
print()
if fetchonly:
query = ':: ' + _('Fetch the packages? [Y/n] ')
else:
query = ':: ' + _('Proceed with installation? [Y/n] ')
if DS.confirm:
yesno = input(query)
if yesno.lower().strip().startswith('y') or yesno.strip() == '':
return upgnames
else:
return []
else:
# Print the query and then return immediately, pacman does that too
print(query)
return upgnames