Hello community,

here is the log from the commit of package dnf-plugins-extras for 
openSUSE:Factory checked in at 2020-02-27 16:57:47
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/dnf-plugins-extras (Old)
 and      /work/SRC/openSUSE:Factory/.dnf-plugins-extras.new.26092 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "dnf-plugins-extras"

Thu Feb 27 16:57:47 2020 rev:3 rq:779947 version:4.0.9

Changes:
--------
--- /work/SRC/openSUSE:Factory/dnf-plugins-extras/dnf-plugins-extras.changes    
2019-12-03 15:20:15.894564958 +0100
+++ 
/work/SRC/openSUSE:Factory/.dnf-plugins-extras.new.26092/dnf-plugins-extras.changes
 2020-02-27 16:57:49.122855765 +0100
@@ -1,0 +2,12 @@
+Thu Feb 27 14:21:01 UTC 2020 - Neal Gompa <ngomp...@gmail.com>
+
+- Update to version 4.0.9
+  + [doc] move manpages for plugins to "dnf-PLUGIN" (rh#1706386)
+  + Add offline-upgrade and offline-distrosync commands
+  + [doc] Add description for new offline command
+  + Store reason for system-upgrade plugin
+  + Do not show Operation aborted as an error (rh#1797427)
+- Drop backported patch included in this release
+  * Patch: 0001-doc-move-manpages-for-plugins-to-dnf-PLUGIN-RhBug-17.patch
+
+-------------------------------------------------------------------

Old:
----
  0001-doc-move-manpages-for-plugins-to-dnf-PLUGIN-RhBug-17.patch
  dnf-plugins-extras-4.0.8.tar.gz

New:
----
  dnf-plugins-extras-4.0.9.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ dnf-plugins-extras.spec ++++++
--- /var/tmp/diff_new_pack.Ex7LDu/_old  2020-02-27 16:57:49.670856681 +0100
+++ /var/tmp/diff_new_pack.Ex7LDu/_new  2020-02-27 16:57:49.670856681 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package dnf-plugins-extras
 #
-# Copyright (c) 2019 Neal Gompa <ngomp...@gmail.com>.
+# Copyright (c) 2020 Neal Gompa <ngomp...@gmail.com>.
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -16,7 +16,7 @@
 #
 
 
-%{!?dnf_lowest_compatible: %global dnf_lowest_compatible 4.2.1}
+%{!?dnf_lowest_compatible: %global dnf_lowest_compatible 4.2.19}
 %global dnf_plugins_extra_obsolete 2.0.0
 
 # YUM v3 has been removed from openSUSE Tumbleweed as of 20191119
@@ -37,7 +37,7 @@
 %bcond_with tests
 
 Name:           dnf-plugins-extras
-Version:        4.0.8
+Version:        4.0.9
 Release:        0
 Summary:        Extras Plugins for DNF
 Group:          System/Packages
@@ -45,9 +45,6 @@
 URL:            https://github.com/rpm-software-management/%{name}
 Source0:        %{url}/archive/%{version}/%{name}-%{version}.tar.gz
 
-# Backports from upstream
-Patch0001:      0001-doc-move-manpages-for-plugins-to-dnf-PLUGIN-RhBug-17.patch
-
 BuildArch:      noarch
 BuildRequires:  cmake
 BuildRequires:  gettext
@@ -142,6 +139,8 @@
 Requires:       python3-%{name}-common = %{version}-%{release}
 Requires:       python3-systemd
 Requires:       systemd
+Provides:       dnf-command(offline-distrosync)
+Provides:       dnf-command(offline-upgrade)
 Provides:       dnf-command(system-upgrade)
 Provides:       %{name}-system-upgrade = %{version}-%{release}
 Provides:       system-upgrade = %{version}-%{release}

++++++ dnf-plugins-extras-4.0.8.tar.gz -> dnf-plugins-extras-4.0.9.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnf-plugins-extras-4.0.8/dnf-plugins-extras.spec 
new/dnf-plugins-extras-4.0.9/dnf-plugins-extras.spec
--- old/dnf-plugins-extras-4.0.8/dnf-plugins-extras.spec        2019-11-07 
11:41:51.000000000 +0100
+++ new/dnf-plugins-extras-4.0.9/dnf-plugins-extras.spec        2020-02-24 
15:52:08.000000000 +0100
@@ -1,4 +1,4 @@
-%{!?dnf_lowest_compatible: %global dnf_lowest_compatible 4.2.1}
+%{!?dnf_lowest_compatible: %global dnf_lowest_compatible 4.2.19}
 %global dnf_plugins_extra_obsolete 2.0.0
 
 %if 0%{?rhel} > 7 || 0%{?fedora} > 29
@@ -14,7 +14,7 @@
 %endif
 
 Name:           dnf-plugins-extras
-Version:        4.0.8
+Version:        4.0.9
 Release:        1%{?dist}
 Summary:        Extras Plugins for DNF
 License:        GPLv2+
@@ -364,14 +364,14 @@
 %if %{with python2}
 %files -n python2-dnf-plugin-kickstart
 %{python2_sitelib}/dnf-plugins/kickstart.*
-%{_mandir}/man8/dnf.plugin.kickstart.*
+%{_mandir}/man8/dnf-kickstart.*
 %endif
 
 %if %{with python3}
 %files -n python3-dnf-plugin-kickstart
 %{python3_sitelib}/dnf-plugins/kickstart.*
 %{python3_sitelib}/dnf-plugins/__pycache__/kickstart.*
-%{_mandir}/man8/dnf.plugin.kickstart.*
+%{_mandir}/man8/dnf-kickstart.*
 %endif
 
 %if %{with python3}
@@ -379,20 +379,20 @@
 %config(noreplace) %{_sysconfdir}/dnf/plugins/rpmconf.conf
 %{python3_sitelib}/dnf-plugins/rpm_conf.*
 %{python3_sitelib}/dnf-plugins/__pycache__/rpm_conf.*
-%{_mandir}/man8/dnf.plugin.rpmconf.*
+%{_mandir}/man8/dnf-rpmconf.*
 %endif
 
 %if %{with python2}
 %files -n python2-dnf-plugin-snapper
 %{python2_sitelib}/dnf-plugins/snapper.*
-%{_mandir}/man8/dnf.plugin.snapper.*
+%{_mandir}/man8/dnf-snapper.*
 %endif
 
 %if %{with python3}
 %files -n python3-dnf-plugin-snapper
 %{python3_sitelib}/dnf-plugins/snapper.*
 %{python3_sitelib}/dnf-plugins/__pycache__/snapper.*
-%{_mandir}/man8/dnf.plugin.snapper.*
+%{_mandir}/man8/dnf-snapper.*
 %endif
 
 %if %{with python2}
@@ -401,7 +401,7 @@
 %{_unitdir}/dnf-system-upgrade-cleanup.service
 %{_unitdir}/system-update.target.wants/dnf-system-upgrade.service
 %{python2_sitelib}/dnf-plugins/system_upgrade.*
-%{_mandir}/man8/dnf.plugin.system-upgrade.*
+%{_mandir}/man8/dnf-system-upgrade.*
 %endif
 
 %if %{with python3}
@@ -411,20 +411,20 @@
 %{_unitdir}/system-update.target.wants/dnf-system-upgrade.service
 %{python3_sitelib}/dnf-plugins/system_upgrade.py
 %{python3_sitelib}/dnf-plugins/__pycache__/system_upgrade.*
-%{_mandir}/man8/dnf.plugin.system-upgrade.*
+%{_mandir}/man8/dnf-system-upgrade.*
 %endif
 
 %if %{with python2}
 %files -n python2-dnf-plugin-tracer
 %{python2_sitelib}/dnf-plugins/tracer.*
-%{_mandir}/man8/dnf.plugin.tracer.*
+%{_mandir}/man8/dnf-tracer.*
 %endif
 
 %if %{with python3}
 %files -n python3-dnf-plugin-tracer
 %{python3_sitelib}/dnf-plugins/tracer.*
 %{python3_sitelib}/dnf-plugins/__pycache__/tracer.*
-%{_mandir}/man8/dnf.plugin.tracer.*
+%{_mandir}/man8/dnf-tracer.*
 %endif
 
 %if %{with python3}
@@ -432,14 +432,14 @@
 %config(noreplace) %{_sysconfdir}/dnf/plugins/torproxy.conf
 %{python3_sitelib}/dnf-plugins/torproxy.*
 %{python3_sitelib}/dnf-plugins/__pycache__/torproxy.*
-%{_mandir}/man8/dnf.plugin.torproxy.*
+%{_mandir}/man8/dnf-torproxy.*
 %endif
 
 %if %{with python3}
 %files -n python3-dnf-plugin-showvars
 %{python3_sitelib}/dnf-plugins/showvars.*
 %{python3_sitelib}/dnf-plugins/__pycache__/showvars.*
-%{_mandir}/man8/dnf.plugin.showvars.*
+%{_mandir}/man8/dnf-showvars.*
 %endif
 
 %changelog
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnf-plugins-extras-4.0.8/doc/CMakeLists.txt 
new/dnf-plugins-extras-4.0.9/doc/CMakeLists.txt
--- old/dnf-plugins-extras-4.0.8/doc/CMakeLists.txt     2019-11-07 
11:41:51.000000000 +0100
+++ new/dnf-plugins-extras-4.0.9/doc/CMakeLists.txt     2020-02-24 
15:52:08.000000000 +0100
@@ -18,14 +18,14 @@
 ADD_CUSTOM_TARGET (doc)
 ADD_DEPENDENCIES (doc doc-html doc-man)
 
-INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/dnf.plugin.kickstart.8
-    ${CMAKE_CURRENT_BINARY_DIR}/dnf.plugin.snapper.8
-    ${CMAKE_CURRENT_BINARY_DIR}/dnf.plugin.system-upgrade.8
-    ${CMAKE_CURRENT_BINARY_DIR}/dnf.plugin.tracer.8
-    ${CMAKE_CURRENT_BINARY_DIR}/dnf.plugin.showvars.8
+INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/dnf-kickstart.8
+    ${CMAKE_CURRENT_BINARY_DIR}/dnf-snapper.8
+    ${CMAKE_CURRENT_BINARY_DIR}/dnf-system-upgrade.8
+    ${CMAKE_CURRENT_BINARY_DIR}/dnf-tracer.8
+    ${CMAKE_CURRENT_BINARY_DIR}/dnf-showvars.8
        DESTINATION share/man/man8)
 if (${PYTHON_VERSION_MAJOR} STREQUAL "3")
-    INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/dnf.plugin.rpmconf.8
-        ${CMAKE_CURRENT_BINARY_DIR}/dnf.plugin.torproxy.8
+    INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/dnf-rpmconf.8
+        ${CMAKE_CURRENT_BINARY_DIR}/dnf-torproxy.8
         DESTINATION share/man/man8)
 endif()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnf-plugins-extras-4.0.8/doc/conf.py 
