2022-10-01 22:33:11 +02:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
|
|
|
|
|
|
import logging
|
|
|
|
from dnf.conf import Conf
|
|
|
|
from dnf.cli.cli import BaseCli, Cli
|
|
|
|
from dnf.cli.output import Output
|
|
|
|
from dnf.cli.option_parser import OptionParser
|
|
|
|
from dnf.i18n import _, ucd
|
|
|
|
from datetime import datetime, timezone
|
|
|
|
from sys import argv
|
|
|
|
from os import getcwd, unlink
|
|
|
|
from os.path import isfile, join
|
|
|
|
from glob import glob
|
|
|
|
from subprocess import run
|
|
|
|
|
|
|
|
|
|
|
|
# List new or removed file
|
|
|
|
def read_dnf_pkg_file(os_name, filename1, filename2):
|
|
|
|
if os_name == 'debian':
|
|
|
|
idx_pkg = 0, 1
|
|
|
|
idx_version = 1, 2
|
|
|
|
header_idx = 0, 0
|
|
|
|
else:
|
|
|
|
idx_pkg = 0, 0
|
|
|
|
idx_version = 2, 2
|
|
|
|
header_idx = 2, 2
|
|
|
|
pass
|
|
|
|
pkgs = {}
|
|
|
|
for fidx, filename in enumerate((filename1, filename2)):
|
|
|
|
if not isfile(filename):
|
|
|
|
continue
|
|
|
|
with open(filename, 'r') as pkgs_fh:
|
|
|
|
for idx, pkg_line in enumerate(pkgs_fh.readlines()):
|
|
|
|
if idx < header_idx[fidx]:
|
|
|
|
# header
|
|
|
|
continue
|
|
|
|
sp_line = pkg_line.strip().split()
|
|
|
|
if len(sp_line) < idx_version[fidx] + 1:
|
|
|
|
continue
|
|
|
|
if sp_line[idx_pkg[fidx]] in pkgs:
|
|
|
|
raise Exception(f'package already set {sp_line[0]}?')
|
|
|
|
version = sp_line[idx_version[fidx]]
|
|
|
|
if os_name == 'debian' and version.startswith('('):
|
|
|
|
version = version[1:]
|
|
|
|
pkgs[sp_line[idx_pkg[fidx]]] = version
|
|
|
|
return pkgs
|
|
|
|
|
|
|
|
|
|
|
|
def list_packages(title, packages, packages_info):
|
|
|
|
print(f'# {title}\n')
|
|
|
|
if not packages:
|
|
|
|
print('*Aucun*')
|
|
|
|
packages = list(packages)
|
|
|
|
packages = sorted(packages)
|
|
|
|
for idx, pkg in enumerate(packages):
|
|
|
|
print(f' - {pkg} ({packages_info[pkg]})')
|
|
|
|
print()
|
|
|
|
|
|
|
|
|
|
|
|
# List updated packages
|
|
|
|
class CustomOutput(Output):
|
|
|
|
def listPkgs(self, *args, **kwargs):
|
|
|
|
# do not display list
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
def format_changelog_markdown(changelog):
|
|
|
|
"""Return changelog formatted as in spec file"""
|
|
|
|
text = '\n'.join([f' {line}' for line in changelog['text'].split('\n')])
|
|
|
|
chlog_str = ' - %s %s\n\n%s\n' % (
|
|
|
|
changelog['timestamp'].strftime("%a %b %d %X %Y"),
|
|
|
|
ucd(changelog['author']),
|
|
|
|
ucd(text))
|
|
|
|
return chlog_str
|
|
|
|
|
|
|
|
|
|
|
|
def print_changelogs_markdown(packages):
|
|
|
|
# group packages by src.rpm to avoid showing duplicate changelogs
|
|
|
|
self = BASE
|
|
|
|
bysrpm = dict()
|
|
|
|
for p in packages:
|
|
|
|
# there are packages without source_name, use name then.
|
|
|
|
bysrpm.setdefault(p.source_name or p.name, []).append(p)
|
|
|
|
for source_name in sorted(bysrpm.keys()):
|
|
|
|
bin_packages = bysrpm[source_name]
|
|
|
|
print('- ' + _("Changelogs for {}").format(', '.join([str(pkg) for pkg in bin_packages])))
|
|
|
|
print()
|
|
|
|
for chl in self.latest_changelogs(bin_packages[0]):
|
|
|
|
print(format_changelog_markdown(chl))
|
|
|
|
|
|
|
|
|
2023-01-23 20:23:32 +01:00
|
|
|
def dnf_update(image_name, releasever):
|
2022-10-01 22:33:11 +02:00
|
|
|
conf = Conf()
|
|
|
|
# obsoletes are already listed
|
|
|
|
conf.obsoletes = False
|
|
|
|
with BaseCli(conf) as base:
|
|
|
|
global BASE
|
|
|
|
BASE = base
|
|
|
|
base.print_changelogs = print_changelogs_markdown
|
|
|
|
custom_output = CustomOutput(base.output.base, base.output.conf)
|
|
|
|
base.output = custom_output
|
|
|
|
cli = Cli(base)
|
|
|
|
image_dir = join(getcwd(), image_name)
|
2023-01-23 20:23:32 +01:00
|
|
|
cli.configure(['--setopt=install_weak_deps=False', '--nodocs', '--noplugins', '--installroot=' + image_dir, '--releasever', releasever, 'check-update', '--changelog'], OptionParser())
|
2022-10-01 22:33:11 +02:00
|
|
|
logger = logging.getLogger("dnf")
|
|
|
|
for h in logger.handlers:
|
|
|
|
logger.removeHandler(h)
|
|
|
|
logger.addHandler(logging.NullHandler())
|
|
|
|
cli.run()
|
|
|
|
|
|
|
|
|
|
|
|
def main(os_name, image_name, old_version, releasever):
|
|
|
|
date = datetime.now(timezone.utc).isoformat()
|
|
|
|
if old_version == 0:
|
|
|
|
title = f"Création de l'image {image_name}"
|
|
|
|
subtitle = f"Les paquets de la première image {image_name} sur base Fedora {releasever}"
|
|
|
|
else:
|
|
|
|
title = f"Nouvelle version de l'image {image_name}"
|
|
|
|
subtitle = f"Différence des paquets de l'image {image_name} sur base Fedora {releasever} entre la version {old_version} et {old_version + 1}"
|
|
|
|
print(f"""+++
|
|
|
|
title = "{title}"
|
|
|
|
description = "{subtitle}"
|
|
|
|
date = {date}
|
|
|
|
updated = {date}
|
|
|
|
draft = false
|
|
|
|
template = "blog/page.html"
|
|
|
|
|
|
|
|
[taxonomies]
|
|
|
|
authors = ["Automate"]
|
|
|
|
|
|
|
|
[extra]
|
|
|
|
lead = "{subtitle}."
|
|
|
|
type = "installe"
|
|
|
|
+++
|
|
|
|
""")
|
|
|
|
new_dict = read_dnf_pkg_file(os_name, f'/var/lib/risotto/images/image_bases-{os_name}-{releasever}.pkgs', f'/var/lib/risotto/images/{image_name}.pkgs.new')
|
|
|
|
new_pkg = new_dict.keys()
|
|
|
|
old_file = f'/var/lib/risotto/images/{image_name}.pkgs'
|
|
|
|
if not old_version or not isfile(old_file):
|
|
|
|
list_packages('Liste des paquets', new_pkg, new_dict)
|
|
|
|
else:
|
|
|
|
ori_dict = read_dnf_pkg_file(os_name, f'/var/lib/risotto/images/{image_name}.base.pkgs', old_file)
|
|
|
|
ori_pkg = ori_dict.keys()
|
|
|
|
list_packages('Les paquets supprimés', ori_pkg - new_pkg, ori_dict)
|
|
|
|
list_packages('Les paquets ajoutés', new_pkg - ori_pkg, new_dict)
|
|
|
|
print('# Les paquets mises à jour\n')
|
|
|
|
if os_name == 'fedora':
|
2023-01-23 20:23:32 +01:00
|
|
|
dnf_update(image_name, releasever)
|
2022-10-01 22:33:11 +02:00
|
|
|
else:
|
|
|
|
for filename in glob('*.deb'):
|
|
|
|
unlink(filename)
|
|
|
|
for package in ori_pkg & new_dict:
|
|
|
|
if ori_dict[package] == new_dict[package]:
|
|
|
|
continue
|
|
|
|
info = run(['apt', 'download', package], capture_output=True)
|
|
|
|
if info.returncode:
|
|
|
|
raise Exception(f'cannot download {package}: {info}')
|
|
|
|
packages = list(glob('*.deb'))
|
|
|
|
packages.sort()
|
|
|
|
for package in packages:
|
|
|
|
info = run(['chroot', '.', 'apt-listchanges', '--which', 'both', '-f', 'text', package], capture_output=True)
|
|
|
|
if info.returncode:
|
|
|
|
raise Exception(f'cannot list changes for {package}: {info}')
|
|
|
|
header = True
|
|
|
|
for line in info.stdout.decode().split('\n'):
|
|
|
|
if not header:
|
|
|
|
print(line)
|
|
|
|
if line.startswith('-----------------------'):
|
|
|
|
header = False
|
|
|
|
print()
|
|
|
|
unlink(package)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
image_name = argv[1]
|
|
|
|
old_version = int(argv[2])
|
|
|
|
os_name = argv[3]
|
|
|
|
releasever = argv[4]
|
|
|
|
main(os_name, image_name, old_version, releasever)
|