Last active
September 17, 2024 19:44
-
-
Save digitaltrails/094cd5a5d08670560c8bb199041321a6 to your computer and use it in GitHub Desktop.
Zypper history summarise rpm changes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# | |
# Usage: sudo zypphist.py [options] | |
# | |
# Report change log entries for recent zypper installs. | |
# | |
# Options: | |
# -h, --help show this help message and exit | |
# -i ZYPP_INSTALL_DAYS, --installed-since=ZYPP_INSTALL_DAYS | |
# Include anything zypper installed up to the specified | |
# days ago (default 1). | |
# -c CHANGE_ENTRIES, --change-entries | |
# For matched zypper installs, report a given number | |
# of dated RPM change-log entries (default 1). | |
# -g, --group group packages by change-log text (for a minimum sized report) | |
# -s, --group-by-source group packages by source-rpm and repository | |
# | |
# 2024-09-16 0900 Cleanup and made slightly more pythonic. | |
# 2024-09-16 1302 Use source-rpm, repo-name as the key for grouping. | |
# | |
# Copyright (C) 2011: Michael Hamilton | |
# The code is GPL 3.0(GNU General Public License) ( http://www.gnu.org/copyleft/gpl.html ) | |
# | |
from datetime import datetime, timedelta | |
from enum import Enum | |
from optparse import OptionParser | |
import csv | |
import rpm | |
from fontTools.misc.bezierTools import namedtuple | |
DEFAULT_ZYPP_DAYS = 1 | |
DEFAULT_RPM_CHANGE_ENTRIES = 1 | |
ZYPP_HISTORY_FILENAME = '/var/log/zypp/history' | |
class GroupBy(Enum): | |
CHANGE_LOG = 1 | |
SRC_AND_REPO = 2 | |
PACKAGE_HEADER_START = '==================================================' | |
PACKAGE_HEADER_END = '------------------------------' | |
SrcGroupKey = namedtuple('SrcGroupKey', ['source_rpm', 'repo_name']) | |
def print_package_line(install_date, package_name, package_version, repo_name): | |
print(f'+Package: {package_name: <30} {package_version} {install_date} {repo_name}') | |
def recent_changelog(package_name, package_version_and_release, full_log_text, number_of_entries): | |
if full_log_text is None: | |
return f"Removed: {package_name} {package_version_and_release} is no longer installed." | |
extracted_text = '' | |
if len(full_log_text): | |
package_change_entry_count = 0 | |
for line in full_log_text[0].splitlines(): | |
try: | |
if len(line) > 1 and line[0] == '*' and line[1] == ' ' and len(line) > 17: | |
_ = datetime.strptime(line[6:17], '%b %d %Y') | |
package_change_entry_count += 1 | |
if package_change_entry_count > number_of_entries: | |
break | |
except ValueError: | |
pass # not a date - move on | |
extracted_text += line + '\n' | |
return extracted_text | |
if __name__ == '__main__': | |
optParser = OptionParser(description='Report change log entries for recent zypper installs.') | |
optParser.add_option('-i', '--installed-since', dest='N', type='int', | |
default=DEFAULT_ZYPP_DAYS, | |
help=f'Include anything zypper installed up to the specified days ago' | |
f'(default {DEFAULT_ZYPP_DAYS}).') | |
optParser.add_option('-c', '--change-entries', dest='M', type='int', | |
default=DEFAULT_RPM_CHANGE_ENTRIES, | |
help=f'For matched zypper installs, report a given number of dated RPM' | |
f' change-log entries (default {DEFAULT_RPM_CHANGE_ENTRIES}).') | |
optParser.add_option("-g", "--group", | |
action="store_true", dest="USE_GROUPS", default=False, | |
help="group packages with duplicate change-log entries") | |
optParser.add_option("-s", "--group-by-source", | |
action="store_true", dest="USE_SOURCE_GROUPS", default=False, | |
help="group packages from same source rpm") | |
(options, args) = optParser.parse_args() | |
installedSince = datetime.now() - timedelta(days=options.N) | |
changelog_limit = options.M | |
group_packages = options.USE_GROUPS or options.USE_SOURCE_GROUPS | |
group_by_source = options.USE_SOURCE_GROUPS | |
zypHistReader = csv.reader(open(ZYPP_HISTORY_FILENAME, 'r'), delimiter='|') | |
rpms_by_group = {} | |
changelogs_by_group = {} | |
for historyRec in zypHistReader: | |
if historyRec[0][0] != '#' and historyRec[1] == 'install': | |
install_date = datetime.strptime(historyRec[0], '%Y-%m-%d %H:%M:%S') | |
if install_date >= installedSince: | |
package_name, package_version_and_release, repo_name = historyRec[2], historyRec[3], historyRec[6] | |
if not group_packages: | |
print(PACKAGE_HEADER_START) | |
print_package_line(install_date, package_name, package_version_and_release, repo_name) | |
print(PACKAGE_HEADER_END) | |
source_rpm = f'unknown source package for {package_name}' | |
rpm_changelog = None | |
rpm_transaction_set = rpm.TransactionSet() | |
rpm_match_inter = rpm_transaction_set.dbMatch('name', package_name) | |
for rpm_record in rpm_match_inter: | |
rpm_version_and_release = f"{rpm_record[rpm.RPMTAG_VERSION]}-{rpm_record[rpm.RPMTAG_RELEASE]}" | |
if rpm_version_and_release == package_version_and_release: | |
source_rpm = rpm_record[rpm.RPMTAG_SOURCERPM] | |
rpm_changelog = rpm_record[rpm.RPMTAG_CHANGELOGTEXT] | |
break | |
rpm_transaction_set.clear() | |
if group_packages: | |
group_key = SrcGroupKey(source_rpm, repo_name) if group_by_source else recent_changelog( | |
package_name, package_version_and_release, rpm_changelog, changelog_limit) | |
if group_key not in rpms_by_group: | |
rpms_by_group[group_key] = [] | |
log_extract = recent_changelog(package_name, package_version_and_release, rpm_changelog, | |
changelog_limit) if group_by_source else group_key | |
changelogs_by_group[group_key] = log_extract | |
rpms_by_group[group_key] += [(package_name, install_date, package_version_and_release)] | |
else: | |
print(recent_changelog(package_name, package_version_and_release, | |
rpm_changelog, changelog_limit), end='') | |
if group_packages: | |
for group_key, packages in rpms_by_group.items(): | |
print(PACKAGE_HEADER_START) | |
for package_name, install_date, package_version_and_release in packages: | |
print_package_line(install_date, package_name, package_version_and_release, '') | |
print(f'+Source: {group_key.source_rpm: <30} {group_key.repo_name}') if group_by_source else None | |
print(PACKAGE_HEADER_END) | |
print(changelogs_by_group[group_key]) |
Now uses the python-rpm bindings
When grouping (-g option), the script now groups by (source-package, repo-name) and reports these as well, for example:
==================================================
+Package: spectacle 24.08.1-1.1 2024-09-16 12:09:36
+Package: spectacle-lang 24.08.1-1.1 2024-09-16 12:09:37
+Source: spectacle-24.08.1-1.1.src.rpm download.opensuse.org-oss
------------------------------
- Update to 24.08.1
* New bugfix release
* For more details please see:
* https://kde.org/announcements/gear/24.08.1/
- Changes since 24.08.0:
* Set fallback icon theme (kde#492824)
* Fix InlineMessage headers/appearance on "no screenshot" dialog
* Set suggested filename for copied images
* Go back to copying images with setImageData (kde#485096)
Added options for how to group
-g, --group group packages by change-log text (for a minimum sized report)
-s, --group-by-source group packages by source-rpm and repository
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Refined the help.