new/dnf-plugins-extras-4.0.9/doc/conf.py
--- old/dnf-plugins-extras-4.0.8/doc/conf.py    2019-11-07 11:41:51.000000000 
+0100
+++ new/dnf-plugins-extras-4.0.9/doc/conf.py    2020-02-24 15:52:08.000000000 
+0100
@@ -240,17 +240,13 @@
 # One entry per manual page. List of tuples
 # (source start file, name, description, authors, manual section).
 man_pages = [
-    ('kickstart', 'dnf.plugin.kickstart',
-     u'DNF kickstart Plugin', AUTHORS, 8),
-    ('rpmconf', 'dnf.plugin.rpmconf',
-     u'DNF rpmconf Plugin', AUTHORS, 8),
-    ('snapper', 'dnf.plugin.snapper',
-     u'DNF snapper Plugin', AUTHORS, 8),
-    ('system-upgrade', 'dnf.plugin.system-upgrade', u'DNF system-upgrade 
Plugin', AUTHORS, 8),
-    ('torproxy', 'dnf.plugin.torproxy', u'DNF torproxy Plugin', AUTHORS, 8),
-    ('showvars', 'dnf.plugin.showvars', u'DNF showvars Plugin', AUTHORS, 8),
-    ('tracer', 'dnf.plugin.tracer',
-     u'DNF tracer Plugin', AUTHORS, 8),
+    ('kickstart', 'dnf-kickstart', u'DNF kickstart Plugin', AUTHORS, 8),
+    ('rpmconf', 'dnf-rpmconf', u'DNF rpmconf Plugin', AUTHORS, 8),
+    ('snapper', 'dnf-snapper', u'DNF snapper Plugin', AUTHORS, 8),
+    ('system-upgrade', 'dnf-system-upgrade', u'DNF system-upgrade Plugin', 
AUTHORS, 8),
+    ('torproxy', 'dnf-torproxy', u'DNF torproxy Plugin', AUTHORS, 8),
+    ('showvars', 'dnf-showvars', u'DNF showvars Plugin', AUTHORS, 8),
+    ('tracer', 'dnf-tracer', u'DNF tracer Plugin', AUTHORS, 8),
 ]
 
 # If true, show URL addresses after external links.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnf-plugins-extras-4.0.8/doc/release_notes.rst 
new/dnf-plugins-extras-4.0.9/doc/release_notes.rst
--- old/dnf-plugins-extras-4.0.8/doc/release_notes.rst  2019-11-07 
11:41:51.000000000 +0100
+++ new/dnf-plugins-extras-4.0.9/doc/release_notes.rst  2020-02-24 
15:52:08.000000000 +0100
@@ -5,6 +5,21 @@
 .. contents::
 
 ===================
+4.0.9 Release Notes
+===================
+
+- [doc] move manpages for plugins to "dnf-PLUGIN" (RhBug:1706386)
+- Add offline-upgrade and offline-distrosync commands
+- [doc] Add description for new offline command
+- Store reason for system-upgrade plugin
+- Do not show Operation aborted as an error (RhBug:1797427)
+
+Bugs fixed in 4.0.9:
+
+* :rhbug:`1706386`
+* :rhbug:`1797427`
+
+===================
 4.0.8 Release Notes
 ===================
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnf-plugins-extras-4.0.8/doc/summaries_cache 
new/dnf-plugins-extras-4.0.9/doc/summaries_cache
--- old/dnf-plugins-extras-4.0.8/doc/summaries_cache    2019-11-07 
11:41:51.000000000 +0100
+++ new/dnf-plugins-extras-4.0.9/doc/summaries_cache    2020-02-24 
15:52:08.000000000 +0100
@@ -190,5 +190,13 @@
     [
         1764169,
         "system upgrade fails when packages need to be reinstalled, they are 
not downloaded"
+    ],
+    [
+        1706386,
+        "[RFE] Discoverable man page names for DNF plugins"
+    ],
+    [
+        1797427,
+        "'dnf system-upgrade download' misleading message"
     ]
 ]
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnf-plugins-extras-4.0.8/doc/system-upgrade.rst 
new/dnf-plugins-extras-4.0.9/doc/system-upgrade.rst
--- old/dnf-plugins-extras-4.0.8/doc/system-upgrade.rst 2019-11-07 
11:41:51.000000000 +0100
+++ new/dnf-plugins-extras-4.0.9/doc/system-upgrade.rst 2020-02-24 
15:52:08.000000000 +0100
@@ -23,6 +23,10 @@
 Description
 -----------
 
