[calm - Cygwin server-side packaging maintenance script] branch master, updated. 20230209-111-gb586495
Jon Turney
jturney@sourceware.org
Wed May 29 15:12:43 GMT 2024
https://sourceware.org/git/gitweb.cgi?p=cygwin-apps/calm.git;h=b5864958558c98ed95f831a9276f3cd33eb8998f
commit b5864958558c98ed95f831a9276f3cd33eb8998f
Author: Jon Turney <jon.turney@dronecode.org.uk>
Date: Mon May 27 15:19:00 2024 +0100
Produce a report to help identify long-inactive maintainers
Diff:
---
calm/maintainers.py | 9 ++++
calm/pkg2html.py | 5 ++-
calm/reports.py | 70 +++++++++++++++++++++++++++++-
test/testdata/process_arch/htdocs.expected | 7 ++-
4 files changed, 88 insertions(+), 3 deletions(-)
diff --git a/calm/maintainers.py b/calm/maintainers.py
index e2c60c7..2a02dc0 100644
--- a/calm/maintainers.py
+++ b/calm/maintainers.py
@@ -33,6 +33,7 @@
# - an email address (in HOME/!email (or !mail), as we don't want to publish
# it, and want to allow the maintainer to change it)
# - the timestamp when 'ignoring' warnings were last emitted
+# - the timestamp of their last ssh connection
#
import logging
@@ -84,6 +85,7 @@ class Maintainer(object):
self.email = email
self.pkgs = pkgs
self.quiet = False
+ self.has_homedir = os.path.isdir(self.homedir())
# the mtime of this file records the timestamp
reminder_file = os.path.join(self.homedir(), '!reminder-timestamp')
@@ -94,6 +96,13 @@ class Maintainer(object):
self.reminders_issued = False
self.reminders_timestamp_checked = False
+ # the mtime of this file records the last ssh session
+ last_seen_file = os.path.join(self.homedir(), '.last-seen')
+ if os.path.isfile(last_seen_file):
+ self.last_seen = os.path.getmtime(last_seen_file)
+ else:
+ self.last_seen = 0 # meaning 'unknown'
+
def __repr__(self):
return "maintainers.Maintainer('%s', %s, %s)" % (self.name, self.email, self.pkgs)
diff --git a/calm/pkg2html.py b/calm/pkg2html.py
index 979914b..ab7591a 100755
--- a/calm/pkg2html.py
+++ b/calm/pkg2html.py
@@ -136,7 +136,10 @@ 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))
+ if ts == 0:
+ return 'Unknown'
+ else:
+ return time.strftime('%Y-%m-%d %H:%M', time.gmtime(ts))
#
diff --git a/calm/reports.py b/calm/reports.py
index 02e9082..240b5d9 100644
--- a/calm/reports.py
+++ b/calm/reports.py
@@ -69,7 +69,7 @@ def write_report(args, title, body, fn, reportlist, not_empty=True):
def linkify(pn, po):
- return '<a href="/packages/summary/{0}.html">{1}</a>'.format(pn, po.orig_name)
+ return '<a href="/packages/summary/{0}.html">{1}</a>'.format(po.name, po.orig_name)
#
@@ -281,6 +281,72 @@ def unstable(args, packages, reportlist):
write_report(args, 'Packages marked as unstable', body, 'unstable.html', reportlist)
+# produce a report on maintainer (in)activity
+#
+def maintainer_activity(args, packages, reportlist):
+ activity_list = []
+
+ arch = 'x86_64'
+ # XXX: look into how we can make this 'src', after x86 is dropped
+
+ ml = maintainers.maintainer_list(args)
+ for m in ml.values():
+ if m.name == 'ORPHANED':
+ continue
+
+ a = types.SimpleNamespace()
+ a.name = m.name
+ a.last_seen = m.last_seen
+
+ count = 0
+ mtime = 0
+ pkgs = []
+ for p in m.pkgs:
+ if not p.is_orphaned():
+ count += 1
+
+ pn = p.data + '-src'
+ # do something reasonable for sourceless packages
+ if pn not in packages[arch]:
+ pn = p.data
+
+ po = packages[arch].get(pn, None)
+ if po:
+ pkgs.append(pn)
+
+ for v in po.versions():
+ if po.tar(v).mtime > mtime:
+ mtime = po.tar(v).mtime
+
+ # ignore if all their packages are orphaned
+ # (key should be already disabled in this case)
+ if count == 0:
+ continue
+
+ a.count = count
+ a.pkgs = pkgs
+ a.last_package = mtime
+
+ activity_list.append(a)
+
+ body = io.StringIO()
+ print('<p>Maintainer activity.</p>', file=body)
+
+ print('<table class="grid sortable">', file=body)
+ print('<tr><th>Maintainer</th><th># packages</th><th>Last ssh</th><th>Latest package</th></tr>', file=body)
+
+ for a in sorted(activity_list, key=lambda i: (i.last_seen, i.last_package)):
+ def pkg_details(pkgs):
+ return '<details><summary>%d</summary>%s</details>' % (len(pkgs), ', '.join(linkify(p, packages[arch][p]) for p in pkgs))
+
+ print('<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>' %
+ (a.name, pkg_details(a.pkgs), pkg2html.tsformat(a.last_seen), pkg2html.tsformat(a.last_package)), file=body)
+
+ print('</table>', file=body)
+
+ write_report(args, 'Maintainer activity', body, 'maintainer_activity.html', reportlist, not_empty=False)
+
+
# produce a report of packages which need rebuilding for the latest major
# version version provides
#
@@ -463,6 +529,8 @@ def do_reports(args, packages):
provides_rebuild(args, packages, 'ruby_rebuilds.html', 'ruby', reportlist)
python_rebuild(args, packages, 'python_rebuilds.html', reportlist)
+ maintainer_activity(args, packages, reportlist)
+
fn = os.path.join(args.htdocs, 'reports_list.inc')
with utils.open_amifc(fn) as f:
print('<ul>', file=f)
diff --git a/test/testdata/process_arch/htdocs.expected b/test/testdata/process_arch/htdocs.expected
index 80e2fdf..cb93982 100644
--- a/test/testdata/process_arch/htdocs.expected
+++ b/test/testdata/process_arch/htdocs.expected
@@ -1,5 +1,10 @@
{'.': ['calm.db', 'packages.inc', 'reports_list.inc', 'src_packages.inc'],
- 'reports': ['deprecated_so.html', 'perl_rebuilds.html', 'ruby_rebuilds.html', 'unmaintained.html', 'unstable.html'],
+ 'reports': ['deprecated_so.html',
+ 'maintainer_activity.html',
+ 'perl_rebuilds.html',
+ 'ruby_rebuilds.html',
+ 'unmaintained.html',
+ 'unstable.html'],
'summary': ['arc-src.html',
'arc.html',
'base-cygwin.html',
More information about the Cygwin-apps-cvs
mailing list