#!/usr/bin/env python3
#
# Copyright (c) 2022 Jon Turney
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
import io
import os
import re
import textwrap
import types
from . import common_constants
from . import maintainers
from . import package
from . import pkg2html
from . import utils
from .version import SetupVersion
def template(title, body, f):
os.fchmod(f.fileno(), 0o755)
print(textwrap.dedent('''\
'''), file=f)
def write_report(args, title, body, fn, reportlist, not_empty=True):
if not_empty:
reportlist[title] = os.path.join('reports', fn)
fn = os.path.join(args.htdocs, 'reports', fn)
with utils.open_amifc(fn) as f:
template(title, body.getvalue(), f)
def linkify(pn, po):
return '{1}'.format(pn, po.orig_name)
#
# produce a report of unmaintained packages
#
def unmaintained(args, packages, reportlist):
pkg_maintainers = maintainers.pkg_list(args.pkglist)
um_list = []
arch = 'x86_64'
# XXX: look into how we can make this 'src', after x86 is dropped
for p in packages[arch]:
po = packages[arch][p]
if po.kind != package.Kind.source:
continue
if (po.orig_name not in pkg_maintainers) or (not pkg_maintainers[po.orig_name].is_orphaned()):
continue
# the highest version we have
v = sorted(po.versions(), key=lambda v: SetupVersion(v), reverse=True)[0]
# determine the number of unique rdepends over all subpackages (and
# likewise build_rdepends)
#
# zero rdepends makes this package a candidate for removal, whereas lots
# means it's important to update it.
rdepends = set()
build_rdepends = set()
for subp in po.is_used_by:
rdepends.update(packages[arch][subp].rdepends)
build_rdepends.update(packages[arch][subp].build_rdepends)
up = types.SimpleNamespace()
up.pn = p
up.po = po
up.v = SetupVersion(v).V
up.upstream_v = getattr(po, 'upstream_version', 'unknown')
up.ts = po.tar(v).mtime
up.rdepends = len(rdepends)
up.build_rdepends = len(build_rdepends)
up.importance = po.importance
# some packages are mature. If 'v' is still latest upstream version,
# then maybe we don't need to worry about this package quite as much...
up.unchanged = (SetupVersion(v)._V == SetupVersion(up.upstream_v)._V)
if up.unchanged:
up.upstream_v += " (unchanged)"
um_list.append(up)
body = io.StringIO()
print('
Packages without a maintainer.
', file=body)
print('
', file=body)
print('
last updated
package
version
upstream version
rdepends
build_rdepends
importance
', file=body)
for up in sorted(um_list, key=lambda i: (-i.importance, i.rdepends + i.build_rdepends, not i.unchanged, i.ts), reverse=True):
print('
', file=body)
write_report(args, 'Unmaintained packages', body, 'unmaintained.html', reportlist)
# produce a report of deprecated packages
#
def deprecated(args, packages, reportlist):
dep_list = []
arch = 'x86_64'
# XXX: look into how we can make this 'src', after x86 is dropped
for p in packages[arch]:
po = packages[arch][p]
if po.kind != package.Kind.binary:
continue
if not re.match(common_constants.SOVERSION_PACKAGE_RE, p):
continue
if p.startswith('girepository-'):
continue
bv = po.best_version
es = po.version_hints[bv].get('external-source', None)
if not es:
continue
if packages[arch][es].best_version == bv:
continue
if po.tar(bv).is_empty:
continue
# an old version of a shared library
depp = types.SimpleNamespace()
depp.pn = p
depp.po = po
depp.v = bv
depp.ts = po.tar(bv).mtime
# number of rdepends which have a different source package
depp.rdepends = len(list(p for p in po.rdepends if packages[arch][p].srcpackage(packages[arch][p].best_version) != es))
dep_list.append(depp)
body = io.StringIO()
print(textwrap.dedent('''\
Packages for old soversions. (The corresponding source package produces a
newer soversion, or has stopped producing this solib).
'''), file=body)
print('
', file=body)
print('
package
version
timestamp
rdepends
', file=body)
for depp in sorted(dep_list, key=lambda i: (i.rdepends, i.ts), reverse=True):
print('
', file=body)
write_report(args, 'Deprecated shared library packages', body, 'deprecated_so.html', reportlist)
# produce a report of packages which need rebuilding for the latest major
# version version provides
#
def provides_rebuild(args, packages, fn, provide_package, reportlist):
pr_list = []
arch = 'x86_64'
# XXX: look into how we can change this, after x86 is dropped
pp_package = packages[arch].get(provide_package, None)
pp_provide = None
if pp_package:
pp_bv = pp_package.best_version
pp_provide = pp_package.version_hints[pp_bv]['provides']
pp_provide_base = re.sub(r'\d+$', '', pp_provide)
for p in packages[arch]:
po = packages[arch][p]
bv = po.best_version
depends = packages[arch][p].version_hints[bv]['depends'].split(', ')
depends = [re.sub(r'(.*) +\(.*\)', r'\1', r) for r in depends]
for d in depends:
if not d.startswith(pp_provide_base):
continue
if d == pp_provide:
continue
if po.obsoleted_by:
continue
# requires an old provide
pr = types.SimpleNamespace()
pr.pn = p
pr.po = po
pr.spn = po.srcpackage(bv)
pr.spo = packages[arch][pr.spn]
pr.depends = d
pr.bv = bv
pr_list.append(pr)
break
body = io.StringIO()
print('
Packages whose latest version depends on a version provides: other than %s.
' % pp_provide, file=body)
print('
', file=body)
print('
package
srcpackage
version
depends
', file=body)
for pr in sorted(pr_list, key=lambda i: (i.depends, i.pn)):
print('