+DNF system-upgrades plugin provides three commands: ``system-upgrade``, 
``offline-upgrade``, and
+``offline-distrosync``. Only ``system-upgrade`` command requires increase of 
distribution major
+version (``--releasever``) compared to installed version.
+
 ``dnf system-upgrade`` can be used to upgrade a Fedora system to a new major
 release. It replaces fedup (the old Fedora Upgrade tool). Before you proceed 
ensure that your system
 is fully upgraded (``dnf --refresh upgrade``).
@@ -35,7 +39,7 @@
 
 On modular system, also set the ``module_platform_id``. For example, for 
Fedora 30:
 
-    ``dnf system-upgrade download --releasever 30 
--setopt='module_platform_id=platform:f30' [OPTIONS]``
+``dnf system-upgrade download --releasever 30 [OPTIONS]``
 
 ``dnf system-upgrade reboot``
 
@@ -45,6 +49,26 @@
 
 ``dnf system-upgrade log --number=<number>``
 
+``dnf offline-upgrade download [OPTIONS]``
+
+``dnf offline-upgrade reboot``
+
+``dnf offline-upgrade clean``
+
+``dnf offline-upgrade log``
+
+``dnf offline-upgrade log --number=<number>``
+
+``dnf offline-distrosync download [OPTIONS]``
+
+``dnf offline-distrosync reboot``
+
+``dnf offline-distrosync clean``
+
+``dnf offline-distrosync log``
+
+``dnf offline-distrosync log --number=<number>``
+
 -----------
 Subcommands
 -----------
