#!/usr/bin/python3
# -*- coding: utf-8 -*-
# pylint: disable=line-too-long
# kate: space-indent on; indent-width 4; replace-tabs on; indent-mode python; remove-trailing-space modified;
# vim: expandtab ts=4
# pylint: enable=line-too-long
############################################################################
# Copyright © 2018-2024 José Manuel Santamaría Lema <panfaust@gmail.com> #
# #
# This program is free software; you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation; either version 2 of the License, or #
# (at your option) any later version. #
############################################################################
"""This module just provides the KAChangelog class"""
import codecs
import subprocess
import time
import re
from debian.changelog import Changelog
from debian.changelog import get_maintainer
from libka.ubuntu_info import ubuntu_dist_version_from_name, ubuntu_dist_name_from_version
from libka.ka_print import ka_print_good_stuff
from libka.ka_print import ka_print_plain
[docs]
class KAChangelog(Changelog):
"""
This class is meant to represent a debian/changelog file.
"""
def __init__(self, file_path='debian/changelog'):
Changelog.__init__(self)
self._file_path = file_path
# pylint: disable=arguments-differ
[docs]
def parse_changelog(self):
"""Parses a changelog file."""
try:
Changelog.parse_changelog(self, codecs.open(self._file_path, 'r', 'utf-8'))
except Exception as exception:
raise Exception(("Couldn't open %s\n" % self._file_path) + str(exception))
# pylint: disable=arguments-differ
[docs]
def get_last_released_version(self):
"""
This function returns the last released version i.e. the version from the
most recent changelog block ignoring 'UNRELEASED' changelog blocks.
"""
result = None
for changelog_block in self:
if str(changelog_block.distributions) != 'UNRELEASED':
result = changelog_block.version
break
return result
[docs]
def get_last_ubuntu_released_version(self):
"""
This function returns the last released version i.e. the version from the
most recent changelog block ignoring 'UNRELEASED' changelog blocks.
"""
result = None
ignored_blocks = ['UNRELEASED', 'experimental', 'unstable']
for changelog_block in self:
if str(changelog_block.distributions) not in ignored_blocks:
result = changelog_block.version
break
return result
[docs]
def convert2sru(self, lp_bug_number=None, force=False):
"""
This method will edit the last changelog block to be fit for a SRU.
If `force` is `True` it will perform the operation ignoring safety checks.
"""
#Convert the lp bug number to string, just in case
lp_bug_number = str(lp_bug_number)
#Set changelog distributionn
ka_print_good_stuff("Changing changelog distribution:")
old_dist = self.distributions
new_dist = 'UNRELEASED'
self.set_distributions(new_dist)
ka_print_plain("%s -> %s" % (old_dist, new_dist))
#Set changelog version
ka_print_good_stuff("Changing changelog version:")
old_version = str(self.version)
match = re.match('.*ubuntu1$', old_version)
if (match is None) and not force:
raise Exception("Changelog version does't end with 'ubuntu1', aborting SRU"
"conversion.\n")
new_version = re.sub('ubuntu1$', 'ubuntu0.1', old_version)
self.set_version(new_version)
ka_print_plain("%s -> %s" % (old_version, new_version))
#Add the LP bug number
if lp_bug_number is not None:
ka_print_good_stuff("Adding Launchpad bug number: ", end='')
ka_print_plain(lp_bug_number)
#Read info from the latest changelog block
latest_changelog_block = next(iter(self))
orig_changelog_entries = latest_changelog_block.changes()
#Prepare the new list of changelog entries
final_changelog_entries = []
#Populate the new list of changelog entries, adding the LP bug to the
#'New upstream release' line
for changelog_entry in orig_changelog_entries:
match = re.match('.*New upstream release.*', changelog_entry)
if match is not None:
changelog_entry = '%s (LP: #%s)' % (changelog_entry, lp_bug_number)
final_changelog_entries.append(changelog_entry)
#Set the final changelog entries
#NOTE: Yes, it's true we shouldn't access objects 'private' variables,
# however it seems there's no other way to do this with python-debian
latest_changelog_block._changes = final_changelog_entries # pylint: disable=protected-access
[docs]
def refresh_date(self):
"""
Executing this method will refresh the date in the last changelog block
"""
#Abort if the last changelog entry isn't 'UNRELEASED'
if self.distributions != "UNRELEASED":
raise Exception("Last changelog block distribution is not 'UNRELEASED'.")
#Refresh changelog date
self.set_date(time.strftime('%a, %d %b %Y %X %z'))
[docs]
def new_upstream_release(self, new_upstream_version, stability=None):
"""
This method will prepare the changelog for a new upstream release.
The `stability` parameter may be a string such as "beta" or "RC", if
this parameter is different than `None` the changelog entry will say if
the new release in question is a beta version, release candidate or
whatever.
"""
#Convert the new upstream version to string, just in case
new_upstream_version = str(new_upstream_version)
#Create the text line to add to the changelog block
if stability is None:
new_upstream_release_text = 'New upstream release (%s)' % new_upstream_version
else:
new_upstream_release_text = ('New upstream (%s) release (%s)'
% (stability, new_upstream_version))
#Find out the maintainer text for the changelog trailer
maintainer_str = "%s <%s>" % get_maintainer()
#Find out the new package version
upstream_pkg_version = str(self.get_version().upstream_version)
if upstream_pkg_version.endswith('+dfsg'):
new_upstream_version += '+dfsg'
epoch = self.get_version().epoch
if epoch is not None:
new_pkg_version_str = epoch + ":" + new_upstream_version
else:
new_pkg_version_str = new_upstream_version
new_pkg_version_str += "-0ubuntu1"
#Add the new changelog block or reuse the last one to get new_upstream_release_text in
if self.distributions != "UNRELEASED":
#If latest changelog entry is not 'UNRELEASED' we have to create a new changelog block
self.new_block(package=self.get_package(),
version=new_pkg_version_str,
distributions="UNRELEASED",
urgency='medium',
author=maintainer_str,
date=time.strftime('%a, %d %b %Y %X %z'))
#Add the first changelog entry
self.add_change('')
self.add_change(' * ' + new_upstream_release_text)
self.add_change('')
else:
#Set the new version
self.set_version(new_pkg_version_str)
#Add the "New upstream release" entry
#Apparently we can't do this with python-debian right now
self.dump()
subprocess.call(["dch", "--urgency", "medium",
"--multimaint-merge", new_upstream_release_text])
self.parse_changelog()
#Update the maintainer trailer
self.set_author(maintainer_str)
self.set_date(time.strftime('%a, %d %b %Y %X %z'))
# pylint: disable=no-self-use
[docs]
def get_ppa_suffix(self, dist, suffix_number):
"""
Returns a PPA suffix for the changelog.
"""
#Find out dist_version
dist_version = ubuntu_dist_version_from_name(dist)
#Create the suffix string
suffix_string = "~ubuntu%s~ppa%s" % (dist_version, str(suffix_number))
#Return result
return suffix_string
# pylint: enable=no-self-use
[docs]
def add_ppa_suffix(self, dist, suffix_number):
"""
This method will append to the package version a suffix following this formula:
~ubuntu<dist_version>~ppa<suffix_number>
It will also set the distribution name in the top changelog block.
"""
#Find out dist_version and dist_name
dist_version = ubuntu_dist_version_from_name(dist)
dist_name = ubuntu_dist_name_from_version(dist_version)
#Create the suffix string
suffix_string = self.get_ppa_suffix(dist, suffix_number)
#Alter the changelog version, distribution and maint trailer
version = str(self.get_version()) + suffix_string
self.set_urgency("high")
self.set_version(version)
self.set_distributions(dist_name)
maintainer_str = "%s <%s>" % get_maintainer()
self.set_author(maintainer_str)
# pylint: disable=no-self-use
[docs]
def get_qt_ppa_suffix(self, dist, end_number, begin_number=0):
"""
Returns a Qt PPA suffix for the changelog.
"""
#Find out dist_version
dist_version = ubuntu_dist_version_from_name(dist)
#Create the suffix string
suffix_string = "~%s~ubuntu%s~ppa%s" % (str(begin_number), dist_version, str(end_number))
#Return result
return suffix_string
# pylint: enable=no-self-use
[docs]
def add_qt_ppa_suffix(self, dist, end_number, begin_number=0):
"""
This method will append to the package version a suffix following this formula:
~<begin_number>~ubuntu<dist_version>~ppa<end_number>
It will also set the distribution name in the top changelog block.
"""
#Find out dist_version and dist_name
dist_version = ubuntu_dist_version_from_name(dist)
dist_name = ubuntu_dist_name_from_version(dist_version)
#Create the suffix string
suffix_string = self.get_qt_ppa_suffix(dist, end_number, begin_number)
#Alter the changelog version, distribution and maint trailer
version = str(self.get_version()) + suffix_string
self.set_urgency("high")
self.set_version(version)
self.set_distributions(dist_name)
maintainer_str = "%s <%s>" % get_maintainer()
self.set_author(maintainer_str)
# pylint: disable=no-self-use, unused-argument
[docs]
def get_tritemio_suffix(self, dist, suffix_number):
"""
Returns a string providing a tritemio suffix for the changelog
"""
# Get current version and find out if it's a debian version, see in the comment below why
# we need this
current_version = str(self.get_version())
is_ubuntu_version = re.search(r'ubuntu', current_version) is not None
#If we have a debian version, we also need to append 'ubuntu0' to avoid situations like
#this:
# 1. package has a 1.0-1 version, synced with debian
# 2. this package gets uploaded to tritemio with version '1.0-1+tritemio1'
# 3. this package is uploaded again officially to ubuntu as '1.0-1ubuntu1'
# 4. we try to rebuild this package again for tritemio, that would be with
# version '1.0-1ubuntu1+tritemio1', however this version is lower than
# the previous upload to tritemio, which was '1.0-1+tritemio1', and this
# will make wannabuild unable to rebuild the package unless we edit
# manually the wannabuild database
if is_ubuntu_version:
suffix_string = "+tritemio%s" % str(suffix_number)
else:
suffix_string = "ubuntu0+tritemio%s" % str(suffix_number)
#Return result
return suffix_string
# pylint: disable=no-self-use, unused-argument
[docs]
def add_tritemio_suffix(self, dist, suffix_number):
"""
This method will append to the package version a suffix following this formula:
+tritemio<suffix_number>
It will also set the distribution name in the top changelog block.
"""
#Create the suffix string
suffix_string = self.get_tritemio_suffix(dist, suffix_number)
#Alter the changelog version, distribution and maint trailer
version = str(self.get_version()) + suffix_string
self.set_version(version)
self.set_distributions(dist)
maintainer_str = "%s <%s>" % get_maintainer()
self.set_author(maintainer_str)
[docs]
def make_ubuntu_release(self, dist):
"""
This method will prepare the changelog for an Ubuntu archive upload.
"""
#Abort if the last changelog entry isn't 'UNRELEASED'
if self.distributions != "UNRELEASED":
raise Exception("Last changelog block distribution is not 'UNRELEASED'.")
#Edit last changelog entry - maint trailer.
self.dump()
subprocess.check_call(["dch", "-r", "-m", "-D", dist, "--multimaint-merge", ""])
self.parse_changelog()
[docs]
def dump(self):
"""Dump to hard disk all the changes made so far"""
self.write_to_open_file(codecs.open(self._file_path, 'w', 'utf-8'))