Welcome to mirror list, hosted at ThFree Co, Russian Federation.

cygwin.com/git/cygwin-apps/calm.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJon Turney <jon.turney@dronecode.org.uk>2022-01-29 21:11:46 +0300
committerJon Turney <jon.turney@dronecode.org.uk>2022-02-03 16:28:12 +0300
commit2677eb4380ec544335aec40b71d12d723e76195d (patch)
tree22ab9485b8d273e90bf6d3c1fc2ec361d6dc97a4
parent88b57728790ae1556d545697c15f315dfa182e75 (diff)
Add 'unmaintained packages' report
-rwxr-xr-xcalm/calm.py14
-rwxr-xr-xcalm/package.py20
-rwxr-xr-xcalm/pkg2html.py9
-rw-r--r--calm/reports.py130
4 files changed, 167 insertions, 6 deletions
diff --git a/calm/calm.py b/calm/calm.py
index cfc05dd..2502997 100755
--- a/calm/calm.py
+++ b/calm/calm.py
@@ -71,6 +71,7 @@ from . import irk
from . import maintainers
from . import package
from . import pkg2html
+from . import reports
from . import setup_exe
from . import uploads
from . import utils
@@ -382,10 +383,6 @@ def do_output(args, state):
# update packages listings
# XXX: perhaps we need a --[no]listing command line option to disable this from being run?
pkg2html.update_package_listings(args, state.packages)
- # if we are daemonized, allow force regeneration of static content in htdocs
- # initially (in case the generation code has changed), but update that
- # static content only as needed on subsequent loops
- args.force = 0
update_json = False
@@ -482,6 +479,15 @@ def do_output(args, state):
except (OSError):
pass
+ # write reports
+ if update_json or args.force:
+ reports.do_reports(args, state.packages)
+
+ # if we are daemonized, allow force regeneration of static content in htdocs
+ # initially (in case the generation code has changed), but update that
+ # static content only as needed on subsequent loops
+ args.force = 0
+
#
# daemonization loop
diff --git a/calm/package.py b/calm/package.py
index a6b20af..9fd734c 100755
--- a/calm/package.py
+++ b/calm/package.py
@@ -495,8 +495,10 @@ def validate_packages(args, packages):
for hints in packages[p].version_hints.values():
valid_requires.update(hints.get('provides', '').split())
- # reset obsolete:d by some other package state
+ # reset computed package state
packages[p].obsolete = False
+ packages[p].rdepends = set()
+ packages[p].build_rdepends = set()
# perform various package validations
for p in sorted(packages.keys()):
@@ -762,6 +764,22 @@ def validate_packages(args, packages):
lvl = logging.ERROR
logging.log(lvl, "package '%s' version '%s' has empty source tar file" % (p, vr))
+ # build the set of packages which depends: on this package (rdepends), and
+ # the set of packages which build-depends: on it (build_rdepends)
+ for p in packages:
+ for hints in packages[p].version_hints.values():
+ for k, a in [
+ ('depends', 'rdepends'),
+ ('build-depends', 'build_rdepends')
+ ]:
+ if k in hints:
+ dpl = hints[k].split(',')
+ for dp in dpl:
+ dp = dp.strip()
+ dp = re.sub(r'(.*)\s+\(.*\)', r'\1', dp)
+ if dp in packages:
+ getattr(packages[dp], a).add(p)
+
# make another pass to verify a source tarfile exists for every install
# tarfile version
for p in packages.keys():
diff --git a/calm/pkg2html.py b/calm/pkg2html.py
index b43ed71..b2080dc 100755
--- a/calm/pkg2html.py
+++ b/calm/pkg2html.py
@@ -120,6 +120,13 @@ def ensure_dir_exists(args, path):
#
+# format a unix epoch time (UTC)
+#
+def tsformat(ts):
+ return time.strftime('%Y-%m-%d %H:%M', time.gmtime(ts))
+
+
+#
#
#
@@ -280,7 +287,7 @@ def update_package_listings(args, packages):
name = v + ' (source)'
target = "%s-%s-src" % (p.orig_name, v)
test = 'test' if 'test' in p.version_hints[v] else 'stable'
- ts = time.strftime('%Y-%m-%d %H:%M', time.gmtime(p.tar(v).mtime))
+ ts = tsformat(p.tar(v).mtime)
print('<tr><td>%s</td><td class="right">%d KiB</td><td>%s</td><td>[<a href="../%s/%s/%s">list of files</a>]</td><td>%s</td></tr>' % (name, size, ts, arch, pn, target, test), file=f)
for version in sorted(packages[arch][p].versions(), key=lambda v: SetupVersion(v)):
diff --git a/calm/reports.py b/calm/reports.py
new file mode 100644
index 0000000..2b7020f
--- /dev/null
+++ b/calm/reports.py
@@ -0,0 +1,130 @@
+#!/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 textwrap
+import types
+
+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('''\
+ <!DOCTYPE html>
+ <html>
+ <head>
+ <link rel="stylesheet" type="text/css" href="/style.css"/>
+ <title>{0}</title>
+ </head>
+ <body>
+ <div id="main">
+ <h1>{0}</h1>''').format(title), file=f)
+
+ print(body, file=f)
+
+ print(textwrap.dedent('''\
+ </div>
+ </body>
+ </html>'''), file=f)
+
+
+def linkify(pn, po):
+ return '<a href="/packages/summary/{0}.html">{1}</a>'.format(pn, po.orig_name)
+
+
+#
+# produce a report of unmaintained packages
+#
+def unmaintained(args, packages, reportsdir):
+ mlist = maintainers.read(args, None)
+ pkg_maintainers = maintainers.invert(mlist)
+
+ 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 'ORPHANED' not in pkg_maintainers[po.orig_name]:
+ 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 = v
+ up.ts = po.tar(v).mtime
+ up.rdepends = len(rdepends)
+ up.build_rdepends = len(build_rdepends)
+
+ um_list.append(up)
+
+ body = io.StringIO()
+ print('<p>Packages without a maintainer.</p>', file=body)
+
+ print('<table class="grid">', file=body)
+ print('<tr><th>last updated</th><th>package</th><th>version</th><th>rdepends</th><th>build_rdepends</th></tr>', file=body)
+
+ for up in sorted(um_list, key=lambda i: (i.rdepends + i.build_rdepends, i.ts), reverse=True):
+ print('<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>' %
+ (pkg2html.tsformat(up.ts), linkify(up.pn, up.po), up.v, up.rdepends, up.build_rdepends), file=body)
+
+ print('</table>', file=body)
+
+ unmaintained = os.path.join(reportsdir, 'unmaintained.html')
+ with utils.open_amifc(unmaintained) as f:
+ template('Unmaintained packages', body.getvalue(), f)
+
+
+def do_reports(args, packages):
+ if args.dryrun:
+ return
+
+ reportsdir = os.path.join(args.htdocs, 'reports')
+ pkg2html.ensure_dir_exists(args, reportsdir)
+
+ unmaintained(args, packages, reportsdir)