@@ -88,7 +112,8 @@
 ``--no-downgrade``
     Behave like ``dnf update``: do not install packages from the new release
     if they are older than what is currently installed. This is the opposite of
-    ``--distro-sync``. If both are specified, the last option will be used.
+    ``--distro-sync``. If both are specified, the last option will be used. 
The option cannot be
+    used with the ``offline-distrosync` command.
 
 ``--number``
     Applied with ``log`` subcommand will show the log specified by the number.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/dnf-plugins-extras-4.0.8/plugins/system_upgrade.py 
new/dnf-plugins-extras-4.0.9/plugins/system_upgrade.py
--- old/dnf-plugins-extras-4.0.8/plugins/system_upgrade.py      2019-11-07 
11:41:51.000000000 +0100
+++ new/dnf-plugins-extras-4.0.9/plugins/system_upgrade.py      2020-02-24 
15:52:08.000000000 +0100
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 #
-# Copyright (c) 2015 Red Hat, Inc.
+# Copyright (c) 2015-2020 Red Hat, Inc.
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -26,6 +26,7 @@
 import os
 import os.path
 import re
+import sys
 import uuid
 
 from systemd import journal
@@ -60,11 +61,13 @@
 RELEASEVER_MSG = _(
     "Need a --releasever greater than the current system version.")
 DOWNLOAD_FINISHED_MSG = _(  # Translators: do not change "reboot" here
-    "Download complete! Use 'dnf system-upgrade reboot' to start the 
upgrade.\n"
-    "To remove cached metadata and transaction use 'dnf system-upgrade clean'")
+    "Download complete! Use 'dnf {command} reboot' to start the upgrade.\n"
+    "To remove cached metadata and transaction use 'dnf {command} clean'")
 CANT_RESET_RELEASEVER = _(
     "Sorry, you need to use 'download --releasever' instead of '--network'")
 
+STATE_VERSION = 2
+
 # --- Miscellaneous helper functions ------------------------------------------
 
 
@@ -166,6 +169,9 @@
             return self._data.get(option)
         return property(getprop, setprop)
 
+    #  !!! Increase STATE_VERSION for any changes in data structure like a new 
property or a new
+    #  data structure !!!
+    state_version = _prop("state_version")
     download_status = _prop("download_status")
     destdir = _prop("destdir")
     target_releasever = _prop("target_releasever")
@@ -176,6 +182,7 @@
     # list of repos with repo_gpgcheck=True
     repo_gpgcheck_repos = _prop("repo_gpgcheck_repos")
     upgrade_status = _prop("upgrade_status")
+    upgrade_command = _prop("upgrade_command")
     distro_sync = _prop("distro_sync")
     allow_erasing = _prop("allow_erasing")
     enable_disable_repos = _prop("enable_disable_repos")
@@ -323,10 +330,12 @@
         super(SystemUpgradePlugin, self).__init__(base, cli)
         if cli:
             cli.register_command(SystemUpgradeCommand)
+            cli.register_command(OfflineUpgradeCommand)
+            cli.register_command(OfflineDistrosyncCommand)
 
 
 class SystemUpgradeCommand(dnf.cli.Command):
-    aliases = ('system-upgrade', 'fedup')
+    aliases = ('system-upgrade', 'fedup',)
     summary = _("Prepare system for upgrade to a new release")
 
     def __init__(self, cli):
@@ -353,11 +362,11 @@
                      DNF_VERSION=dnf.const.VERSION)
 
     def pre_configure(self):
+        self._call_sub("check")
         self._call_sub("pre_configure")
 
     def configure(self):
         self._call_sub("configure")
-        self._call_sub("check")
 
     def run(self):
         self._call_sub("run")
@@ -365,21 +374,56 @@
     def run_transaction(self):
         self._call_sub("transaction")
 
+    def run_resolved(self):
+        self._call_sub("resolved")
+
     def _call_sub(self, name):
         subfunc = getattr(self, name + '_' + self.opts.tid[0], None)
         if callable(subfunc):
             subfunc()
 
+    def _check_state_version(self, command):
+        if self.state.state_version != STATE_VERSION:
+            msg = _("Incompatible version of data. Rerun 'dnf {command} 
download [OPTIONS]'"
+                    "").format(command=command)
+            raise CliError(msg)
+
     def _set_cachedir(self):
         # set download directories from json state file
         self.base.conf.cachedir = DEFAULT_DATADIR
         self.base.conf.destdir = self.state.destdir if self.state.destdir else 
None
 
+    def _get_forward_reverse_pkg_reason_pairs(self):
+        """
+        forward = {repoid:{pkg_nevra: {tsi.action: tsi.reason}}
+        reverse = {pkg_nevra: {tsi.action: tsi.reason}}
+        :return: forward, reverse
+        """
+        backward_action = set(dnf.transaction.BACKWARD_ACTIONS +\
+                          
[libdnf.transaction.TransactionItemAction_REINSTALLED])
+        forward_actions = set(dnf.transaction.FORWARD_ACTIONS)
+
+        forward = {}
+        reverse = {}
+        for tsi in self.cli.base.transaction:
+            if tsi.action in forward_actions:
+                pkg = tsi.pkg
+                forward.setdefault(pkg.repo.id, {}).setdefault(
+                    str(pkg), {})[tsi.action] = tsi.reason
+            elif tsi.action in backward_action:
+                reverse.setdefault(str(tsi.pkg), {})[tsi.action] = tsi.reason
+        return forward, reverse
+
     # == pre_configure_*: set up action-specific demands 
==========================
     def pre_configure_download(self):
         # only download subcommand accepts --destdir command line option
         self.base.conf.cachedir = DEFAULT_DATADIR
         self.base.conf.destdir = self.opts.destdir if self.opts.destdir else 
None
+        if 'offline-distrosync' == self.opts.command and not 
self.opts.distro_sync:
+            raise CliError(
+                _("Command 'offline-distrosync' cannot be used with 
--no-downgrade option"))
+        elif 'offline-upgrade' == self.opts.command:
+            self.opts.distro_sync = False
 
     def pre_configure_reboot(self):
         self._set_cachedir()
@@ -396,16 +440,19 @@
     # == configure_*: set up action-specific demands ==========================
 
     def configure_download(self):
-        help_url = get_url_from_os_release()
-        if help_url:
-            msg = _('Additional information for System Upgrade: {}')
-            logger.info(msg.format(ucd(help_url)))
-        if self.base._promptWanted():
-            msg = _('Before you continue ensure that your system is fully 
upgraded by running '
-                    '"dnf --refresh upgrade". Do you want to continue')
-            if self.base.conf.assumeno or not self.base.output.userconfirm(
-                    msg='{} [y/N]: '.format(msg), defaultyes_msg='{} [Y/n]: 
'.format(msg)):
-                raise CliError(_("Operation aborted."))
+        if 'system-upgrade' == self.opts.command or 'fedup' == 
self.opts.command:
+            help_url = get_url_from_os_release()
+            if help_url:
+                msg = _('Additional information for System Upgrade: {}')
+                logger.info(msg.format(ucd(help_url)))
+            if self.base._promptWanted():
+                msg = _('Before you continue ensure that your system is fully 
upgraded by running '
+                        '"dnf --refresh upgrade". Do you want to continue')
+                if self.base.conf.assumeno or not self.base.output.userconfirm(
+                        msg='{} [y/N]: '.format(msg), defaultyes_msg='{} 
[Y/n]: '.format(msg)):
+                    logger.error(_("Operation aborted."))
+                    sys.exit(1)
+            check_release_ver(self.base.conf, target=self.opts.releasever)
         self.cli.demands.root_user = True
         self.cli.demands.resolving = True
         self.cli.demands.available_repos = True
@@ -464,15 +511,14 @@
 
     # == check_*: do any action-specific checks ===============================
 
-    def check_download(self):
-        check_release_ver(self.base.conf, target=self.opts.releasever)
-        dnf.util.ensure_dir(self.base.conf.cachedir)
-        if self.base.conf.destdir:
-            dnf.util.ensure_dir(self.base.conf.destdir)
-
     def check_reboot(self):
         if not self.state.download_status == 'complete':
             raise CliError(_("system is not ready for upgrade"))
+        self._check_state_version(self.opts.command)
+        if self.state.upgrade_command != self.opts.command:
+            msg = _("the transaction was not prepared for '{command}'. "
+                    "Rerun 'dnf {command} download 
[OPTIONS]'").format(command=self.opts.command)
+            raise CliError(msg)
         if os.path.lexists(MAGIC_SYMLINK):
             raise CliError(_("upgrade is already scheduled"))
         dnf.util.ensure_dir(DEFAULT_DATADIR)
@@ -487,9 +533,13 @@
             raise SystemExit(0)
         # Delete symlink ASAP to avoid reboot loops
         dnf.yum.misc.unlink_f(MAGIC_SYMLINK)
+        command = self.state.upgrade_command
+        if not command:
+            command = self.opts.command
+        self._check_state_version(command)
         if not self.state.upgrade_status == 'ready':
-            raise CliError(  # Translators: do not change "reboot" here
-                _("use 'dnf system-upgrade reboot' to begin the upgrade"))
+            msg = _("use 'dnf {command} reboot' to begin the 
upgrade").format(command=command)
+            raise CliError(msg)
 
     # == run_*: run the action/prep the transaction ===========================
 
@@ -525,16 +575,23 @@
 
     def run_upgrade(self):
         # change the upgrade status (so we can detect crashed upgrades later)
+        command = ''
         with self.state as state:
             state.upgrade_status = 'incomplete'
+            command = state.upgrade_command
+        if command == 'offline-upgrade':
+            msg = _("Starting offline upgrade. This will take a while.")
+        elif command == 'offline-distrosync':
+            msg = _("Starting offline distrosync. This will take a while.")
+        else:
+            msg = _("Starting system upgrade. This will take a while.")
 
-        self.log_status(_("Starting system upgrade. This will take a while."),
-                        UPGRADE_STARTED_ID)
+        self.log_status(msg, UPGRADE_STARTED_ID)
 
         # reset the splash mode and let the user know we're running
         Plymouth.set_mode()
         Plymouth.progress(0)
-        Plymouth.message(_("Starting system upgrade. This will take a while."))
+        Plymouth.message(msg)
 
         # disable screen blanking
         disable_blanking()
@@ -555,7 +612,7 @@
 
         errs = []
 
-        for pkgspec in self.state.remove_packages:
+        for pkgspec in self.state.remove_packages.keys():
             try:
                 self.base.remove(pkgspec)
             except dnf.exceptions.MarkingError:
@@ -563,8 +620,8 @@
                 logger.info(msg, self.base.output.term.bold(pkgspec))
                 errs.append(pkgspec)
 
-        for repo_id, pkg_spec_list in self.state.install_packages.items():
-            for pkgspec in pkg_spec_list:
+        for repo_id, pkg_spec_dict in self.state.install_packages.items():
+            for pkgspec in pkg_spec_dict.keys():
                 try:
                     self.base.install(pkgspec, reponame=repo_id)
                 except dnf.exceptions.MarkingError:
@@ -582,7 +639,9 @@
             clear_dir(self.base.conf.destdir)
         with self.state as state:
             state.download_status = None
+            state.state_version = None
             state.upgrade_status = None
+            state.upgrade_command = None
             state.destdir = None
             state.install_packages = {}
             state.remove_packages = []
@@ -593,19 +652,46 @@
         else:
             list_logs()
 
+    # == resolved_*: do staff after succesful resolvement =====================
+
+    def resolved_upgrade(self):
+        """Adjust transaction reasons according to stored values"""
+        if not self.cli.base.transaction:
+            return
+        backward_action = set(dnf.transaction.BACKWARD_ACTIONS + \
+                              
[libdnf.transaction.TransactionItemAction_REINSTALLED])
+        forward_actions = set(dnf.transaction.FORWARD_ACTIONS)
+
+        install_packages = self.state.install_packages
+        remove_packages = self.state.remove_packages
+        for tsi in self.cli.base.transaction:
+            if tsi.action in forward_actions:
+                pkg = tsi.pkg
+                try:
+                    stored_reason = 
install_packages[pkg.repo.id][str(pkg)][str(tsi.action)]
+                    if stored_reason != tsi.reason:
+                        tsi.reason = stored_reason
+                except KeyError:
+                    pass
+            elif tsi.action in backward_action:
+                pkg = tsi.pkg
+                try:
+                    stored_reason = remove_packages[str(pkg)][str(tsi.action)]
+                    if stored_reason != tsi.reason:
+                        tsi.reason = stored_reason
+                except KeyError:
+                    pass
+
     # == transaction_*: do stuff after a successful transaction ===============
 
     def transaction_download(self):
-        downloads = self.cli.base.transaction.install_set
-        install_packages = {}
-        for pkg in downloads:
-            install_packages.setdefault(pkg.repo.id, []).append(str(pkg))
-        remove_packages = [str(pkg) for pkg in 
self.cli.base.transaction.remove_set]
+        install_packages, remove_packages = 
self._get_forward_reverse_pkg_reason_pairs()
 
         # Okay! Write out the state so the upgrade can use it.
         system_ver = dnf.rpm.detect_releasever(self.base.conf.installroot)
         with self.state as state:
             state.download_status = 'complete'
+            state.state_version = STATE_VERSION
             state.distro_sync = self.opts.distro_sync
             state.allow_erasing = self.cli.demands.allow_erasing
             state.gpgcheck = self.base.conf.gpgcheck
@@ -622,7 +708,9 @@
             state.module_platform_id = self.base.conf.module_platform_id
             state.enable_disable_repos = self.opts.repos_ed
             state.destdir = self.base.conf.destdir
-        logger.info(DOWNLOAD_FINISHED_MSG)
+            state.upgrade_command = self.opts.command
+        msg = DOWNLOAD_FINISHED_MSG.format(command=self.opts.command)
+        logger.info(msg)
         self.log_status(_("Download finished."),
                         DOWNLOAD_FINISHED_ID)
 
@@ -633,3 +721,13 @@
         self.run_clean()
         if self.opts.tid[0] == "upgrade":
             reboot()
+
+
+class OfflineUpgradeCommand(SystemUpgradeCommand):
+    aliases = ('offline-upgrade',)
+    summary = _("Prepare offline upgrade of the system")
+
+
+class OfflineDistrosyncCommand(SystemUpgradeCommand):
+    aliases = ('offline-distrosync',)
+    summary = _("Prepare offline distrosync of the system")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/dnf-plugins-extras-4.0.8/tests/test_system_upgrade.py 
new/dnf-plugins-extras-4.0.9/tests/test_system_upgrade.py
--- old/dnf-plugins-extras-4.0.8/tests/test_system_upgrade.py   2019-11-07 
11:41:51.000000000 +0100
+++ new/dnf-plugins-extras-4.0.9/tests/test_system_upgrade.py   2020-02-24 
15:52:08.000000000 +0100
@@ -259,6 +259,7 @@
         self.command = system_upgrade.SystemUpgradeCommand(cli=self.cli)
         self.command.base.conf.cachedir = os.path.join(self.statedir, "cache")
         self.command.base.conf.destdir = None
+        system_upgrade.DEFAULT_DATADIR = os.path.join(self.statedir, 
'default_datadir')
 
     def tearDown(self):
         shutil.rmtree(self.statedir)
@@ -315,16 +316,26 @@
         self.command.configure_reboot()
         self.assertTrue(self.cli.demands.root_user)
 
-    def check_reboot(self, status='complete', lexists=False):
+    def check_reboot(self, status='complete', lexists=False, 
command='system-upgrade',
+                     state_command='system-upgrade'):
         with patch('system_upgrade.os.path.lexists') as lexists_func,\
                 patch('system_upgrade.DEFAULT_DATADIR', self.DEFAULT_DATADIR):
+            self.command.state.state_version = 2
             self.command.state.download_status = status
+            self.command.opts = mock.MagicMock()
+            self.command.opts.command = command
+            self.command.state.upgrade_command = state_command
             lexists_func.return_value = lexists
             self.command.check_reboot()
 
     def test_check_reboot_ok(self):
         self.check_reboot(status='complete', lexists=False)
 
+    def test_check_reboot_different_command(self):
+        with self.assertRaises(CliError):
+            self.check_reboot(status='complete', lexists=False, 
command='system-upgrade',
+                              state_command='offline-upgrade')
+
     def test_check_reboot_no_download(self):
         with self.assertRaises(CliError):
             self.check_reboot(status=None, lexists=False)
@@ -336,7 +347,7 @@
     def test_run_prepare(self):
         with patch('system_upgrade.MAGIC_SYMLINK', self.MAGIC_SYMLINK):
             self.command.run_prepare()
-        self.assertEqual(os.readlink(self.MAGIC_SYMLINK), DEFAULT_DATADIR)
+        self.assertEqual(os.readlink(self.MAGIC_SYMLINK), 
system_upgrade.DEFAULT_DATADIR)
         self.assertEqual(self.command.state.upgrade_status, 'ready')
 
     @patch('system_upgrade.SystemUpgradeCommand.run_prepare')
@@ -366,14 +377,16 @@
 class DownloadCommandTestCase(CommandTestCase):
     def test_pre_configure_download_default(self):
         self.command.opts = mock.MagicMock()
+        self.command.opts.destdir = None
+        self.command.base.conf.destdir = None
         self.command.pre_configure_download()
-        self.assertEqual(self.command.base.conf.cachedir, DEFAULT_DATADIR)
+        self.assertEqual(self.command.base.conf.cachedir, 
system_upgrade.DEFAULT_DATADIR)
 
     def test_pre_configure_download_destdir(self):
         self.command.opts = mock.MagicMock()
-        self.command.opts.destdir = "/grape/wine"
+        self.command.opts.destdir = self.statedir
         self.command.pre_configure_download()
-        self.assertEqual(self.command.base.conf.destdir, "/grape/wine")
+        self.assertEqual(self.command.base.conf.destdir, self.statedir)
 
     def test_configure_download(self):
         self.command.opts = mock.MagicMock()
@@ -392,23 +405,56 @@
         pkg.repo = repo
         self.cli.base.transaction.install_set = [pkg]
         self.command.opts = mock.MagicMock()
-        self.command.opts.distro_sync = "distro_sync"
+        self.command.opts.distro_sync = True
+        self.command.opts.command = "system_upgrade"
         self.command.opts.repos_ed = []
         self.cli.demands.allow_erasing = "allow_erasing"
-        self.command.base.conf.best = "best"
+        self.command.base.conf.best = True
         self.command.base.conf.installroot = "/"
         self.command.base.conf.releasever = "35"
         self.command.base.conf.gpgcheck = True
-        self.command.base.conf.destdir = "/grape/wine"
+        self.command.opts.destdir = self.statedir
         self.command.base.conf.install_weak_deps = True
         self.command.base.conf.module_platform_id = ''
+        self.command.pre_configure_download()
+        self.command.transaction_download()
+        with system_upgrade.State() as state:
+            self.assertEqual(state.state_version, system_upgrade.STATE_VERSION)
+            self.assertEqual(state.download_status, "complete")
+            self.assertEqual(state.distro_sync, True)
+            self.assertEqual(state.allow_erasing, "allow_erasing")
+            self.assertEqual(state.best, True)
+            self.assertEqual(state.destdir, self.statedir)
+            self.assertEqual(state.upgrade_command, "system_upgrade")
+
+    def test_transaction_download_offline_upgrade(self):
+        pkg = mock.MagicMock()
+        repo = mock.MagicMock()
+        repo.id = 'test'
+        pkg.name = "kernel"
+        pkg.repo = repo
+        self.cli.base.transaction.install_set = [pkg]
+        self.command.opts = mock.MagicMock()
+        self.command.opts.distro_sync = True
+        self.command.opts.command = "offline-upgrade"
+        self.command.opts.repos_ed = []
+        self.cli.demands.allow_erasing = "allow_erasing"
+        self.command.base.conf.best = True
+        self.command.base.conf.installroot = "/"
+        self.command.base.conf.releasever = "35"
+        self.command.base.conf.gpgcheck = True
+        self.command.opts.destdir = self.statedir
+        self.command.base.conf.install_weak_deps = True
+        self.command.base.conf.module_platform_id = ''
+        self.command.pre_configure_download()
         self.command.transaction_download()
         with system_upgrade.State() as state:
             self.assertEqual(state.download_status, "complete")
-            self.assertEqual(state.distro_sync, "distro_sync")
+            self.assertEqual(state.distro_sync, False)
             self.assertEqual(state.allow_erasing, "allow_erasing")
-            self.assertEqual(state.best, "best")
-            self.assertEqual(state.destdir, "/grape/wine")
+            self.assertEqual(state.best, True)
+            self.assertEqual(state.destdir, self.statedir)
+            self.assertEqual(state.upgrade_command, "offline-upgrade")
 
 
 class UpgradeCommandTestCase(CommandTestCase):


Reply via email to