Blame scripts/update_releasenotes.py

Packit 6f3914
#!/usr/bin/python2
Packit 6f3914
#
Packit 6f3914
# Copyright (C) 2015 Red Hat, Inc.
Packit 6f3914
#
Packit 6f3914
# This copyrighted material is made available to anyone wishing to use,
Packit 6f3914
# modify, copy, or redistribute it subject to the terms and conditions of
Packit 6f3914
# the GNU General Public License v.2, or (at your option) any later version.
Packit 6f3914
# This program is distributed in the hope that it will be useful, but WITHOUT
Packit 6f3914
# ANY WARRANTY expressed or implied, including the implied warranties of
Packit 6f3914
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
Packit 6f3914
# Public License for more details.  You should have received a copy of the
Packit 6f3914
# GNU General Public License along with this program; if not, write to the
Packit 6f3914
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
Packit 6f3914
# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
Packit 6f3914
# source code or documentation are not subject to the GNU General Public
Packit 6f3914
# License and may only be used or replicated with the express permission of
Packit 6f3914
# Red Hat, Inc.
Packit 6f3914
#
Packit 6f3914
Packit 6f3914
r"""A script intended to update release notes.
Packit 6f3914
Packit 6f3914
Works on Python >= 2.7 and < 3.0.
Packit 6f3914
Packit 6f3914
This module, when run as a script, provides a command line interface.
Packit 6f3914
The interface usage is::
Packit 6f3914
Packit 6f3914
    usage: prog [-h]
Packit 6f3914
Packit 6f3914
    Extend the list of issues of the next version in release notes. It
Packit 6f3914
    updates the release notes found in the DNF repository which contains
Packit 6f3914
    this module. The module location is obtained from the __file__
Packit 6f3914
    variable. The version number is parsed from the spec file. If the
Packit 6f3914
    release notes already contains given issue, it is not added again.
Packit 6f3914
    Otherwise, it is added to the beginning of the list. If the release
Packit 6f3914
    notes does not contain the next version, a new section is appended
Packit 6f3914
    right before the first section. The diff between the new release
Packit 6f3914
    notes and the Git index is printed to the standard error stream. The
Packit 6f3914
    message starts with "INFO Update finished. See the diff to the Git
Packit 6f3914
    index: ".
Packit 6f3914
Packit 6f3914
    optional arguments:
Packit 6f3914
      -h, --help  show this help message and exit
Packit 6f3914
Packit 6f3914
    If the version is already released or if another error occurs the
Packit 6f3914
    exit status is non-zero.
Packit 6f3914
Packit 6f3914
:var LOGGER: the logger used by this script
Packit 6f3914
:type LOGGER: logging.Logger
Packit 6f3914
:var SPEC_FN: a name of the DNF spec file relative to the working
Packit 6f3914
   directory of the Git repository which contains this script
Packit 6f3914
:type SPEC_FN: str
Packit 6f3914
:var TAG_PAT: a string pattern of DNF tags where the "{version}" field
Packit 6f3914
   should be replaced with the given version of DNF
Packit 6f3914
:type TAG_PAT: str
Packit 6f3914
:var ISSUE_RE: a regular expression matching numbers of resolved issues
Packit 6f3914
   in the first line of a commit message
Packit 6f3914
:type ISSUE_RE: re.RegexObject
Packit 6f3914
:var RELATED_RE: a regular expression matching numbers of related issues
Packit 6f3914
   in a commit message
Packit 6f3914
:type RELATED_RE: re.RegexObject
Packit 6f3914
:var SIMILAR_RE: a regular expression matching numbers which look like a
Packit 6f3914
   number of an issue referred in a commit message
Packit 6f3914
:type SIMILAR_RE: re.RegexObject
Packit 6f3914
:var NOTES_FN: a name of the DNF release notes file relative to the
Packit 6f3914
   working directory of the Git repository which contains this script
Packit 6f3914
:type NOTES_FN: str
Packit 6f3914
:var TITLE_PAT: a string pattern of a DNF release notes section title
Packit 6f3914
   (including a trailing "\n") where the "{version}" field should be
Packit 6f3914
   replaced with the given release version
Packit 6f3914
:type TITLE_PAT: str
Packit 6f3914
:var ISSUE_PAT: a string pattern of a reference to a resolved issue in
Packit 6f3914
   DNF release notes (including a trailing "\n") where the "{number}"
Packit 6f3914
   field should be replaced with the number of the given issue
Packit 6f3914
:type ISSUE_PAT: str
Packit 6f3914
:var OVERLINE_RE: a regular expression matching a section title overline
Packit 6f3914
   in a line of DNF release notes
Packit 6f3914
:type OVERLINE_RE: re.RegexObject
Packit 6f3914
:var TITLE_RE_PAT: a string pattern of a regular expression matching a
Packit 6f3914
   section title in a line of DNF release notes where the "{maxlen}"
Packit 6f3914
   field should be replaced with the maximum expected length of the
Packit 6f3914
   release version
Packit 6f3914
:type TITLE_RE_PAT: str
Packit 6f3914
Packit 6f3914
"""
Packit 6f3914
Packit 6f3914
Packit 6f3914
from __future__ import absolute_import
Packit 6f3914
Packit 6f3914
import argparse
Packit 6f3914
import contextlib
Packit 6f3914
import io
Packit 6f3914
import itertools
Packit 6f3914
import logging
Packit 6f3914
import os
Packit 6f3914
import re
Packit 6f3914
import shutil
Packit 6f3914
import sys
Packit 6f3914
import tempfile
Packit 6f3914
import unittest
Packit 6f3914
Packit 6f3914
import git
Packit 6f3914
import rpm
Packit 6f3914
import tests.mock
Packit 6f3914
Packit 6f3914
Packit 6f3914
LOGGER = logging.getLogger(sys.argv[0])
Packit 6f3914
Packit 6f3914
SPEC_FN = 'dnf.spec'
Packit 6f3914
Packit 6f3914
TAG_PAT = 'dnf-{version}-1'
Packit 6f3914
Packit 6f3914
ISSUE_RE = re.compile(ur'(?
Packit 6f3914
Packit 6f3914
RELATED_RE = re.compile(ur'Related: RhBug:(\d+)')
Packit 6f3914
Packit 6f3914
SIMILAR_RE = re.compile(ur'\d{6,}')
Packit 6f3914
Packit 6f3914
NOTES_FN = os.path.join('doc', 'release_notes.rst')
Packit 6f3914
Packit 6f3914
TITLE_PAT = '{version} Release Notes\n'
Packit 6f3914
Packit 6f3914
ISSUE_PAT = '* :rhbug:`{number}`\n'
Packit 6f3914
Packit 6f3914
OVERLINE_RE = re.compile('^(=+)\n$')
Packit 6f3914
Packit 6f3914
TITLE_RE_PAT = '^(.{{,{maxlen}}}) Release Notes\n$'
Packit 6f3914
Packit 6f3914
Packit 6f3914
@contextlib.contextmanager
Packit 6f3914
def _safe_overwrite(name):
Packit 6f3914
    """Open a file in order to overwrite it safely.
Packit 6f3914
Packit 6f3914
    Until the context manager successfully exits, the file remains
Packit 6f3914
    unchanged.
Packit 6f3914
Packit 6f3914
    :param name: name of the file
Packit 6f3914
    :type name: str
Packit 6f3914
    :returns: a context manager that returns a readable text file object
Packit 6f3914
        intended to read the content of the file and a writable text
Packit 6f3914
        file object intended to write the new content of the file
Packit 6f3914
    :rtype: contextmanager[tuple[file, file]]
Packit 6f3914
    :raises exceptions.IOError: if the file cannot be opened
Packit 6f3914
Packit 6f3914
    """
Packit 6f3914
    with open(name, 'rt+') as file_, tempfile.TemporaryFile('wt+') as tmpfile:
Packit 6f3914
        yield file_, tmpfile
Packit 6f3914
        tmpfile.seek(0)
Packit 6f3914
        file_.seek(0)
Packit 6f3914
        shutil.copyfileobj(tmpfile, file_)
Packit 6f3914
Packit 6f3914
Packit 6f3914
def detect_repository():
Packit 6f3914
    """Detect the DNF Git repository which contains this module.
Packit 6f3914
Packit 6f3914
    The module location is obtained from :const:`__file__`.
Packit 6f3914
Packit 6f3914
    :returns: the repository
Packit 6f3914
    :rtype: git.Repo
Packit 6f3914
Packit 6f3914
    """
Packit 6f3914
    dirname = os.path.dirname(__file__)
Packit 6f3914
    # FIXME remove this once we can get rid of supporting GitPython < 0.3.5
Packit 6f3914
    try:
Packit 6f3914
        return git.Repo(dirname, search_parent_directories=True)
Packit 6f3914
    except TypeError:
Packit 6f3914
        return git.Repo(dirname)
Packit 6f3914
Packit 6f3914
Packit 6f3914
def detect_version(repository):
Packit 6f3914
    """Detect the DNF version from its spec file.
Packit 6f3914
Packit 6f3914
    :param repository: a non-bare DNF repository which contains the spec
Packit 6f3914
       file
Packit 6f3914
    :type repository: git.Repo
Packit 6f3914
    :returns: the detected version
Packit 6f3914
    :rtype: str
Packit 6f3914
Packit 6f3914
    """
Packit 6f3914
    filename = os.path.join(repository.working_dir, SPEC_FN)
Packit 6f3914
    return rpm.spec(filename).sourceHeader[rpm.RPMTAG_VERSION]
Packit 6f3914
Packit 6f3914
Packit 6f3914
def find_tag(repository, version):
Packit 6f3914
    """Find the Git tag corresponding to a DNF version.
Packit 6f3914
Packit 6f3914
    :param repository: a DNF repository
Packit 6f3914
    :type repository: git.Repo
Packit 6f3914
    :returns: the name of the tag
Packit 6f3914
    :rtype: str | None
Packit 6f3914
Packit 6f3914
    """
Packit 6f3914
    tagname = TAG_PAT.format(version=version)
Packit 6f3914
    return tagname if tagname in repository.tags else None
Packit 6f3914
Packit 6f3914
Packit 6f3914
def iter_unreleased_commits(repository):
Packit 6f3914
    """Iterate over the commits that were not tagged as a release.
Packit 6f3914
Packit 6f3914
    :param repository: a repository
Packit 6f3914
    :type repository: git.Repo
Packit 6f3914
    :returns: a generator yielding the commits
Packit 6f3914
    :rtype: generator[git.Commit]
Packit 6f3914
Packit 6f3914
    """
Packit 6f3914
    tagshas = {tag.commit.hexsha for tag in repository.tags}
Packit 6f3914
    for commit in repository.iter_commits():
Packit 6f3914
        if commit.hexsha in tagshas:
Packit 6f3914
            # FIXME encode() once we get rid of supporting GitPython < 0.3.4
Packit 6f3914
            LOGGER.debug(
Packit 6f3914
                'Unreleased commits iteration stopped on the first tagged '
Packit 6f3914
                'commit: %s', commit.hexsha.encode())
Packit 6f3914
            break
Packit 6f3914
        yield commit
Packit 6f3914
Packit 6f3914
Packit 6f3914
def parse_issues(commits):
Packit 6f3914
    """Parse the numbers of the resolved DNF issues from commit messages.
Packit 6f3914
Packit 6f3914
    :param commits: the DNF commits
Packit 6f3914
    :type commits: collections.Iterable[git.Commit]
Packit 6f3914
    :returns: a generator yielding the issue numbers
Packit 6f3914
    :rtype: generator[str]
Packit 6f3914
Packit 6f3914
    """
Packit 6f3914
    for commit in commits:
Packit 6f3914
        firstline = commit.message.splitlines()[0]
Packit 6f3914
        valid = {match.group(1) for match in ISSUE_RE.finditer(firstline)}
Packit 6f3914
        for issue in valid:
Packit 6f3914
            issue = issue.encode()
Packit 6f3914
            # FIXME decode() once we get rid of supporting GitPython < 0.3.4
Packit 6f3914
            LOGGER.debug(
Packit 6f3914
                'Recognized %s in commit %s.', issue,
Packit 6f3914
                commit.hexsha.decode().encode())
Packit 6f3914
            yield issue.encode()
Packit 6f3914
        valid |= {mat.group(1) for mat in RELATED_RE.finditer(commit.message)}
Packit 6f3914
        for match in SIMILAR_RE.finditer(commit.message):
Packit 6f3914
            if match.group(0) not in valid:
Packit 6f3914
                # FIXME decode once we get rid of supporting GitPython < 0.3.4
Packit 6f3914
                LOGGER.warning(
Packit 6f3914
                    'Skipped %s in commit %s which looks like an issue '
Packit 6f3914
                    'number.', match.group(0).encode(),
Packit 6f3914
                    commit.hexsha.decode().encode())
Packit 6f3914
Packit 6f3914
Packit 6f3914
def extend_releases(releases, version, issues):
Packit 6f3914
    r"""Extend the list of issues of a release version in release notes.
Packit 6f3914
Packit 6f3914
    The issues are appended to the beginning of the list. If an issue is
Packit 6f3914
    already in the list of the release version, it is not added again.
Packit 6f3914
    If the release version is not found, the release version
Packit 6f3914
    (``version, '\n', issues, '\n'``) is yielded right before the first
Packit 6f3914
    release version in the release notes.
Packit 6f3914
Packit 6f3914
    :param releases: the version, the description, the list of issue
Packit 6f3914
       numbers and the epilog for each release version in release notes
Packit 6f3914
    :type releases: collections.Iterable[tuple[str | None, str, list[str], str]]
Packit 6f3914
    :param version: the release version to be extended
Packit 6f3914
    :type version: str
Packit 6f3914
    :param issues: the issues to be added
Packit 6f3914
    :type issues: collections.Iterable[str]
Packit 6f3914
    :returns: a generator yielding the modified release notes
Packit 6f3914
    :rtype:  generator[tuple[str | None, str, list[str], str]]
Packit 6f3914
Packit 6f3914
    """
Packit 6f3914
    releases, issues = list(releases), list(issues)
Packit 6f3914
    prepend = not any(release[0] == version for release in releases)
Packit 6f3914
    for version_, description, issues_, epilog in releases:
Packit 6f3914
        if prepend and version_ is not None:
Packit 6f3914
            yield version, '\n', issues, '\n'
Packit 6f3914
            prepend = False
Packit 6f3914
        elif version_ == version:
Packit 6f3914
            issues_ = issues_[:]
Packit 6f3914
            for issue in reversed(issues):
Packit 6f3914
                if issue not in issues_:
Packit 6f3914
                    issues_.insert(0, issue)
Packit 6f3914
        yield version_, description, issues_, epilog
Packit 6f3914
Packit 6f3914
Packit 6f3914
def format_release(version, description, issues, epilog):
Packit 6f3914
    """Compose a string in form of DNF release notes from a release.
Packit 6f3914
Packit 6f3914
    The order of issues is preserved.
Packit 6f3914
Packit 6f3914
    :param version: the version of the release
Packit 6f3914
    :type version: str | None
Packit 6f3914
    :param description: the description of the release
Packit 6f3914
    :type description: str
Packit 6f3914
    :param issues: the list of issue numbers of the release
Packit 6f3914
    :type issues: list[str]
Packit 6f3914
    :param epilog: the epilog of the release
Packit 6f3914
    :type epilog: str
Packit 6f3914
    :returns: the formatted string
Packit 6f3914
    :rtype: str
Packit 6f3914
Packit 6f3914
    """
Packit 6f3914
    titlestr = ''
Packit 6f3914
    if version:
Packit 6f3914
        title = TITLE_PAT.format(version=version)
Packit 6f3914
        length = len(title) - 1
Packit 6f3914
        titlestr = '{}\n{}{}\n'.format('=' * length, title, '=' * length)
Packit 6f3914
    issuestr = ''.join(ISSUE_PAT.format(number=issue) for issue in issues)
Packit 6f3914
    return ''.join([titlestr, description, issuestr, epilog])
Packit 6f3914
Packit 6f3914
Packit 6f3914
def update_notes():
Packit 6f3914
    r"""Extend the list of issues of the next version in release notes.
Packit 6f3914
Packit 6f3914
    It updates the release notes found in the DNF repository which
Packit 6f3914
    contains this module. The module location is obtained from
Packit 6f3914
    :const:`__file__`. The version number is parsed from the spec file.
Packit 6f3914
    If the release notes already contains given issue, it is not added
Packit 6f3914
    again. Otherwise, it is added to the beginning of the list. If the
Packit 6f3914
    release notes does not contain the next version, a new section is
Packit 6f3914
    appended right before the first section. The diff between the new
Packit 6f3914
    release notes and the Git index is logged to :const:`.LOGGER` with
Packit 6f3914
    level :const:`INFO`. The message starts with "Update finished. See
Packit 6f3914
    the diff to the Git index:\n".
Packit 6f3914
Packit 6f3914
    :raises exceptions.ValueError: if the version is already released
Packit 6f3914
Packit 6f3914
    """
Packit 6f3914
    repository = detect_repository()
Packit 6f3914
    LOGGER.info('Detected the repository at: %s', repository.working_dir)
Packit 6f3914
    notesfn = os.path.join(repository.working_dir, NOTES_FN)
Packit 6f3914
    version = detect_version(repository)
Packit 6f3914
    LOGGER.info('Detected DNF version (from the spec file): %s', version)
Packit 6f3914
    issues = parse_issues(iter_unreleased_commits(repository))
Packit 6f3914
    parser = ReleaseNotesParser()
Packit 6f3914
    tagname = find_tag(repository, version)
Packit 6f3914
    if tagname:
Packit 6f3914
        LOGGER.warning('Found a tag matching the current version: %s', tagname)
Packit 6f3914
        LOGGER.error('Extending an already released version is not allowed!')
Packit 6f3914
        raise ValueError('version already released')
Packit 6f3914
    with _safe_overwrite(notesfn) as (source, destination):
Packit 6f3914
        releases = extend_releases(parser.parse_lines(source), version, issues)
Packit 6f3914
        for version_, desc, issues_, epilog in releases:
Packit 6f3914
            destination.write(format_release(version_, desc, issues_, epilog))
Packit 6f3914
    diff = repository.index.diff(None, NOTES_FN, create_patch=True)[0].diff
Packit 6f3914
    LOGGER.info(
Packit 6f3914
        'Update finished. See the diff to the Git index:\n%s',
Packit 6f3914
        re.sub(r'^', '    ', diff, flags=re.M))
Packit 6f3914
Packit 6f3914
Packit 6f3914
def main():
Packit 6f3914
    """Start the main loop of the command line interface.
Packit 6f3914
Packit 6f3914
    The root logger is configured to write DEBUG+ messages into the
Packit 6f3914
    directory where is this module located if not configured otherwise.
Packit 6f3914
    A handler that writes INFO+ messages to :data:`sys.stderr` is added
Packit 6f3914
    to :const:`.LOGGER`.
Packit 6f3914
Packit 6f3914
    The interface usage is::
Packit 6f3914
Packit 6f3914
        usage: prog [-h]
Packit 6f3914
Packit 6f3914
        Extend the list of issues of the next version in release notes.
Packit 6f3914
        It updates the release notes found in the DNF repository which
Packit 6f3914
        contains this module. The module location is obtained from the
Packit 6f3914
        __file__ variable. The version number is parsed from the spec
Packit 6f3914
        file. If the release notes already contains given issue, it is
Packit 6f3914
        not added again. Otherwise, it is added to the beginning of the
Packit 6f3914
        list. If the release notes does not contain the next version, a
Packit 6f3914
        new section is appended right before the first section. The diff
Packit 6f3914
        between the new release notes and the Git index is printed to
Packit 6f3914
        the standard error stream. The message starts with "INFO Update
Packit 6f3914
        finished. See the diff to the Git index: ".
Packit 6f3914
Packit 6f3914
        optional arguments:
Packit 6f3914
          -h, --help  show this help message and exit
Packit 6f3914
Packit 6f3914
        If the version is already released or if another error occurs
Packit 6f3914
        the exit status is non-zero.
Packit 6f3914
Packit 6f3914
    :raises exceptions.SystemExit: with a non-zero exit status if an
Packit 6f3914
       error occurs
Packit 6f3914
Packit 6f3914
    """
Packit 6f3914
    logging.basicConfig(
Packit 6f3914
        filename='{}.log'.format(__file__),
Packit 6f3914
        filemode='wt',
Packit 6f3914
        format='%(asctime)s.%(msecs)03d:%(levelname)s:%(name)s:%(message)s',
Packit 6f3914
        datefmt='%Y%m%dT%H%M%S',
Packit 6f3914
        level=logging.DEBUG)
Packit 6f3914
    handler = logging.StreamHandler()
Packit 6f3914
    handler.setLevel(logging.INFO)
Packit 6f3914
    handler.setFormatter(logging.Formatter('%(levelname)s %(message)s'))
Packit 6f3914
    LOGGER.addHandler(handler)
Packit 6f3914
    argparser = argparse.ArgumentParser(
Packit 6f3914
        description='Extend the list of issues of the next version in release '
Packit 6f3914
                    'notes. It updates the release notes found in the DNF '
Packit 6f3914
                    'repository which contains this module. The module '
Packit 6f3914
                    'location is obtained from the __file__ variable. The '
Packit 6f3914
                    'version number is parsed from the spec file. If the '
Packit 6f3914
                    'release notes already contains given issue, it is not '
Packit 6f3914
                    'added again. Otherwise, it is added to the beginning of '
Packit 6f3914
                    'the list. If the release notes does not contain the next '
Packit 6f3914
                    'version, a new section is appended right before the first'
Packit 6f3914
                    ' section. The diff between the new release notes and the '
Packit 6f3914
                    'Git index is printed to the standard error stream. The '
Packit 6f3914
                    'message starts with "INFO Update finished. See the diff '
Packit 6f3914
                    'to the Git index:\n".',
Packit 6f3914
        epilog='If the version is already released or if another error occurs '
Packit 6f3914
               'the exit status is non-zero.')
Packit 6f3914
    argparser.parse_args()
Packit 6f3914
    try:
Packit 6f3914
        update_notes()
Packit 6f3914
    except ValueError:
Packit 6f3914
        sys.exit(1)
Packit 6f3914
Packit 6f3914
Packit 6f3914
class ReleaseNotesParser(object):
Packit 6f3914
Packit 6f3914
    """A class able to parse DNF release notes.
Packit 6f3914
Packit 6f3914
    :ivar _needline: an expected line which represents the end of a
Packit 6f3914
       section title
Packit 6f3914
    :type _needline: str | None
Packit 6f3914
    :ivar _version: a version parsed from a potential next section title
Packit 6f3914
    :type _version: str | None
Packit 6f3914
    :ivar version: a version parsed from the last section title
Packit 6f3914
    :type version: str | None
Packit 6f3914
    :ivar description: lines parsed from the last section description
Packit 6f3914
    :type description: list[str]
Packit 6f3914
    :ivar issues: numbers of resolved issues parsed from the last
Packit 6f3914
       section
Packit 6f3914
    :type issues: list[str]
Packit 6f3914
    :ivar epilog: lines parsed from the last section epilog
Packit 6f3914
    :type epilog: list[str]
Packit 6f3914
Packit 6f3914
    """
Packit 6f3914
Packit 6f3914
    def __init__(self):
Packit 6f3914
        """Initialize the parser."""
Packit 6f3914
        super(ReleaseNotesParser, self).__init__()
Packit 6f3914
        self._needline = None
Packit 6f3914
        self._version = None
Packit 6f3914
        self.version = None
Packit 6f3914
        self.description = []
Packit 6f3914
        self.issues = []
Packit 6f3914
        self.epilog = []
Packit 6f3914
Packit 6f3914
    @property
Packit 6f3914
    def _last_version(self):
Packit 6f3914
        """The last parsed release version.
Packit 6f3914
Packit 6f3914
        :returns: the version, the description, the list of issue
Packit 6f3914
           numbers and the epilog of the release
Packit 6f3914
        :rtype: tuple[str | None, str, list[str], str]
Packit 6f3914
Packit 6f3914
        """
Packit 6f3914
        return (
Packit 6f3914
            self.version,
Packit 6f3914
            ''.join(self.description),
Packit 6f3914
            self.issues,
Packit 6f3914
            ''.join(self.epilog))
Packit 6f3914
Packit 6f3914
    def _cancel_title(self):
Packit 6f3914
        """Cancel expectations of a next section title.
Packit 6f3914
Packit 6f3914
        It modifies :attr:`_needline` and :attr:`_version`.
Packit 6f3914
Packit 6f3914
        :returns: the old values of :attr:`_needline` and
Packit 6f3914
           :attr:`_version`
Packit 6f3914
        :rtype: tuple[str | None, str | None]
Packit 6f3914
Packit 6f3914
        """
Packit 6f3914
        needline, version = self._needline, self._version
Packit 6f3914
        self._needline = self._version = None
Packit 6f3914
        return needline, version
Packit 6f3914
Packit 6f3914
    def _parse_overline(self, line):
Packit 6f3914
        """Parse the overline of the next section title from a line.
Packit 6f3914
Packit 6f3914
        It changes the state of the parser.
Packit 6f3914
Packit 6f3914
        :param line: the line to be parsed
Packit 6f3914
        :type line: str
Packit 6f3914
        :returns: returns :data:`True` if the line contains an
Packit 6f3914
            overline, otherwise it returns :data:`False`
Packit 6f3914
        :rtype: bool
Packit 6f3914
Packit 6f3914
        """
Packit 6f3914
        match = OVERLINE_RE.match(line)
Packit 6f3914
        if not match:
Packit 6f3914
            return False
Packit 6f3914
        self._cancel_title()
Packit 6f3914
        self._needline = match.group(1)
Packit 6f3914
        return True
Packit 6f3914
Packit 6f3914
    def _parse_title(self, line):
Packit 6f3914
        """Parse the title from a line.
Packit 6f3914
Packit 6f3914
        It changes the state of the parser even if the line does not
Packit 6f3914
        contain a title.
Packit 6f3914
Packit 6f3914
        :param line: the line to be parsed
Packit 6f3914
        :type line: str
Packit 6f3914
        :returns: returns :data:`True` if the line contains a title,
Packit 6f3914
           otherwise it returns :data:`False`
Packit 6f3914
        :rtype: bool
Packit 6f3914
Packit 6f3914
        """
Packit 6f3914
        maxlen = len(self._needline) - len(' Release Notes')
Packit 6f3914
        match = re.match(TITLE_RE_PAT.format(maxlen=maxlen), line)
Packit 6f3914
        if not match:
Packit 6f3914
            self._cancel_title()
Packit 6f3914
            return False
Packit 6f3914
        self._version = match.group(1)
Packit 6f3914
        return True
Packit 6f3914
Packit 6f3914
    def _parse_underline(self, line):
Packit 6f3914
        """Parse the underline of the next section title from a line.
Packit 6f3914
Packit 6f3914
        It changes the state of the parser.
Packit 6f3914
Packit 6f3914
        :param line: the line to be parsed
Packit 6f3914
        :type line: str
Packit 6f3914
        :returns: the version, the description, the list of issue
Packit 6f3914
           numbers and the epilog of the previous release
Packit 6f3914
        :rtype: tuple[str | None, str, list[str], str] | None
Packit 6f3914
Packit 6f3914
        """
Packit 6f3914
        needline, version = self._cancel_title()
Packit 6f3914
        if line != '{}\n'.format(needline):
Packit 6f3914
            return None
Packit 6f3914
        previous_version = self._last_version
Packit 6f3914
        self.version = version
Packit 6f3914
        self.description, self.issues, self.epilog = [], [], []
Packit 6f3914
        return previous_version
Packit 6f3914
Packit 6f3914
    def _parse_issue(self, line):
Packit 6f3914
        """Parse an issue number from a line.
Packit 6f3914
Packit 6f3914
        It changes the state of the parser.
Packit 6f3914
Packit 6f3914
        :param line: the line to be parsed
Packit 6f3914
        :type line: str
Packit 6f3914
        :returns: returns :data:`True` if the line refers to an issue,
Packit 6f3914
           otherwise it returns :data:`False`
Packit 6f3914
        :rtype: bool
Packit 6f3914
Packit 6f3914
        """
Packit 6f3914
        match = re.match(r'^\* :rhbug:`(\d+)`\n$', line)
Packit 6f3914
        if not match:
Packit 6f3914
            return False
Packit 6f3914
        self.issues.append(match.group(1))
Packit 6f3914
        return True
Packit 6f3914
Packit 6f3914
    def _parse_line(self, line):
Packit 6f3914
        """Parse a line of DNF release notes.
Packit 6f3914
Packit 6f3914
        It changes the state of the parser.
Packit 6f3914
Packit 6f3914
        :param line: the line to be parsed
Packit 6f3914
        :type line: str
Packit 6f3914
        :returns: the version, the description, the list of issue
Packit 6f3914
           numbers and the epilog of the previous release
Packit 6f3914
        :rtype: tuple[str | None, str, list[str], str] | None
Packit 6f3914
Packit 6f3914
        """
Packit 6f3914
        needtitle = self._needline and self._version
Packit 6f3914
        if not needtitle and self._parse_overline(line):
Packit 6f3914
            return None
Packit 6f3914
        if self._needline and not self._version and self._parse_title(line):
Packit 6f3914
            return None
Packit 6f3914
        if self._needline and self._version:
Packit 6f3914
            previous_version = self._parse_underline(line)
Packit 6f3914
            if previous_version:
Packit 6f3914
                return previous_version
Packit 6f3914
        if not self.epilog and self._parse_issue(line):
Packit 6f3914
            return None
Packit 6f3914
        if not self.issues:
Packit 6f3914
            self.description.append(line)
Packit 6f3914
            return None
Packit 6f3914
        self.epilog.append(line)
Packit 6f3914
Packit 6f3914
    def parse_lines(self, lines):
Packit 6f3914
        """Parse the lines of DNF release notes.
Packit 6f3914
Packit 6f3914
        :param lines: the line to be parsed
Packit 6f3914
        :type lines: collections.Iterable[str]
Packit 6f3914
        :returns: a generator yielding the version, the description, the
Packit 6f3914
           list of issue numbers and the epilog of each release version
Packit 6f3914
        :rtype: generator[tuple[str | None, str, list[str], str]]
Packit 6f3914
Packit 6f3914
        """
Packit 6f3914
        self._needline = None
Packit 6f3914
        self._version = None
Packit 6f3914
        self.version = None
Packit 6f3914
        self.description = []
Packit 6f3914
        self.issues = []
Packit 6f3914
        self.epilog = []
Packit 6f3914
        for line in lines:
Packit 6f3914
            previous_version = self._parse_line(line)
Packit 6f3914
            if previous_version:
Packit 6f3914
                yield previous_version
Packit 6f3914
        yield self._last_version
Packit 6f3914
Packit 6f3914
Packit 6f3914
class TestCase(unittest.TestCase):
Packit 6f3914
Packit 6f3914
    """A test fixture common to all tests.
Packit 6f3914
Packit 6f3914
    Among other things, the fixture contains a non-bare DNF repository
Packit 6f3914
    with a spec file specifying an unreleased version, a tag dnf-1.0.1-1
Packit 6f3914
    matching the version 1.0.1, one extra commit which resolves an issue
Packit 6f3914
    and release notes matching the revision 9110490 of DNF.
Packit 6f3914
Packit 6f3914
    :cvar VERSION: the unreleased version specified in the spec file
Packit 6f3914
    :type VERSION: str
Packit 6f3914
    :cvar ISSUE: the number of the issue resolved by the extra commit
Packit 6f3914
    :type ISSUE: str
Packit 6f3914
    :ivar repository: the testing repository
Packit 6f3914
    :type repository: git.Repo
Packit 6f3914
    :ivar commit: the extra commit
Packit 6f3914
    :type commit: unicode
Packit 6f3914
Packit 6f3914
    """
Packit 6f3914
Packit 6f3914
    VERSION = '999.9.9'
Packit 6f3914
Packit 6f3914
    ISSUE = '123456'
Packit 6f3914
Packit 6f3914
    def setUp(self):
Packit 6f3914
        """Prepare the test fixture.
Packit 6f3914
Packit 6f3914
        :const:`__file__` is needed to prepare the fixture.
Packit 6f3914
Packit 6f3914
        """
Packit 6f3914
        dirname = tempfile.mkdtemp()
Packit 6f3914
        self.addCleanup(shutil.rmtree, dirname)
Packit 6f3914
        self.repository = detect_repository().clone(dirname)
Packit 6f3914
        self.repository.head.reset(
Packit 6f3914
            '9110490d690bbad10977b86e7ffbe1feeae26e03', working_tree=True)
Packit 6f3914
        with open(os.path.join(dirname, SPEC_FN), 'wt') as specfile:
Packit 6f3914
            specfile.write(
Packit 6f3914
                'Name: dnf\n' +
Packit 6f3914
                'Version: {}\n'.format(self.VERSION) +
Packit 6f3914
                'Release: 1\n'
Packit 6f3914
                'Summary: Package manager forked from Yum\n'
Packit 6f3914
                'License: GPLv2+ and GPLv2 and GPL\n'
Packit 6f3914
                '%description\n'
Packit 6f3914
                'Package manager forked from Yum, using libsolv.\n'
Packit 6f3914
                '%files\n')
Packit 6f3914
        self.repository.index.add([SPEC_FN])
Packit 6f3914
        self.commit = self.repository.index.commit(
Packit 6f3914
            u'Version (RhBug:{})'.format(self.ISSUE.decode()))
Packit 6f3914
Packit 6f3914
    @staticmethod
Packit 6f3914
    @contextlib.contextmanager
Packit 6f3914
    def _read_copy(name):
Packit 6f3914
        """Create and open a readable copy of a file.
Packit 6f3914
Packit 6f3914
        :param name: name of the file
Packit 6f3914
        :type name: str
Packit 6f3914
        :returns: the readable copy
Packit 6f3914
        :rtype: file
Packit 6f3914
        :raises exceptions.IOError: if the file cannot be opened
Packit 6f3914
Packit 6f3914
        """
Packit 6f3914
        with tempfile.TemporaryFile('wt+') as copy:
Packit 6f3914
            with open(name, 'rt') as original:
Packit 6f3914
                shutil.copyfileobj(original, copy)
Packit 6f3914
            copy.seek(0)
Packit 6f3914
            yield copy
Packit 6f3914
Packit 6f3914
    @contextlib.contextmanager
Packit 6f3914
    def _patch_file(self):
Packit 6f3914
        """Temporarily set :const:`__file__` to point to the testing repo.
Packit 6f3914
Packit 6f3914
        :returns: a context manager
Packit 6f3914
        :rtype: contextmanager
Packit 6f3914
Packit 6f3914
        """
Packit 6f3914
        filename = os.path.join(
Packit 6f3914
            self.repository.working_dir, 'scripts', 'update_releasenotes.py')
Packit 6f3914
        original = __file__
Packit 6f3914
        with tests.mock.patch('update_releasenotes.__file__', filename):
Packit 6f3914
            assert __file__ != original, 'double check that the patch works'
Packit 6f3914
            yield
Packit 6f3914
Packit 6f3914
    @contextlib.contextmanager
Packit 6f3914
    def _assert_logs(self, level, regex):
Packit 6f3914
        """Test whether a message matching an expression is logged.
Packit 6f3914
Packit 6f3914
        :param level: the level of the message
Packit 6f3914
        :type level: int
Packit 6f3914
        :param regex: the regular expression to be matched
Packit 6f3914
        :type regex: re.RegexObject
Packit 6f3914
        :returns: a context manager which represents the block in which
Packit 6f3914
           the message should be logged
Packit 6f3914
        :rtype: contextmanager
Packit 6f3914
        :raises exceptions.AssertionError: if the test fails
Packit 6f3914
Packit 6f3914
        """
Packit 6f3914
        class Handler(logging.Handler):
Packit 6f3914
            def __init__(self, regex):
Packit 6f3914
                super(Handler, self).__init__()
Packit 6f3914
                self.regex = regex
Packit 6f3914
                self.found = False
Packit 6f3914
Packit 6f3914
            def emit(self, record):
Packit 6f3914
                if not self.found:
Packit 6f3914
                    self.found = self.regex.match(record.getMessage())
Packit 6f3914
        handler = Handler(regex)
Packit 6f3914
        LOGGER.setLevel(level)
Packit 6f3914
        LOGGER.addHandler(handler)
Packit 6f3914
        yield
Packit 6f3914
        self.assertTrue(handler.found)
Packit 6f3914
Packit 6f3914
    @contextlib.contextmanager
Packit 6f3914
    def _assert_prints(self, regex, stream):
Packit 6f3914
        """Test whether a message matching an expression is printed.
Packit 6f3914
Packit 6f3914
        :param regex: the regular expression to be matched
Packit 6f3914
        :type regex: re.RegexObject
Packit 6f3914
        :param stream: a name of the stream to be matched
Packit 6f3914
        :type stream: str
Packit 6f3914
        :returns: a context manager which represents the block in which
Packit 6f3914
           the message should be printed
Packit 6f3914
        :rtype: contextmanager
Packit 6f3914
        :raises exceptions.AssertionError: if the test fails
Packit 6f3914
Packit 6f3914
        """
Packit 6f3914
        with tests.mock.patch(stream, io.BytesIO()) as mock:
Packit 6f3914
            yield
Packit 6f3914
            self.assertRegexpMatches(mock.getvalue(), regex)
Packit 6f3914
Packit 6f3914
    def _assert_iter_equal(self, actual, expected):
Packit 6f3914
        """Test whether two iterables are equal.
Packit 6f3914
Packit 6f3914
        :param actual: one of the iterables
Packit 6f3914
        :type actual: collections.Iterable[object]
Packit 6f3914
        :param expected: the other iterable
Packit 6f3914
        :type expected: collections.Iterable[object]
Packit 6f3914
        :raises exceptions.AssertionError: if the test fails
Packit 6f3914
Packit 6f3914
        """
Packit 6f3914
        self.assertTrue(all(
Packit 6f3914
            actual_ == expected_ for actual_, expected_ in
Packit 6f3914
            itertools.izip_longest(actual, expected, fillvalue=object())))
Packit 6f3914
Packit 6f3914
    def test_detect_repository(self):
Packit 6f3914
        """Test whether correct repository is detected.
Packit 6f3914
Packit 6f3914
        :raises exceptions.AssertionError: if the test fails
Packit 6f3914
Packit 6f3914
        """
Packit 6f3914
        with self._patch_file():
Packit 6f3914
            self.assertEqual(
Packit 6f3914
                detect_repository().working_dir, self.repository.working_dir)
Packit 6f3914
Packit 6f3914
    def test_detect_version(self):
Packit 6f3914
        """Test whether correct version is detected.
Packit 6f3914
Packit 6f3914
        :raises exceptions.AssertionError: if the test fails
Packit 6f3914
Packit 6f3914
        """
Packit 6f3914
        self.assertEqual(detect_version(self.repository), self.VERSION)
Packit 6f3914
Packit 6f3914
    def test_find_tag_name(self):
Packit 6f3914
        """Test whether correct tag is detected.
Packit 6f3914
Packit 6f3914
        :raises exceptions.AssertionError: if the test fails
Packit 6f3914
Packit 6f3914
        """
Packit 6f3914
        self.assertEqual(find_tag(self.repository, '1.0.1'), 'dnf-1.0.1-1')
Packit 6f3914
Packit 6f3914
    def test_find_tag_none(self):
Packit 6f3914
        """Test whether correct tag is detected.
Packit 6f3914
Packit 6f3914
        :raises exceptions.AssertionError: if the test fails
Packit 6f3914
Packit 6f3914
        """
Packit 6f3914
        self.assertIsNone(find_tag(self.repository, '9999'))
Packit 6f3914
Packit 6f3914
    def test_iter_unreleased_commits(self):
Packit 6f3914
        """Test whether correct commits are yielded.
Packit 6f3914
Packit 6f3914
        :raises exceptions.AssertionError: if the test fails
Packit 6f3914
Packit 6f3914
        """
Packit 6f3914
        commits = iter_unreleased_commits(self.repository)
Packit 6f3914
        self.assertItemsEqual(
Packit 6f3914
            (commit.hexsha for commit in commits), [self.commit.hexsha])
Packit 6f3914
Packit 6f3914
    def test_parse_issues(self):
Packit 6f3914
        """Test whether correct issues are yielded.
Packit 6f3914
Packit 6f3914
        :raises exceptions.AssertionError: if the test fails
Packit 6f3914
Packit 6f3914
        """
Packit 6f3914
        self.assertItemsEqual(parse_issues([self.commit]), [self.ISSUE])
Packit 6f3914
Packit 6f3914
    def test_extend_releases_extend(self):
Packit 6f3914
        """Test whether the release version is extended.
Packit 6f3914
Packit 6f3914
        :raises exceptions.AssertionError: if the test fails
Packit 6f3914
Packit 6f3914
        """
Packit 6f3914
        releases = [
Packit 6f3914
            (None, 'd1\n', [], ''),
Packit 6f3914
            ('1.0.1', '\nd2\n', ['234567'], '\n'),
Packit 6f3914
            ('999.9.9', '\nd3\n', ['345678'], '\n')]
Packit 6f3914
        expected = [
Packit 6f3914
            (None, 'd1\n', [], ''),
Packit 6f3914
            ('1.0.1', '\nd2\n', ['456789', '234567'], '\n'),
Packit 6f3914
            ('999.9.9', '\nd3\n', ['345678'], '\n')]
Packit 6f3914
        self.assertItemsEqual(
Packit 6f3914
            extend_releases(releases, '1.0.1', ['456789']), expected)
Packit 6f3914
Packit 6f3914
    def test_extend_releases_skip(self):
Packit 6f3914
        """Test whether the already present issues are skipped.
Packit 6f3914
Packit 6f3914
        :raises exceptions.AssertionError: if the test fails
Packit 6f3914
Packit 6f3914
        """
Packit 6f3914
        releases = [
Packit 6f3914
            (None, 'd1\n', [], ''),
Packit 6f3914
            ('1.0.1', '\nd2\n', ['234567'], '\n'),
Packit 6f3914
            ('999.9.9', '\nd3\n', ['345678'], '\n')]
Packit 6f3914
        expected = [
Packit 6f3914
            (None, 'd1\n', [], ''),
Packit 6f3914
            ('1.0.1', '\nd2\n', ['234567'], '\n'),
Packit 6f3914
            ('999.9.9', '\nd3\n', ['345678'], '\n')]
Packit 6f3914
        self.assertItemsEqual(
Packit 6f3914
            extend_releases(releases, '1.0.1', ['234567']), expected)
Packit 6f3914
Packit 6f3914
    def test_extend_releases_append(self):
Packit 6f3914
        """Test whether the rel. version is appended to the beginning.
Packit 6f3914
Packit 6f3914
        :raises exceptions.AssertionError: if the test fails
Packit 6f3914
Packit 6f3914
        """
Packit 6f3914
        releases = [
Packit 6f3914
            (None, 'd1\n', [], ''),
Packit 6f3914
            ('1.0.1', '\nd2\n', ['234567'], '\n')]
Packit 6f3914
        expected = [
Packit 6f3914
            (None, 'd1\n', [], ''),
Packit 6f3914
            ('999.9.9', '\n', ['345678'], '\n'),
Packit 6f3914
            ('1.0.1', '\nd2\n', ['234567'], '\n')]
Packit 6f3914
        self.assertItemsEqual(
Packit 6f3914
            extend_releases(releases, '999.9.9', ['345678']), expected)
Packit 6f3914
Packit 6f3914
    def test_format_release_version(self):
Packit 6f3914
        """Test whether correct string is returned.
Packit 6f3914
Packit 6f3914
        :raises exceptions.AssertionError: if the test fails
Packit 6f3914
Packit 6f3914
        """
Packit 6f3914
        self.assertEqual(
Packit 6f3914
            format_release('1.0.1', '\ndesc\n', ['123456', '234567'], '\ne\n'),
Packit 6f3914
            '===================\n'
Packit 6f3914
            '1.0.1 Release Notes\n'
Packit 6f3914
            '===================\n'
Packit 6f3914
            '\n'
Packit 6f3914
            'desc\n'
Packit 6f3914
            '* :rhbug:`123456`\n'
Packit 6f3914
            '* :rhbug:`234567`\n'
Packit 6f3914
            '\n'
Packit 6f3914
            'e\n')
Packit 6f3914
Packit 6f3914
    def test_format_release_none(self):
Packit 6f3914
        """Test whether no version is handled properly.
Packit 6f3914
Packit 6f3914
        :raises exceptions.AssertionError: if the test fails
Packit 6f3914
Packit 6f3914
        """
Packit 6f3914
        self.assertEqual(format_release(None, 'l1\nl2\n', [], ''), 'l1\nl2\n')
Packit 6f3914
Packit 6f3914
    def test_update_notes_append(self):
Packit 6f3914
        """Test whether the rel. version is appended to the beginning.
Packit 6f3914
Packit 6f3914
        :raises exceptions.AssertionError: if the test fails
Packit 6f3914
Packit 6f3914
        """
Packit 6f3914
        regex = re.compile(
Packit 6f3914
            '^Update finished. See the diff to the Git index:\n.+')
Packit 6f3914
        notesfn = os.path.join(self.repository.working_dir, NOTES_FN)
Packit 6f3914
        title = TITLE_PAT.format(version=self.VERSION)
Packit 6f3914
        extra = [
Packit 6f3914
            '\n',
Packit 6f3914
            '{}\n'.format('=' * (len(title) - 1)),
Packit 6f3914
            title,
Packit 6f3914
            '{}\n'.format('=' * (len(title) - 1)),
Packit 6f3914
            '\n',
Packit 6f3914
            ISSUE_PAT.format(number=self.ISSUE)]
Packit 6f3914
        with self._read_copy(notesfn) as original:
Packit 6f3914
            # Insert the extra lines right after the 22nd line.
Packit 6f3914
            expected = itertools.chain(
Packit 6f3914
                itertools.islice(original, 0, 22), extra, original)
Packit 6f3914
            with self._patch_file(), self._assert_logs(logging.INFO, regex):
Packit 6f3914
                update_notes()
Packit 6f3914
            with open(notesfn, 'rt') as actual:
Packit 6f3914
                self._assert_iter_equal(actual, expected)
Packit 6f3914
Packit 6f3914
    def test_update_notes_released(self):
Packit 6f3914
        """Test whether the released version is detected.
Packit 6f3914
Packit 6f3914
        :raises exceptions.AssertionError: if the test fails
Packit 6f3914
Packit 6f3914
        """
Packit 6f3914
        self.repository.create_tag(TAG_PAT.format(version=self.VERSION))
Packit 6f3914
        with self._patch_file():
Packit 6f3914
            self.assertRaises(ValueError, update_notes)
Packit 6f3914
Packit 6f3914
    def test_main_append(self):
Packit 6f3914
        """Test whether the release version is appended to the end.
Packit 6f3914
Packit 6f3914
        :raises exceptions.AssertionError: if the test fails
Packit 6f3914
Packit 6f3914
        """
Packit 6f3914
        regex = re.compile(
Packit 6f3914
            '^INFO Update finished. See the diff to the Git index:\n.+', re.M)
Packit 6f3914
        notesfn = os.path.join(self.repository.working_dir, NOTES_FN)
Packit 6f3914
        title = TITLE_PAT.format(version=self.VERSION)
Packit 6f3914
        extra = [
Packit 6f3914
            '\n',
Packit 6f3914
            '{}\n'.format('=' * (len(title) - 1)),
Packit 6f3914
            title,
Packit 6f3914
            '{}\n'.format('=' * (len(title) - 1)),
Packit 6f3914
            '\n',
Packit 6f3914
            ISSUE_PAT.format(number=self.ISSUE)]
Packit 6f3914
        with self._read_copy(notesfn) as original:
Packit 6f3914
            # Insert the extra lines right after the 22nd line.
Packit 6f3914
            expected = itertools.chain(
Packit 6f3914
                itertools.islice(original, 0, 22), extra, original)
Packit 6f3914
            with \
Packit 6f3914
                    tests.mock.patch('sys.argv', ['prog']), \
Packit 6f3914
                    self._patch_file(), \
Packit 6f3914
                    self._assert_prints(regex, 'sys.stderr'):
Packit 6f3914
                main()
Packit 6f3914
            with open(notesfn, 'rt') as actual:
Packit 6f3914
                self._assert_iter_equal(actual, expected)
Packit 6f3914
Packit 6f3914
    def test_main_released(self):
Packit 6f3914
        """Test whether the released version is detected.
Packit 6f3914
Packit 6f3914
        :raises exceptions.AssertionError: if the test fails
Packit 6f3914
Packit 6f3914
        """
Packit 6f3914
        self.repository.create_tag(TAG_PAT.format(version=self.VERSION))
Packit 6f3914
        with \
Packit 6f3914
                tests.mock.patch('sys.argv', ['prog']), self._patch_file(), \
Packit 6f3914
                self.assertRaises(SystemExit) as context:
Packit 6f3914
            main()
Packit 6f3914
        self.assertNotEqual(context.exception.code, 0)
Packit 6f3914
Packit 6f3914
    def test_releasenotesparser(self):
Packit 6f3914
        """Test whether correct release notes are yielded.
Packit 6f3914
Packit 6f3914
        :raises exceptions.AssertionError: if the test fails
Packit 6f3914
Packit 6f3914
        """
Packit 6f3914
        notesfn = os.path.join(self.repository.working_dir, NOTES_FN)
Packit 6f3914
        parser = ReleaseNotesParser()
Packit 6f3914
        descriptions = [
Packit 6f3914
            # None
Packit 6f3914
            '..\n'
Packit 6f3914
            '  Copyright (C) 2014  Red Hat, Inc.\n'
Packit 6f3914
            '\n'
Packit 6f3914
            '  This copyrighted material is made available to anyone wishing '
Packit 6f3914
            'to use,\n  modify, copy, or redistribute it subject to the terms '
Packit 6f3914
            'and conditions of\n  the GNU General Public License v.2, or (at '
Packit 6f3914
            'your option) any later version.\n  This program is distributed in'
Packit 6f3914
            ' the hope that it will be useful, but WITHOUT\n  ANY WARRANTY '
Packit 6f3914
            'expressed or implied, including the implied warranties of\n  '
Packit 6f3914
            'MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU'
Packit 6f3914
            ' General\n  Public License for more details.  You should have '
Packit 6f3914
            'received a copy of the\n  GNU General Public License along with '
Packit 6f3914
            'this program; if not, write to the\n  Free Software Foundation, '
Packit 6f3914
            'Inc., 51 Franklin Street, Fifth Floor, Boston, MA\n  02110-1301, '
Packit 6f3914
            'USA.  Any Red Hat trademarks that are incorporated in the\n  '
Packit 6f3914
            'source code or documentation are not subject to the GNU General '
Packit 6f3914
            'Public\n  License and may only be used or replicated with the '
Packit 6f3914
            'express permission of\n  Red Hat, Inc.\n'
Packit 6f3914
            '\n'
Packit 6f3914
            '###################\n'
Packit 6f3914
            ' DNF Release Notes\n'
Packit 6f3914
            '###################\n'
Packit 6f3914
            '\n'
Packit 6f3914
            '.. contents::\n'
Packit 6f3914
            '\n',
Packit 6f3914
            # 0.3.1
Packit 6f3914
            '\n0.3.1 brings mainly changes to the automatic metadata '
Packit 6f3914
            'synchronization. In\nFedora, ``dnf makecache`` is triggered via '
Packit 6f3914
            'SystemD timers now and takes an\noptional ``background`` '
Packit 6f3914
            'extra-argument to run in resource-considerate mode (no\nsyncing '
Packit 6f3914
            'when running on laptop battery, only actually performing the '
Packit 6f3914
            'check at\nmost once every three hours). Also, the IO and CPU '
Packit 6f3914
            'priorities of the\ntimer-triggered process are lowered now and '
Packit 6f3914
            "shouldn't as noticeably impact the\nsystem's performance.\n\nThe "
Packit 6f3914
            'administrator can also easily disable the automatic metadata '
Packit 6f3914
            'updates by\nsetting :ref:`metadata_timer_sync '
Packit 6f3914
            '<metadata_timer_sync-label>` to 0.\n\nThe default value of '
Packit 6f3914
            ':ref:`metadata_expire <metadata_expire-label>` was\nincreased '
Packit 6f3914
            'from 6 hours to 48 hours. In Fedora, the repos usually set this\n'
Packit 6f3914
            'explicitly so this change is not going to cause much impact.\n\n'
Packit 6f3914
            'The following reported issues are fixed in this release:\n\n',
Packit 6f3914
            # 0.3.2
Packit 6f3914
            '\nThe major improvement in this version is in speeding up syncing'
Packit 6f3914
            ' of repositories\nusing metalink by looking at the repomd.xml '
Packit 6f3914
            'checksums. This effectively lets DNF\ncheaply refresh expired '
Packit 6f3914
            'repositories in cases where the original has not\nchanged\\: for '
Packit 6f3914
            'instance the main Fedora repository is refreshed with one 30 kB\n'
Packit 6f3914
            'HTTP download. This functionality is present in the current Yum '
Packit 6f3914
            "but hasn't\nworked in DNF since 3.0.0.\n\nOtherwise this is "
Packit 6f3914
            'mainly a release fixing bugs and tracebacks. The following\n'
Packit 6f3914
            'reported bugs are fixed:\n\n',
Packit 6f3914
            # 0.3.3
Packit 6f3914
            '\nThe improvements in 0.3.3 are only API changes to the logging. '
Packit 6f3914
            'There is a new\nmodule ``dnf.logging`` that defines simplified '
Packit 6f3914
            'logging structure compared to\nYum, with fewer logging levels and'
Packit 6f3914
            ' `simpler usage for the developers\n
Packit 6f3914
            'rpm-software-management/dnf/wiki/Hacking#logging>`_. The RPM '
Packit 6f3914
            'transaction logs are\nno longer in ``/var/log/dnf.transaction.log'
Packit 6f3914
            '`` but in ``/var/log/dnf.rpm.log`` by\ndefault.\n\nThe exception '
Packit 6f3914
            'classes were simplified and moved to ``dnf.exceptions``.\n\nThe '
Packit 6f3914
            'following bugs are fixed in 0.3.3:\n\n',
Packit 6f3914
            # 0.3.4
Packit 6f3914
            '\n0.3.4 is the first DNF version since the fork from Yum that is '
Packit 6f3914
            'able to\nmanipulate the comps data. In practice, ``dnf group '
Packit 6f3914
            'install <group name>`` works\nagain. No other group commands are '
Packit 6f3914
            'supported yet.\n\nSupport for ``librepo-0.0.4`` and related '
Packit 6f3914
            'cleanups and extensions this new\nversion allows are included '
Packit 6f3914
            '(see the buglist below)\n\nThis version has also improved '
Packit 6f3914
            'reporting of obsoleted packages in the CLI (the\nYum-style '
Packit 6f3914
            '"replacing <package-nevra>" appears in the textual transaction\n'
Packit 6f3914
            'overview).\n\nThe following bugfixes are included in 0.3.4:\n\n',
Packit 6f3914
            # 0.3.5
Packit 6f3914
            '\nBesides few fixed bugs this version should not present any '
Packit 6f3914
            'differences for the\nuser. On the inside, the transaction '
Packit 6f3914
            'managing mechanisms have changed\ndrastically, bringing code '
Packit 6f3914
            'simplification, better maintainability and better\ntestability.\n'
Packit 6f3914
            '\nIn Fedora, there is a change in the spec file effectively '
Packit 6f3914
            'preventing the\nmakecache timer from running *immediately after '
Packit 6f3914
            'installation*. The timer\nservice is still enabled by default, '
Packit 6f3914
            'but unless the user starts it manually with\n``systemctl start '
Packit 6f3914
            'dnf-makecache.timer`` it will not run until after the first\n'
Packit 6f3914
            'reboot. This is in alignment with Fedora packaging best '
Packit 6f3914
            'practices.\n\nThe following bugfixes are included in 0.3.5:\n\n',
Packit 6f3914
            # 0.3.6
Packit 6f3914
            '\nThis is a bugfix release, including the following fixes:\n\n',
Packit 6f3914
            # 0.3.7
Packit 6f3914
            '\nThis is a bugfix release:\n\n',
Packit 6f3914
            # 0.3.8
Packit 6f3914
            '\nA new locking module has been integrated in this version, '
Packit 6f3914
            'clients should see the\nmessage about DNF lock being taken less '
Packit 6f3914
            'often.\n\nPanu Matilainen has submitted many patches to this '
Packit 6f3914
            'release to cleanup the RPM\ninterfacing modules.\n\nThe following'
Packit 6f3914
            ' bugs are fixed in this release:\n\n',
Packit 6f3914
            # 0.3.9
Packit 6f3914
            '\nThis is a quick bugfix release dealing with reported bugs and '
Packit 6f3914
            'tracebacks:\n\n',
Packit 6f3914
            # 0.3.10
Packit 6f3914
            '\nThe only major change is that ``skip_if_unavailable`` is '
Packit 6f3914
            ':ref:`enabled by\ndefault now <skip_if_unavailable_default>`.\n\n'
Packit 6f3914
            'A minor release otherwise, mainly to get a new version of DNF out'
Packit 6f3914
            ' that uses a\nfresh librepo. The following issues are now a thing'
Packit 6f3914
            ' of the past:\n\n',
Packit 6f3914
            # 0.3.11
Packit 6f3914
            '\nThe default multilib policy configuration value is ``best`` '
Packit 6f3914
            'now. This does not\npose any change for the Fedora users because '
Packit 6f3914
            'exactly the same default had been\npreviously achieved by a '
Packit 6f3914
            'setting in ``/etc/dnf/dnf.conf`` shipped with the\nFedora '
Packit 6f3914
            'package.\n\nAn important fix to the repo module speeds up package'
Packit 6f3914
            ' downloads again is present\nin this release. The full list of '
Packit 6f3914
            'fixes is:\n\n',
Packit 6f3914
            # 0.4.0
Packit 6f3914
            '\nThe new minor version brings many internal changes to the comps'
Packit 6f3914
            ' code, most comps\nparsing and processing is now delegated to '
Packit 6f3914
            '`libcomps\n<https://github.com/midnightercz/libcomps>`_ by '
Packit 6f3914
            'Jind\xc5\x99ich Lu\xc5\xbea.\n\nThe ``overwrite_groups`` config '
Packit 6f3914
            'option has been dropped in this version and DNF\nacts if it was '
Packit 6f3914
            '0, that is groups with the same name are merged together.\n\nThe '
Packit 6f3914
            'currently supported groups commands (``group list`` and '
Packit 6f3914
            '``group install``)\nare documented on the manpage now.\n\nThe '
Packit 6f3914
            '0.4.0 version is the first one supported by the DNF Payload for '
Packit 6f3914
            'Anaconda and\nmany changes since 0.3.11 make that possible by '
Packit 6f3914
            'cleaning up the API and making\nit more sane (cleanup of '
Packit 6f3914
            '``yumvars`` initialization API, unifying the RPM\ntransaction '
Packit 6f3914
            'callback objects hierarchy, slimming down ``dnf.rpmUtils.arch``,'
Packit 6f3914
            '\nimproved logging).\n\nFixes for the following are contained in '
Packit 6f3914
            'this version:\n\n',
Packit 6f3914
            # 0.4.1
Packit 6f3914
            '\nThe focus of this release was to support our efforts in '
Packit 6f3914
            'implementing the DNF\nPayload for Anaconda, with changes on the '
Packit 6f3914
            'API side of things (better logging,\nnew ``Base.reset()`` '
Packit 6f3914
            'method).\n\nSupport for some irrelevant config options has been '
Packit 6f3914
            'dropped (``kernelpkgnames``,\n``exactarch``, '
Packit 6f3914
            '``rpm_check_debug``). We also no longer detect metalinks in the\n'
Packit 6f3914
            '``mirrorlist`` option (see `Fedora bug 948788\n'
Packit 6f3914
            '<https://bugzilla.redhat.com/show_bug.cgi?id=948788>`_).\n\nDNF '
Packit 6f3914
            'is on its way to drop the urlgrabber dependency and the first set'
Packit 6f3914
            ' of patches\ntowards this goal is already in.\n\nExpect the '
Packit 6f3914
            'following bugs to go away with upgrade to 0.4.1:\n\n',
Packit 6f3914
            # 0.4.2
Packit 6f3914
            '\nDNF now downloads packages for the transaction in parallel with'
Packit 6f3914
            ' progress bars\nupdated to effectively represent this. Since so '
Packit 6f3914
            'many things in the downloading\ncode were changing, we figured it'
Packit 6f3914
            ' was a good idea to finally drop urlgrabber\ndependency at the '
Packit 6f3914
            "same time. Indeed, this is the first version that doesn't\n"
Packit 6f3914
            'require urlgrabber for neither build nor run.\n\nSimilarly, since'
Packit 6f3914
            ' `librepo started to support this\n
Packit 6f3914
            'librepo/commit/acf458f29f7234d2d8d93a68391334343beae4b9>`_,\n'
Packit 6f3914
            'downloads in DNF now use the fastests mirrors available by '
Packit 6f3914
            "default.\n\nThe option to :ref:`specify repositories' costs "
Packit 6f3914
            '<repo_cost-label>` has been\nreadded.\n\nInternally, DNF has seen'
Packit 6f3914
            ' first part of ongoing refactorings of the basic\noperations '
Packit 6f3914
            '(install, update) as well as a couple of new API methods '
Packit 6f3914
            'supporting\ndevelopment of extensions.\n\nThese bugzillas are '
Packit 6f3914
            'fixed in 0.4.2:\n\n',
Packit 6f3914
            # 0.4.3
Packit 6f3914
            '\nThis is an early release to get the latest DNF out with the '
Packit 6f3914
            'latest librepo\nfixing the `Too many open files\n'
Packit 6f3914
            '<https://bugzilla.redhat.com/show_bug.cgi?id=1015957>`_ bug.\n\n'
Packit 6f3914
            'In Fedora, the spec file has been updated to no longer depend on '
Packit 6f3914
            'precise\nversions of the libraries so in the future they can be '
Packit 6f3914
            'released\nindependently.\n\nThis release sees the finished '
Packit 6f3914
            'refactoring in error handling during basic\noperations and adds '
Packit 6f3914
            'support for ``group remove`` and ``group info`` commands,\ni.e. '
Packit 6f3914
            'the following two bugs:\n\n',
Packit 6f3914
            # 0.4.4
Packit 6f3914
            '\nThe initial support for Python 3 in DNF has been merged in this'
Packit 6f3914
            ' version. In\npractice one can not yet run the ``dnf`` command in'
Packit 6f3914
            ' Py3 but the unit tests\nalready pass there. We expect to give '
Packit 6f3914
            'Py3 and DNF heavy testing during the\nFedora 21 development cycle'
Packit 6f3914
            ' and eventually switch to it as the default. The plan\nis to drop'
Packit 6f3914
            ' Python 2 support as soon as Anaconda is running in Python 3.\n\n'
Packit 6f3914
            'Minor adjustments to allow Anaconda support also happened during '
Packit 6f3914
            'the last week,\nas well as a fix to a possibly severe bug that '
Packit 6f3914
            'one is however not really likely\nto see with non-devel Fedora '
Packit 6f3914
            'repos:\n\n',
Packit 6f3914
            # 0.4.5
Packit 6f3914
            '\nA serious bug causing `tracebacks during package downloads\n'
Packit 6f3914
            '<https://bugzilla.redhat.com/show_bug.cgi?id=1021087>`_ made it '
Packit 6f3914
            'into 0.4.4 and\nthis release contains a fix for that. Also, a '
Packit 6f3914
            'basic proxy support has been\nreadded now.\n\nBugs fixed in '
Packit 6f3914
            '0.4.5:\n\n',
Packit 6f3914
            # 0.4.6
Packit 6f3914
            '\n0.4.6 brings two new major features. Firstly, it is the revival'
Packit 6f3914
            ' of ``history\nundo``, so transactions can be reverted now.  '
Packit 6f3914
            'Secondly, DNF will now limit the\nnumber of installed kernels and'
Packit 6f3914
            ' *installonly* packages in general to the number\nspecified by '
Packit 6f3914
            ':ref:`installonly_limit <installonly-limit-label>` configuration'
Packit 6f3914
            '\noption.\n\nDNF now supports the ``group summary`` command and '
Packit 6f3914
            'one-word group commands no\nlonger cause tracebacks, e.g. '
Packit 6f3914
            '``dnf grouplist``.\n\nThere are vast internal changes to '
Packit 6f3914
            '``dnf.cli``, the subpackage that provides CLI\nto DNF. In '
Packit 6f3914
            'particular, it is now better separated from the core.\n\nThe '
Packit 6f3914
            'hawkey library used against DNF from with this versions uses a '
Packit 6f3914
            '`recent RPMDB\nloading optimization in libsolv\n'
Packit 6f3914
            '<https://github.com/openSUSE/libsolv/commit/843dc7e1>`_ that '
Packit 6f3914
            'shortens DNF\nstartup by seconds when the cached RPMDB is '
Packit 6f3914
            'invalid.\n\nWe have also added further fixes to support Python 3 '
Packit 6f3914
            "and enabled `librepo's\nfastestmirror caching optimization\n"
Packit 6f3914
            '
Packit 6f3914
            'b8a063763ccd8a84b8ec21a643461eaace9b9c08>`_\nto tighten the '
Packit 6f3914
            'download times even more.\n\nBugs fixed in 0.4.6:\n\n',
Packit 6f3914
            # 0.4.7
Packit 6f3914
            '\nWe start to publish the :doc:`api` with this release. It is '
Packit 6f3914
            'largely\nincomprehensive at the moment, yet outlines the shape of'
Packit 6f3914
            ' the documentation and\nthe process the project is going to use '
Packit 6f3914
            'to maintain it.\n\nThere are two Yum configuration options that '
Packit 6f3914
            'were dropped: :ref:`group_package_types '
Packit 6f3914
            '<group_package_types_dropped>` and '
Packit 6f3914
            ':ref:`upgrade_requirements_on_install '
Packit 6f3914
            '<upgrade_requirements_on_install_dropped>`.\n\nBugs fixed in '
Packit 6f3914
            '0.4.7:\n\n',
Packit 6f3914
            # 0.4.8
Packit 6f3914
            '\nThere are mainly internal changes, new API functions and '
Packit 6f3914
            'bugfixes in this release.\n\nPython 3 is fully supported now, the'
Packit 6f3914
            ' Fedora builds include the Py3 variant. The DNF program still '
Packit 6f3914
            'runs under Python 2.7 but the extension authors can now choose '
Packit 6f3914
            'what Python they prefer to use.\n\nThis is the first version of '
Packit 6f3914
            'DNF that deprecates some of its API. Clients using deprecated '
Packit 6f3914
            'code will see a message emitted to stderr using the standard '
Packit 6f3914
            '`Python warnings module '
Packit 6f3914
            '<http://docs.python.org/3.3/library/warnings.html>`_. You can '
Packit 6f3914
            'filter out :exc:`dnf.exceptions.DeprecationWarning` to suppress '
Packit 6f3914
            'them.\n\nAPI additions in 0.4.8:\n\n* :attr:`dnf.Base.sack`\n* '
Packit 6f3914
            ':attr:`dnf.conf.Conf.cachedir`\n* '
Packit 6f3914
            ':attr:`dnf.conf.Conf.config_file_path`\n* '
Packit 6f3914
            ':attr:`dnf.conf.Conf.persistdir`\n* :meth:`dnf.conf.Conf.read`\n*'
Packit 6f3914
            ' :class:`dnf.package.Package`\n* :class:`dnf.query.Query`\n* '
Packit 6f3914
            ':class:`dnf.subject.Subject`\n* :meth:`dnf.repo.Repo.__init__`\n*'
Packit 6f3914
            ' :class:`dnf.sack.Sack`\n* :class:`dnf.selector.Selector`\n* '
Packit 6f3914
            ':class:`dnf.transaction.Transaction`\n\nAPI deprecations in '
Packit 6f3914
            '0.4.8:\n\n* :mod:`dnf.queries` is deprecated now. If you need to '
Packit 6f3914
            'create instances of :class:`.Subject`, import it from '
Packit 6f3914
            ':mod:`dnf.subject`. To create :class:`.Query` instances it is '
Packit 6f3914
            'recommended to use :meth:`sack.query() <dnf.sack.Sack.query>`.\n'
Packit 6f3914
            '\nBugs fixed in 0.4.8:\n\n',
Packit 6f3914
            # 0.4.9
Packit 6f3914
            '\nSeveral Yum features are revived in this release. '
Packit 6f3914
            '``dnf history rollback`` now works again. The '
Packit 6f3914
            '``history userinstalled`` has been added, it displays a list of '
Packit 6f3914
            'ackages that the user manually selected for installation on an '
Packit 6f3914
            'installed system and does not include those packages that got '
Packit 6f3914
            "installed as dependencies.\n\nWe're happy to announce that the "
Packit 6f3914
            'API in 0.4.9 has been extended to finally support plugins. There '
Packit 6f3914
            'is a limited set of plugin hooks now, we will carefully add new '
Packit 6f3914
            'ones in the following releases. New marking operations have ben '
Packit 6f3914
            'added to the API and also some configuration options.\n\nAn '
Packit 6f3914
            'alternative to ``yum shell`` is provided now for its most common '
Packit 6f3914
            'use case: :ref:`replacing a non-leaf package with a conflicting '
Packit 6f3914
            'package <allowerasing_instead_of_shell>` is achieved by using the'
Packit 6f3914
            ' ``--allowerasing`` switch now.\n\nAPI additions in 0.4.9:\n\n* '
Packit 6f3914
            ':doc:`api_plugins`\n* :ref:`logging_label`\n* '
Packit 6f3914
            ':meth:`.Base.read_all_repos`\n* :meth:`.Base.reset`\n* '
Packit 6f3914
            ':meth:`.Base.downgrade`\n* :meth:`.Base.remove`\n* '
Packit 6f3914
            ':meth:`.Base.upgrade`\n* :meth:`.Base.upgrade_all`\n* '
Packit 6f3914
            ':attr:`.Conf.pluginpath`\n* :attr:`.Conf.reposdir`\n\nAPI '
Packit 6f3914
            'deprecations in 0.4.9:\n\n* :exc:`.PackageNotFoundError` is '
Packit 6f3914
            'deprecated for public use. Please catch :exc:`.MarkingError` '
Packit 6f3914
            'instead.\n* It is deprecated to use :meth:`.Base.install` return '
Packit 6f3914
            'value for anything. The method either returns or raises an '
Packit 6f3914
            'exception.\n\nBugs fixed in 0.4.9:\n\n',
Packit 6f3914
            # 0.4.10
Packit 6f3914
            '\n0.4.10 is a bugfix release that also adds some long-requested '
Packit 6f3914
            'CLI features and extends the plugin support with two new plugin '
Packit 6f3914
            'hooks. An important feature for plugin developers is going to be '
Packit 6f3914
            "the possibility to register plugin's own CLI command, available "
Packit 6f3914
            'from this version.\n\n``dnf history`` now recognizes ``last`` as '
Packit 6f3914
            'a special argument, just like other history commands.\n\n'
Packit 6f3914
            '``dnf install`` now accepts group specifications via the ``@`` '
Packit 6f3914
            'character.\n\nSupport for the ``--setopt`` option has been '
Packit 6f3914
            'readded from Yum.\n\nAPI additions in 0.4.10:\n\n* '
Packit 6f3914
            ':doc:`api_cli`\n* :attr:`.Plugin.name`\n* '
Packit 6f3914
            ':meth:`.Plugin.__init__` now specifies the second parameter as an'
Packit 6f3914
            ' instance of `.cli.Cli`\n* :meth:`.Plugin.sack`\n* '
Packit 6f3914
            ':meth:`.Plugin.transaction`\n* :func:`.repo.repo_id_invalid`\n\n'
Packit 6f3914
            'API changes in 0.4.10:\n\n* Plugin authors must specify '
Packit 6f3914
            ':attr:`.Plugin.name` when authoring a plugin.\n\nBugs fixed in '
Packit 6f3914
            '0.4.10:\n\n',
Packit 6f3914
            # 0.4.11
Packit 6f3914
            '\nThis is mostly a bugfix release following quickly after 0.4.10,'
Packit 6f3914
            ' with many updates to documentation.\n\nAPI additions in 0.4.11:'
Packit 6f3914
            '\n\n* :meth:`.Plugin.read_config`\n* :class:`.repo.Metadata`\n* '
Packit 6f3914
            ':attr:`.repo.Repo.metadata`\n\nAPI changes in 0.4.11:\n\n* '
Packit 6f3914
            ':attr:`.Conf.pluginpath` is no longer hard coded but depends on '
Packit 6f3914
            'the major Python version.\n\nBugs fixed in 0.4.11:\n\n',
Packit 6f3914
            # 0.4.12
Packit 6f3914
            '\nThis release disables fastestmirror by default as we received '
Packit 6f3914
            'many complains about it. There are also several bugfixes, most '
Packit 6f3914
            'importantly an issue has been fixed that caused packages '
Packit 6f3914
            'installed by Anaconda be removed together with a depending '
Packit 6f3914
            'package. It is now possible to use ``bandwidth`` and ``throttle``'
Packit 6f3914
            ' config values too.\n\nBugs fixed in 0.4.12:\n\n',
Packit 6f3914
            # 0.4.13
Packit 6f3914
            '\n0.4.13 finally ships support for `delta RPMS '
Packit 6f3914
            '<https://gitorious.org/deltarpm>`_. Enabling this can save some '
Packit 6f3914
            'bandwidth (and use some CPU time) when downloading packages for '
Packit 6f3914
            'updates.\n\nSupport for bash completion is also included in this '
Packit 6f3914
            'version. It is recommended to use the '
Packit 6f3914
            '``generate_completion_cache`` plugin to have the completion work '
Packit 6f3914
            'fast. This plugin will be also shipped with '
Packit 6f3914
            '``dnf-plugins-core-0.0.3``.\n\nThe '
Packit 6f3914
            ':ref:`keepcache <keepcache-label>` config option has been '
Packit 6f3914
            'readded.\n\nBugs fixed in 0.4.13:\n\n',
Packit 6f3914
            # 0.4.14
Packit 6f3914
            '\nThis quickly follows 0.4.13 to address the issue of crashes '
Packit 6f3914
            'when DNF output is piped into another program.\n\nAPI additions '
Packit 6f3914
            'in 0.4.14:\n\n* :attr:`.Repo.pkgdir`\n\nBugs fixed in 0.4.14:\n'
Packit 6f3914
            '\n',
Packit 6f3914
            # 0.4.15
Packit 6f3914
            '\nMassive refactoring of the downloads handling to provide better'
Packit 6f3914
            ' API for reporting download progress and fixed bugs are the main '
Packit 6f3914
            'things brought in 0.4.15.\n\nAPI additions in 0.4.15:\n\n* '
Packit 6f3914
            ':exc:`dnf.exceptions.DownloadError`\n* '
Packit 6f3914
            ':meth:`dnf.Base.download_packages` now takes the optional '
Packit 6f3914
            '`progress` parameter and can raise :exc:`.DownloadError`.\n* '
Packit 6f3914
            ':class:`dnf.callback.Payload`\n* '
Packit 6f3914
            ':class:`dnf.callback.DownloadProgress`\n* '
Packit 6f3914
            ':meth:`dnf.query.Query.filter` now also recognizes ``provides`` '
Packit 6f3914
            'as a filter name.\n\nBugs fixed in 0.4.15:\n\n',
Packit 6f3914
            # 0.4.16
Packit 6f3914
            '\nThe refactorings from 0.4.15 are introducing breakage causing '
Packit 6f3914
            'the background ``dnf makecache`` runs traceback. This release '
Packit 6f3914
            'fixes that.\n\nBugs fixed in 0.4.16:\n\n',
Packit 6f3914
            # 0.4.17
Packit 6f3914
            '\nThis release fixes many bugs in the downloads/DRPM CLI area. A '
Packit 6f3914
            'bug got fixed preventing a regular user from running read-only '
Packit 6f3914
            'operations using ``--cacheonly``. Another fix ensures that '
Packit 6f3914
            '``metadata_expire=never`` setting is respected. Lastly, the '
Packit 6f3914
            'release provides three requested API calls in the repo management'
Packit 6f3914
            ' area.\n\nAPI additions in 0.4.17:\n\n* '
Packit 6f3914
            ':meth:`dnf.repodict.RepoDict.all`\n* '
Packit 6f3914
            ':meth:`dnf.repodict.RepoDict.get_matching`\n* '
Packit 6f3914
            ':meth:`dnf.repo.Repo.set_progress_bar`\n\nBugs fixed in 0.4.17:\n'
Packit 6f3914
            '\n',
Packit 6f3914
            # 0.4.18
Packit 6f3914
            '\nSupport for ``dnf distro-sync <spec>`` finally arrives in this '
Packit 6f3914
            'version.\n\nDNF has moved to handling groups as objects,  tagged '
Packit 6f3914
            'installed/uninstalled independently from the actual installed '
Packit 6f3914
            'packages. This has been in Yum as the ``group_command=objects`` '
Packit 6f3914
            'setting and the default in recent Fedora releases. There are API '
Packit 6f3914
            'extensions related to this change as well as two new CLI '
Packit 6f3914
            'commands: ``group mark install`` and ``group mark remove``.\n\n'
Packit 6f3914
            'API items deprecated in 0.4.8 and 0.4.9 have been dropped in '
Packit 6f3914
            '0.4.18, in accordance with our :ref:`deprecating-label`.\n\nAPI '
Packit 6f3914
            'changes in 0.4.18:\n\n* :mod:`dnf.queries` has been dropped as '
Packit 6f3914
            'announced in `0.4.8 Release Notes`_\n* '
Packit 6f3914
            ':exc:`dnf.exceptions.PackageNotFoundError` has been dropped from '
Packit 6f3914
            'API as announced in `0.4.9 Release Notes`_\n* '
Packit 6f3914
            ':meth:`dnf.Base.install` no longer has to return the number of '
Packit 6f3914
            'marked packages as announced in `0.4.9 Release Notes`_\n\nAPI '
Packit 6f3914
            'deprecations in 0.4.18:\n\n* :meth:`dnf.Base.select_group` is '
Packit 6f3914
            'deprecated now. Please use :meth:`~.Base.group_install` instead.'
Packit 6f3914
            '\n\nAPI additions in 0.4.18:\n\n* :meth:`dnf.Base.group_install`'
Packit 6f3914
            '\n* :meth:`dnf.Base.group_remove`\n\nBugs fixed in 0.4.18:\n\n',
Packit 6f3914
            # 0.4.19
Packit 6f3914
            '\nArriving one week after 0.4.18, the 0.4.19 mainly provides a '
Packit 6f3914
            'fix to a traceback in group operations under non-root users.\n\n'
Packit 6f3914
            'DNF starts to ship separate translation files (.mo) starting with'
Packit 6f3914
            ' this release.\n\nBugs fixed in 0.4.19:\n\n',
Packit 6f3914
            # 0.5.0
Packit 6f3914
            '\nThe biggest improvement in 0.5.0 is complete support for groups'
Packit 6f3914
            ' `and environments '
Packit 6f3914
            '<https://bugzilla.redhat.com/show_bug.cgi?id=1063666>`_, '
Packit 6f3914
            'including internal database of installed groups independent of '
Packit 6f3914
            'the actual packages (concept known as groups-as-objects from '
Packit 6f3914
            'Yum). Upgrading groups is supported now with ``group upgrade`` '
Packit 6f3914
            'too.\n\nTo force refreshing of metadata before an operation (even'
Packit 6f3914
            ' if the data is not expired yet), `the refresh option has been '
Packit 6f3914
            'added <https://bugzilla.redhat.com/show_bug.cgi?id=1064226>`_.\n'
Packit 6f3914
            '\nInternally, the CLI went through several changes to allow for '
Packit 6f3914
            'better API accessibility like `granular requesting of root '
Packit 6f3914
            'permissions '
Packit 6f3914
            '<https://bugzilla.redhat.com/show_bug.cgi?id=1062889>`_.\n\nAPI '
Packit 6f3914
            'has got many more extensions, focusing on better manipulation '
Packit 6f3914
            'with comps and packages. There are new entries in '
Packit 6f3914
            ':doc:`cli_vs_yum` and :doc:`user_faq` too.\n\nSeveral resource '
Packit 6f3914
            'leaks (file descriptors, noncollectable Python objects) were '
Packit 6f3914
            'found and fixed.\n\nAPI changes in 0.5.0:\n\n* it is now '
Packit 6f3914
            'recommended that either :meth:`dnf.Base.close` is used, or that '
Packit 6f3914
            ':class:`dnf.Base` instances are treated as a context manager.\n\n'
Packit 6f3914
            'API extensions in 0.5.0:\n\n* :meth:`dnf.Base.add_remote_rpms`\n* '
Packit 6f3914
            ':meth:`dnf.Base.close`\n* :meth:`dnf.Base.group_upgrade`\n* '
Packit 6f3914
            ':meth:`dnf.Base.resolve` optionally accepts `allow_erasing` '
Packit 6f3914
            'arguments now.\n* :meth:`dnf.Base.package_downgrade`\n* '
Packit 6f3914
            ':meth:`dnf.Base.package_install`\n* '
Packit 6f3914
            ':meth:`dnf.Base.package_upgrade`\n* '
Packit 6f3914
            ':class:`dnf.cli.demand.DemandSheet`\n* '
Packit 6f3914
            ':attr:`dnf.cli.Command.base`\n* :attr:`dnf.cli.Command.cli`\n* '
Packit 6f3914
            ':attr:`dnf.cli.Command.summary`\n* :attr:`dnf.cli.Command.usage`'
Packit 6f3914
            '\n* :meth:`dnf.cli.Command.configure`\n* '
Packit 6f3914
            ':attr:`dnf.cli.Cli.demands`\n* :class:`dnf.comps.Package`\n* '
Packit 6f3914
            ':meth:`dnf.comps.Group.packages_iter`\n* '
Packit 6f3914
            ':data:`dnf.comps.MANDATORY` etc.\n\nBugs fixed in 0.5.0:\n\n',
Packit 6f3914
            # 0.5.1
Packit 6f3914
            '\nBugfix release with several internal cleanups. One outstanding '
Packit 6f3914
            'change for CLI users is that DNF is a lot less verbose now during'
Packit 6f3914
            ' the dependency resolving phase.\n\nBugs fixed in 0.5.1:\n\n',
Packit 6f3914
            # 0.5.2
Packit 6f3914
            '\nThis release brings `autoremove command '
Packit 6f3914
            '<https://bugzilla.redhat.com/show_bug.cgi?id=963345>`_ that '
Packit 6f3914
            'removes any package that was originally installed as a dependency'
Packit 6f3914
            ' (e.g. had not been specified as an explicit argument to the '
Packit 6f3914
            'install command) and is no longer needed.\n\nEnforced '
Packit 6f3914
            'verification of SSL connections can now be disabled with the '
Packit 6f3914
            ':ref:`sslverify setting <sslverify-label>`.\n\nWe have been '
Packit 6f3914
            'plagued with many crashes related to Unicode and encodings since '
Packit 6f3914
            "the 0.5.0 release. These have been cleared out now.\n\nThere's "
Packit 6f3914
            'more: improvement in startup time, `extended globbing semantics '
Packit 6f3914
            'for input arguments '
Packit 6f3914
            '<https://bugzilla.redhat.com/show_bug.cgi?id=1083679>`_ and '
Packit 6f3914
            '`better search relevance sorting '
Packit 6f3914
            '<https://bugzilla.redhat.com/show_bug.cgi?id=1093888>`_.\n\nBugs '
Packit 6f3914
            'fixed in 0.5.2:\n\n',
Packit 6f3914
            # 0.5.3
Packit 6f3914
            '\nA set of bugfixes related to i18n and Unicode handling. There '
Packit 6f3914
            'is a ``-4/-6`` switch and a corresponding :ref:`ip_resolve '
Packit 6f3914
            '<ip-resolve-label>` configuration option (both known from Yum) to'
Packit 6f3914
            ' force DNS resolving of hosts to IPv4 or IPv6 addresses.\n\n0.5.3'
Packit 6f3914
            ' comes with several extensions and clarifications in the API: '
Packit 6f3914
            'notably :class:`~.dnf.transaction.Transaction` is introspectible '
Packit 6f3914
            'now, :class:`Query.filter <dnf.query.Query.filter>` is more '
Packit 6f3914
            "useful with new types of arguments and we've hopefully shed more"
Packit 6f3914
            ' light on how a client is expected to setup the configuration '
Packit 6f3914
            ':attr:`~dnf.conf.Conf.substitutions`.\n\nFinally, plugin authors '
Packit 6f3914
            'can now use a new :meth:`~dnf.Plugin.resolved` hook.\n\nAPI '
Packit 6f3914
            'changes in 0.5.3:\n\n* extended description given for '
Packit 6f3914
            ':meth:`dnf.Base.fill_sack`\n* :meth:`dnf.Base.select_group` has '
Packit 6f3914
            'been dropped as announced in `0.4.18 Release Notes`_\n\nAPI '
Packit 6f3914
            'additions in 0.5.3:\n\n* :attr:`dnf.conf.Conf.substitutions`\n* '
Packit 6f3914
            ':attr:`dnf.package.Package.arch`\n* '
Packit 6f3914
            ':attr:`dnf.package.Package.buildtime`\n* '
Packit 6f3914
            ':attr:`dnf.package.Package.epoch`\n* '
Packit 6f3914
            ':attr:`dnf.package.Package.installtime`\n* '
Packit 6f3914
            ':attr:`dnf.package.Package.name`\n* '
Packit 6f3914
            ':attr:`dnf.package.Package.release`\n* '
Packit 6f3914
            ':attr:`dnf.package.Package.sourcerpm`\n* '
Packit 6f3914
            ':attr:`dnf.package.Package.version`\n* '
Packit 6f3914
            ':meth:`dnf.Plugin.resolved`\n* :meth:`dnf.query.Query.filter` '
Packit 6f3914
            'accepts suffixes for its argument keys now which change the '
Packit 6f3914
            'filter semantics.\n* :mod:`dnf.rpm`\n* '
Packit 6f3914
            ':class:`dnf.transaction.TransactionItem`\n* '
Packit 6f3914
            ':class:`dnf.transaction.Transaction` is iterable now.\n\nBugs '
Packit 6f3914
            'fixed in 0.5.3:\n\n',
Packit 6f3914
            # 0.5.4
Packit 6f3914
            '\nSeveral encodings bugs were fixed in this release, along with '
Packit 6f3914
            'some packaging issues and updates to :doc:`conf_ref`.\n\n'
Packit 6f3914
            'Repository :ref:`priority <repo_priority-label>` configuration '
Packit 6f3914
            'setting has been added, providing similar functionality to Yum '
Packit 6f3914
            "Utils' Priorities plugin.\n\nBugs fixed in 0.5.4:\n\n",
Packit 6f3914
            # 0.5.5
Packit 6f3914
            '\nThe full proxy configuration, API extensions and several '
Packit 6f3914
            'bugfixes are provided in this release.\n\nAPI changes in 0.5.5:\n'
Packit 6f3914
            '\n* `cachedir`, the second parameter of '
Packit 6f3914
            ':meth:`dnf.repo.Repo.__init__` is not optional (the method has '
Packit 6f3914
            'always been this way but the documentation was not matching)\n\n'
Packit 6f3914
            'API additions in 0.5.5:\n\n* extended description and an example '
Packit 6f3914
            'provided for :meth:`dnf.Base.fill_sack`\n* '
Packit 6f3914
            ':attr:`dnf.conf.Conf.proxy`\n* '
Packit 6f3914
            ':attr:`dnf.conf.Conf.proxy_username`\n* '
Packit 6f3914
            ':attr:`dnf.conf.Conf.proxy_password`\n* '
Packit 6f3914
            ':attr:`dnf.repo.Repo.proxy`\n* '
Packit 6f3914
            ':attr:`dnf.repo.Repo.proxy_username`\n* '
Packit 6f3914
            ':attr:`dnf.repo.Repo.proxy_password`\n\nBugs fixed in 0.5.5:\n\n',
Packit 6f3914
            # 0.6.0
Packit 6f3914
            '\n0.6.0 marks a new minor version of DNF and the first release to'
Packit 6f3914
            ' support advisories listing with the :ref:`udpateinfo command '
Packit 6f3914
            '<updateinfo_command-label>`.\n\nSupport for the :ref:`include '
Packit 6f3914
            'configuration directive <include-label>` has been added. Its '
Packit 6f3914
            "functionality reflects Yum's ``includepkgs`` but it has been "
Packit 6f3914
            'renamed to make it consistent with the ``exclude`` setting.\n\n'
Packit 6f3914
            'Group operations now produce a list of proposed marking changes '
Packit 6f3914
            'to group objects and the user is given a chance to accept or '
Packit 6f3914
            'reject them just like with an ordinary package transaction.\n\n'
Packit 6f3914
            'Bugs fixed in 0.6.0:\n\n',
Packit 6f3914
            # 0.6.1
Packit 6f3914
            '\nNew release adds :ref:`upgrade-type command '
Packit 6f3914
            '<upgrade_type_automatic-label>` to `dnf-automatic` for choosing '
Packit 6f3914
            'specific advisory type updates.\n\nImplemented missing '
Packit 6f3914
            ':ref:`history redo command <history_redo_command-label>` for '
Packit 6f3914
            'repeating transactions.\n\nSupports :ref:`gpgkey '
Packit 6f3914
            '<repo_gpgkey-label>` repo config, :ref:`repo_gpgcheck '
Packit 6f3914
            '<repo_gpgcheck-label>` and :ref:`gpgcheck <gpgcheck-label>` '
Packit 6f3914
            '[main] and Repo configs.\n\nDistributing new package '
Packit 6f3914
            ':ref:`dnf-yum <dnf_yum_package-label>` that provides '
Packit 6f3914
            '`/usr/bin/yum` as a symlink to `/usr/bin/dnf`.\n\nAPI additions '
Packit 6f3914
            'in 0.6.1:\n\n* `exclude`, the third parameter of '
Packit 6f3914
            ':meth:`dnf.Base.group_install` now also accepts glob patterns of '
Packit 6f3914
            'package names.\n\nBugs fixed in 0.6.1:\n\n',
Packit 6f3914
            # 0.6.2
Packit 6f3914
            '\nAPI additions in 0.6.2:\n\n* Now '
Packit 6f3914
            ':meth:`dnf.Base.package_install` method ignores already installed'
Packit 6f3914
            ' packages\n* `CliError` exception from :mod:`dnf.cli` documented'
Packit 6f3914
            '\n* `Autoerase`, `History`, `Info`, `List`, `Provides`, '
Packit 6f3914
            '`Repolist` commands do not force a sync of expired :ref:`metadata'
Packit 6f3914
            ' <metadata_synchronization-label>`\n* `Install` command does '
Packit 6f3914
            'installation only\n\nBugs fixed in 0.6.2:\n\n',
Packit 6f3914
            # 0.6.3
Packit 6f3914
            '\n:ref:`Deltarpm <deltarpm-label>` configuration option is set on'
Packit 6f3914
            ' by default.\n\nAPI additions in 0.6.3:\n\n* dnf-automatic adds '
Packit 6f3914
            ':ref:`motd emitter <emit_via_automatic-label>` as an alternative '
Packit 6f3914
            'output\n\nBugs fixed in 0.6.3:\n\n',
Packit 6f3914
            # 0.6.4
Packit 6f3914
            '\nAdded example code snippets into :doc:`use_cases`.\n\nShows '
Packit 6f3914
            'ordered groups/environments by `display_order` tag from :ref:`cli'
Packit 6f3914
            ' <grouplist_command-label>` and :doc:`api_comps` DNF API.\n\nIn '
Packit 6f3914
            'commands the environment group is specified the same as '
Packit 6f3914
            ':ref:`group <specifying_groups-label>`.\n\n'
Packit 6f3914
            ':ref:`skip_if_unavailable <skip_if_unavailable-label>` '
Packit 6f3914
            'configuration option affects the metadata only.\n\nadded '
Packit 6f3914
            '`enablegroups`, `minrate` and `timeout` :doc:`configuration '
Packit 6f3914
            'options <conf_ref>`\n\nAPI additions in 0.6.4:\n\nDocumented '
Packit 6f3914
            '`install_set` and `remove_set attributes` from '
Packit 6f3914
            ':doc:`api_transaction`.\n\nExposed `downloadsize`, `files`, '
Packit 6f3914
            '`installsize` attributes from :doc:`api_package`.\n\nBugs fixed '
Packit 6f3914
            'in 0.6.4:\n\n',
Packit 6f3914
            # 0.6.5
Packit 6f3914
            '\nPython 3 version of DNF is now default in Fedora 23 and later.'
Packit 6f3914
            '\n\nyum-dnf package does not conflict with yum package.\n\n'
Packit 6f3914
            '`dnf erase` was deprecated in favor of `dnf remove`.\n\nExtended '
Packit 6f3914
            'documentation of handling non-existent packages and YUM to DNF '
Packit 6f3914
            'transition in :doc:`cli_vs_yum`.\n\nAPI additions in 0.6.5:\n\n'
Packit 6f3914
            'Newly added `pluginconfpath` option in :doc:`configuration '
Packit 6f3914
            '<conf_ref>`.\n\nExposed `skip_if_unavailable` attribute from '
Packit 6f3914
            ':doc:`api_repos`.\n\nDocumented `IOError` exception of method '
Packit 6f3914
            '`fill_sack` from :class:`dnf.Base`.\n\nBugs fixed in 0.6.5:\n\n',
Packit 6f3914
            # 1.0.0
Packit 6f3914
            '\nImproved documentation of YUM to DNF transition in '
Packit 6f3914
            ':doc:`cli_vs_yum`.\n\n:ref:`Auto remove command '
Packit 6f3914
            '<autoremove_command-label>` does not remove `installonly` '
Packit 6f3914
            'packages.\n\n:ref:`Downgrade command <downgrade_command-label>` '
Packit 6f3914
            'downgrades to specified package version if that is lower than '
Packit 6f3914
            'currently installed one.\n\nDNF now uses :attr:`dnf.repo.Repo.id`'
Packit 6f3914
            ' as a default value for :attr:`dnf.repo.Repo.name`.\n\nAdded '
Packit 6f3914
            'support of repositories which use basic HTTP authentication.\n\n'
Packit 6f3914
            'API additions in 1.0.0:\n\n:doc:`configuration <conf_ref>` '
Packit 6f3914
            'options `username` and `password` (HTTP authentication)\n\n'
Packit 6f3914
            ':attr:`dnf.repo.Repo.username` and :attr:`dnf.repo.Repo.password`'
Packit 6f3914
            ' (HTTP authentication)\n\nBugs fixed in 1.0.0:\n\n',
Packit 6f3914
            # 1.0.1
Packit 6f3914
            '\nDNF follows the Semantic Versioning as defined at '
Packit 6f3914
            '`<http://semver.org/>`_.\n\nDocumented SSL '
Packit 6f3914
            ':doc:`configuration <conf_ref>` and :doc:`repository <api_repos>`'
Packit 6f3914
            ' options.\n\nAdded virtual provides allowing installation of DNF'
Packit 6f3914
            ' commands by their name in the form of\n'
Packit 6f3914
            '``dnf install dnf-command(name)``.\n\n'
Packit 6f3914
            ':doc:`dnf-automatic <automatic>` now by default waits random '
Packit 6f3914
            'interval between 0 and 300 seconds before any network '
Packit 6f3914
            'communication is performed.\n\n\nBugs fixed in 1.0.1:\n\n'
Packit 6f3914
        ]
Packit 6f3914
        rest = [
Packit 6f3914
            (None, [], ''),
Packit 6f3914
            ('0.3.1',
Packit 6f3914
             ['916657', '921294', '922521', '926871', '878826', '922664',
Packit 6f3914
              '892064', '919769'],
Packit 6f3914
             '\n'),
Packit 6f3914
            ('0.3.2', ['947258', '889202', '923384'], '\n'),
Packit 6f3914
            ('0.3.3', ['950722', '903775'], '\n'),
Packit 6f3914
            ('0.3.4', ['887317', '914919', '922667'], '\n'),
Packit 6f3914
            ('0.3.5', ['958452', '959990', '961549', '962188'], '\n'),
Packit 6f3914
            ('0.3.6',
Packit 6f3914
             ['966372', '965410', '963627', '965114', '964467', '963680',
Packit 6f3914
              '963133'],
Packit 6f3914
             '\n'),
Packit 6f3914
            ('0.3.7', ['916662', '967732'], '\n'),
Packit 6f3914
            ('0.3.8',
Packit 6f3914
             ['908491', '968159', '974427', '974866', '976652', '975858'],
Packit 6f3914
             '\n'),
Packit 6f3914
            ('0.3.9', ['964584', '979942', '980227', '981310'], '\n'),
Packit 6f3914
            ('0.3.10', ['977661', '984483', '986545'], '\n'),
Packit 6f3914
            ('0.3.11', ['979042', '977753', '996138', '993916'], '\n'),
Packit 6f3914
            ('0.4.0', ['997403', '1002508', '1002798'], '\n'),
Packit 6f3914
            ('0.4.1', ['998859', '1006366', '1008444', '1003220'], '\n'),
Packit 6f3914
            ('0.4.2', ['909744', '984529', '967798', '995459'], '\n'),
Packit 6f3914
            ('0.4.3', ['1013764', '1013773'], '\n'),
Packit 6f3914
            ('0.4.4', ['1017278'], '\n'),
Packit 6f3914
            ('0.4.5', ['1021087'], '\n'),
Packit 6f3914
            ('0.4.6',
Packit 6f3914
             ['878348', '880524', '1019957', '1020101', '1020934', '1023486'],
Packit 6f3914
             '\n'),
Packit 6f3914
            ('0.4.7', ['1019170', '1024776', '1025650'], '\n'),
Packit 6f3914
            ('0.4.8',
Packit 6f3914
             ['1014563', '1029948', '1030998', '1030297', '1030980'],
Packit 6f3914
             '\n'),
Packit 6f3914
            ('0.4.9',
Packit 6f3914
             ['884615', '963137', '991038', '1032455', '1034607', '1036116'],
Packit 6f3914
             '\n'),
Packit 6f3914
            ('0.4.10',
Packit 6f3914
             ['967264', '1018284', '1035164', '1036147', '1036211', '1038403',
Packit 6f3914
              '1038937', '1040255', '1044502', '1044981', '1044999'],
Packit 6f3914
             '\n'),
Packit 6f3914
            ('0.4.11',
Packit 6f3914
             ['1048402', '1048572', '1048716', '1048719', '1048988'],
Packit 6f3914
             '\n'),
Packit 6f3914
            ('0.4.12',
Packit 6f3914
             ['1045737', '1048468', '1048488', '1049025', '1051554'],
Packit 6f3914
             '\n'),
Packit 6f3914
            ('0.4.13',
Packit 6f3914
             ['909468', '1030440', '1046244', '1055051', '1056400'],
Packit 6f3914
             '\n'),
Packit 6f3914
            ('0.4.14', ['1062390', '1062847', '1063022', '1064148'],
Packit 6f3914
             '\n'),
Packit 6f3914
            ('0.4.15',
Packit 6f3914
             ['1048788', '1065728', '1065879', '1065959', '1066743'],
Packit 6f3914
             '\n'),
Packit 6f3914
            ('0.4.16', ['1069996'], '\n'),
Packit 6f3914
            ('0.4.17',
Packit 6f3914
             ['1059704', '1058224', '1069538', '1070598', '1070710', '1071323',
Packit 6f3914
              '1071455', '1071501', '1071518', '1071677'],
Packit 6f3914
             '\n'),
Packit 6f3914
            ('0.4.18', ['963710', '1067136', '1071212', '1071501'], '\n'),
Packit 6f3914
            ('0.4.19', ['1077173', '1078832', '1079621'], '\n'),
Packit 6f3914
            ('0.5.0',
Packit 6f3914
             ['1029022', '1051869', '1061780', '1062884', '1062889', '1063666',
Packit 6f3914
              '1064211', '1064226', '1073859', '1076884', '1079519', '1079932',
Packit 6f3914
              '1080331', '1080489', '1082230', '1083432', '1083767', '1084139',
Packit 6f3914
              '1084553', '1088166'],
Packit 6f3914
             '\n'),
Packit 6f3914
            ('0.5.1', ['1065882', '1081753', '1089864'], '\n'),
Packit 6f3914
            ('0.5.2',
Packit 6f3914
             ['963345', '1073457', '1076045', '1083679', '1092006', '1092777',
Packit 6f3914
              '1093888', '1094594', '1095580', '1095861', '1096506'],
Packit 6f3914
             '\n'),
Packit 6f3914
            ('0.5.3',
Packit 6f3914
             ['1047049', '1067156', '1093420', '1104757', '1105009', '1110800',
Packit 6f3914
              '1111569', '1111997', '1112669', '1112704'],
Packit 6f3914
             '\n'),
Packit 6f3914
            ('0.5.4',
Packit 6f3914
             ['1048973', '1108908', '1116544', '1116839', '1116845', '1117102',
Packit 6f3914
              '1117293', '1117678', '1118178', '1118796', '1119032'],
Packit 6f3914
             '\n'),
Packit 6f3914
            ('0.5.5',
Packit 6f3914
             ['1100946', '1117789', '1120583', '1121280', '1122900',
Packit 6f3914
              '1123688'],
Packit 6f3914
             '\n'),
Packit 6f3914
            ('0.6.0',
Packit 6f3914
             ['850912', '1055910', '1116666', '1118272', '1127206'],
Packit 6f3914
             '\n'),
Packit 6f3914
            ('0.6.1',
Packit 6f3914
             ['1132335', '1071854', '1131969', '908764', '1130878', '1130432',
Packit 6f3914
              '1118236', '1109915'],
Packit 6f3914
             '\n'),
Packit 6f3914
            ('0.6.2',
Packit 6f3914
             ['909856', '1134893', '1138700', '1070902', '1124316', '1136584',
Packit 6f3914
              '1135861', '1136223', '1122617', '1133830', '1121184'],
Packit 6f3914
             '\n'),
Packit 6f3914
            ('0.6.3',
Packit 6f3914
             ['1153543', '1151231', '1163063', '1151854', '1151740', '1110780',
Packit 6f3914
              '1149972', '1150474', '995537', '1149952', '1149350', '1170232',
Packit 6f3914
              '1147523', '1148208', '1109927'],
Packit 6f3914
             '\n'),
Packit 6f3914
            ('0.6.4',
Packit 6f3914
             ['1155877', '1175466', '1175466', '1186461', '1170156', '1184943',
Packit 6f3914
              '1177002', '1169165', '1167982', '1157233', '1138096', '1181189',
Packit 6f3914
              '1181397', '1175434', '1162887', '1156084', '1175098', '1174136',
Packit 6f3914
              '1055910', '1155918', '1119030', '1177394', '1154476'],
Packit 6f3914
             '\n'),
Packit 6f3914
            ('0.6.5',
Packit 6f3914
             ['1203151', '1187579', '1185977', '1195240', '1193914', '1195385',
Packit 6f3914
              '1160806', '1186710', '1207726', '1157233', '1190671', '1191579',
Packit 6f3914
              '1195325', '1154202', '1189083', '1193915', '1195661', '1190458',
Packit 6f3914
              '1194685', '1160950'],
Packit 6f3914
             '\n'),
Packit 6f3914
            ('1.0.0',
Packit 6f3914
             ['1215560', '1199648', '1208773', '1208018', '1207861', '1201445',
Packit 6f3914
              '1210275', '1191275', '1207965', '1215289'],
Packit 6f3914
             '\n'),
Packit 6f3914
            ('1.0.1',
Packit 6f3914
             ['1214968', '1222694', '1225246', '1213985', '1225277', '1223932',
Packit 6f3914
              '1223614', '1203661', '1187741'],
Packit 6f3914
             '')]
Packit 6f3914
        expected = (
Packit 6f3914
            (version, desc, issues, epilog)
Packit 6f3914
            for desc, (version, issues, epilog) in zip(descriptions, rest))
Packit 6f3914
        with open(notesfn) as notesfile:
Packit 6f3914
            self.assertItemsEqual(parser.parse_lines(notesfile), expected)
Packit 6f3914
Packit 6f3914
Packit 6f3914
if __name__ == '__main__':
Packit 6f3914
    main()