import CS dnf-4.20.0-12.el10
This commit is contained in:
parent
8ed3aeb040
commit
33736f2fb0
@ -0,0 +1,98 @@
|
|||||||
|
From b00c7171f58dbbda3df4bf5f2e65cbc7eff37a5b Mon Sep 17 00:00:00 2001
|
||||||
|
From: David Cantrell <dcantrell@redhat.com>
|
||||||
|
Date: Thu, 15 Feb 2024 14:03:32 -0500
|
||||||
|
Subject: [PATCH] Add detection for ostree-based systems and warn users about
|
||||||
|
losing changes
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
Upstream commit: 5c050ba2324c5fb95bf0e0501c7925f38f6a09dc
|
||||||
|
|
||||||
|
On ostree-based systems, users can use dnf to customize the
|
||||||
|
environment but those changes will be lost at the next ostree-based
|
||||||
|
image update. If you want to retain changes between ostree-updates
|
||||||
|
you need to make use of rpm-ostree right now.
|
||||||
|
|
||||||
|
Signed-off-by: David Cantrell <dcantrell@redhat.com>
|
||||||
|
Resolves: https://issues.redhat.com/browse/RHEL-49671
|
||||||
|
Signed-off-by: Petr Písař <ppisar@redhat.com>
|
||||||
|
---
|
||||||
|
dnf/cli/cli.py | 9 +++++++++
|
||||||
|
dnf/util.py | 31 +++++++++++++++++++++++++++++++
|
||||||
|
2 files changed, 40 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/dnf/cli/cli.py b/dnf/cli/cli.py
|
||||||
|
index 1824bd00e..c14f83639 100644
|
||||||
|
--- a/dnf/cli/cli.py
|
||||||
|
+++ b/dnf/cli/cli.py
|
||||||
|
@@ -214,6 +214,15 @@ class BaseCli(dnf.Base):
|
||||||
|
elif 'test' in self.conf.tsflags:
|
||||||
|
logger.info(_("{prog} will only download packages, install gpg keys, and check the "
|
||||||
|
"transaction.").format(prog=dnf.util.MAIN_PROG_UPPER))
|
||||||
|
+ if dnf.util.is_container():
|
||||||
|
+ _container_msg = _("""
|
||||||
|
+*** This system is managed with ostree. Changes to the system
|
||||||
|
+*** made with dnf will be lost with the next ostree-based update.
|
||||||
|
+*** If you do not want to lose these changes, use 'rpm-ostree'.
|
||||||
|
+""")
|
||||||
|
+ logger.info(_container_msg)
|
||||||
|
+ raise CliError(_("Operation aborted."))
|
||||||
|
+
|
||||||
|
if self._promptWanted():
|
||||||
|
if self.conf.assumeno or not self.output.userconfirm():
|
||||||
|
raise CliError(_("Operation aborted."))
|
||||||
|
diff --git a/dnf/util.py b/dnf/util.py
|
||||||
|
index 6cd7ad41f..1b465bda5 100644
|
||||||
|
--- a/dnf/util.py
|
||||||
|
+++ b/dnf/util.py
|
||||||
|
@@ -33,11 +33,13 @@ import errno
|
||||||
|
import functools
|
||||||
|
import hawkey
|
||||||
|
import itertools
|
||||||
|
+import json
|
||||||
|
import locale
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import pwd
|
||||||
|
import shutil
|
||||||
|
+import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import time
|
||||||
|
@@ -639,3 +641,32 @@ def _is_file_pattern_present(specs):
|
||||||
|
if subj._filename_pattern:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def is_container():
|
||||||
|
+ """Returns true is the system is managed as an immutable container,
|
||||||
|
+ false otherwise. If msg is True, a warning message is displayed
|
||||||
|
+ for the user.
|
||||||
|
+ """
|
||||||
|
+
|
||||||
|
+ bootc = '/usr/bin/bootc'
|
||||||
|
+ ostree = '/sysroot/ostree'
|
||||||
|
+
|
||||||
|
+ if os.path.isfile(bootc) and os.access(bootc, os.X_OK):
|
||||||
|
+ p = subprocess.Popen([bootc, "status", "--json"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
+ (out, err) = p.communicate()
|
||||||
|
+
|
||||||
|
+ if p.returncode == 0:
|
||||||
|
+ # check the output of 'bootc status'
|
||||||
|
+ j = json.loads(out)
|
||||||
|
+
|
||||||
|
+ # XXX: the API from bootc status is evolving
|
||||||
|
+ status = j.get("status", "")
|
||||||
|
+ kind = j.get("kind", "")
|
||||||
|
+
|
||||||
|
+ if kind.lower() == "bootchost" and bool(status.get("isContainer", None)):
|
||||||
|
+ return True
|
||||||
|
+ elif os.path.isdir(ostree):
|
||||||
|
+ return True
|
||||||
|
+
|
||||||
|
+ return False
|
||||||
|
--
|
||||||
|
2.46.2
|
||||||
|
|
106
0007-Update-ostree-bootc-host-system-check.patch
Normal file
106
0007-Update-ostree-bootc-host-system-check.patch
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
From e2dbb97b9e13a73c47dd59827d7f2214bbdde99f Mon Sep 17 00:00:00 2001
|
||||||
|
From: Joseph Marrero <jmarrero@redhat.com>
|
||||||
|
Date: Tue, 16 Jul 2024 15:48:41 -0400
|
||||||
|
Subject: [PATCH] Update ostree/bootc host system check.
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
Upstream commit: 6120fe52511775b60b6031d4169988c025610ab5
|
||||||
|
|
||||||
|
This changes the is_container() func for _is_bootc_host()
|
||||||
|
and updates the logic and message. This should detect on
|
||||||
|
all ostree and bootc hosts to date that are not using
|
||||||
|
bootc usroverlay or ostree admin unlock for development
|
||||||
|
purposes.
|
||||||
|
|
||||||
|
Resolves: https://issues.redhat.com/browse/RHEL-49671
|
||||||
|
Signed-off-by: Petr Písař <ppisar@redhat.com>
|
||||||
|
---
|
||||||
|
dnf/cli/cli.py | 11 +++++------
|
||||||
|
dnf/util.py | 33 ++++++++-------------------------
|
||||||
|
2 files changed, 13 insertions(+), 31 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/dnf/cli/cli.py b/dnf/cli/cli.py
|
||||||
|
index c14f83639..83b190026 100644
|
||||||
|
--- a/dnf/cli/cli.py
|
||||||
|
+++ b/dnf/cli/cli.py
|
||||||
|
@@ -214,13 +214,12 @@ class BaseCli(dnf.Base):
|
||||||
|
elif 'test' in self.conf.tsflags:
|
||||||
|
logger.info(_("{prog} will only download packages, install gpg keys, and check the "
|
||||||
|
"transaction.").format(prog=dnf.util.MAIN_PROG_UPPER))
|
||||||
|
- if dnf.util.is_container():
|
||||||
|
- _container_msg = _("""
|
||||||
|
-*** This system is managed with ostree. Changes to the system
|
||||||
|
-*** made with dnf will be lost with the next ostree-based update.
|
||||||
|
-*** If you do not want to lose these changes, use 'rpm-ostree'.
|
||||||
|
+ if dnf.util._is_bootc_host():
|
||||||
|
+ _bootc_host_msg = _("""
|
||||||
|
+*** Error: system is configured to be read-only; for more
|
||||||
|
+*** information run `bootc status` or `ostree admin status`.
|
||||||
|
""")
|
||||||
|
- logger.info(_container_msg)
|
||||||
|
+ logger.info(_bootc_host_msg)
|
||||||
|
raise CliError(_("Operation aborted."))
|
||||||
|
|
||||||
|
if self._promptWanted():
|
||||||
|
diff --git a/dnf/util.py b/dnf/util.py
|
||||||
|
index 1b465bda5..1ba2e27ff 100644
|
||||||
|
--- a/dnf/util.py
|
||||||
|
+++ b/dnf/util.py
|
||||||
|
@@ -33,13 +33,11 @@ import errno
|
||||||
|
import functools
|
||||||
|
import hawkey
|
||||||
|
import itertools
|
||||||
|
-import json
|
||||||
|
import locale
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import pwd
|
||||||
|
import shutil
|
||||||
|
-import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import time
|
||||||
|
@@ -643,30 +641,15 @@ def _is_file_pattern_present(specs):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
-def is_container():
|
||||||
|
+def _is_bootc_host():
|
||||||
|
"""Returns true is the system is managed as an immutable container,
|
||||||
|
false otherwise. If msg is True, a warning message is displayed
|
||||||
|
for the user.
|
||||||
|
"""
|
||||||
|
-
|
||||||
|
- bootc = '/usr/bin/bootc'
|
||||||
|
- ostree = '/sysroot/ostree'
|
||||||
|
-
|
||||||
|
- if os.path.isfile(bootc) and os.access(bootc, os.X_OK):
|
||||||
|
- p = subprocess.Popen([bootc, "status", "--json"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
- (out, err) = p.communicate()
|
||||||
|
-
|
||||||
|
- if p.returncode == 0:
|
||||||
|
- # check the output of 'bootc status'
|
||||||
|
- j = json.loads(out)
|
||||||
|
-
|
||||||
|
- # XXX: the API from bootc status is evolving
|
||||||
|
- status = j.get("status", "")
|
||||||
|
- kind = j.get("kind", "")
|
||||||
|
-
|
||||||
|
- if kind.lower() == "bootchost" and bool(status.get("isContainer", None)):
|
||||||
|
- return True
|
||||||
|
- elif os.path.isdir(ostree):
|
||||||
|
- return True
|
||||||
|
-
|
||||||
|
- return False
|
||||||
|
+ ostree_booted = '/run/ostree-booted'
|
||||||
|
+ usr = '/usr/'
|
||||||
|
+ # Check if usr is writtable and we are in a running ostree system.
|
||||||
|
+ # We want this code to return true only when the system is in locked state. If someone ran
|
||||||
|
+ # bootc overlay or ostree admin unlock we would want normal DNF path to be ran as it will be
|
||||||
|
+ # temporary changes (until reboot).
|
||||||
|
+ return os.path.isfile(ostree_booted) and not os.access(usr, os.W_OK)
|
||||||
|
--
|
||||||
|
2.46.2
|
||||||
|
|
32
0008-Update-bootc-hosts-message-to-point-to-bootc-help.patch
Normal file
32
0008-Update-bootc-hosts-message-to-point-to-bootc-help.patch
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
From 15aedf5f4e70695e7801c80498d4da52e49ac626 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Joseph Marrero <jmarrero@redhat.com>
|
||||||
|
Date: Mon, 22 Jul 2024 15:33:32 -0400
|
||||||
|
Subject: [PATCH] Update bootc hosts message to point to bootc --help
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
Upstream commit: e2535589ce16bc36b96b37369502a3c312f6056a
|
||||||
|
Resolves: https://issues.redhat.com/browse/RHEL-49671
|
||||||
|
|
||||||
|
Signed-off-by: Petr Písař <ppisar@redhat.com>
|
||||||
|
---
|
||||||
|
dnf/cli/cli.py | 2 +-
|
||||||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/dnf/cli/cli.py b/dnf/cli/cli.py
|
||||||
|
index 83b190026..0eda2c8cb 100644
|
||||||
|
--- a/dnf/cli/cli.py
|
||||||
|
+++ b/dnf/cli/cli.py
|
||||||
|
@@ -217,7 +217,7 @@ class BaseCli(dnf.Base):
|
||||||
|
if dnf.util._is_bootc_host():
|
||||||
|
_bootc_host_msg = _("""
|
||||||
|
*** Error: system is configured to be read-only; for more
|
||||||
|
-*** information run `bootc status` or `ostree admin status`.
|
||||||
|
+*** information run `bootc --help`.
|
||||||
|
""")
|
||||||
|
logger.info(_bootc_host_msg)
|
||||||
|
raise CliError(_("Operation aborted."))
|
||||||
|
--
|
||||||
|
2.46.2
|
||||||
|
|
47
0009-Allow-installroot-on-read-only-bootc-system.patch
Normal file
47
0009-Allow-installroot-on-read-only-bootc-system.patch
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
From ff86cee7cf33f44e4b10538ceeee5f284d6735ed Mon Sep 17 00:00:00 2001
|
||||||
|
From: =?UTF-8?q?Petr=20P=C3=ADsa=C5=99?= <ppisar@redhat.com>
|
||||||
|
Date: Thu, 15 Aug 2024 14:04:55 +0200
|
||||||
|
Subject: [PATCH] Allow --installroot on read-only bootc system
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
Upstream commit: a1aa8d0e048751859a2bec1b2fb12fcca93c6e83
|
||||||
|
|
||||||
|
Some people use --installroot on a read-only bootc system to install
|
||||||
|
a system into a chroot subtree. However, current bootc check did not
|
||||||
|
take into account --installroot and rejected the operation.
|
||||||
|
|
||||||
|
This patch augments the check for the installroot being different
|
||||||
|
from /.
|
||||||
|
|
||||||
|
It's pointless to check for installroot writability here because
|
||||||
|
installroot is written before this check when updating the
|
||||||
|
repositories and computing a transaction. Moving this check sooner
|
||||||
|
would not help because some directories (/opt, /) are kept read-only
|
||||||
|
even on writable bootc.
|
||||||
|
|
||||||
|
Resolves: #2108
|
||||||
|
Resolves: https://issues.redhat.com/browse/RHEL-49671
|
||||||
|
Signed-off-by: Petr Písař <ppisar@redhat.com>
|
||||||
|
---
|
||||||
|
dnf/cli/cli.py | 3 ++-
|
||||||
|
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/dnf/cli/cli.py b/dnf/cli/cli.py
|
||||||
|
index 0eda2c8cb..008262ea0 100644
|
||||||
|
--- a/dnf/cli/cli.py
|
||||||
|
+++ b/dnf/cli/cli.py
|
||||||
|
@@ -214,7 +214,8 @@ class BaseCli(dnf.Base):
|
||||||
|
elif 'test' in self.conf.tsflags:
|
||||||
|
logger.info(_("{prog} will only download packages, install gpg keys, and check the "
|
||||||
|
"transaction.").format(prog=dnf.util.MAIN_PROG_UPPER))
|
||||||
|
- if dnf.util._is_bootc_host():
|
||||||
|
+ if dnf.util._is_bootc_host() and \
|
||||||
|
+ os.path.realpath(self.conf.installroot) == "/":
|
||||||
|
_bootc_host_msg = _("""
|
||||||
|
*** Error: system is configured to be read-only; for more
|
||||||
|
*** information run `bootc --help`.
|
||||||
|
--
|
||||||
|
2.46.2
|
||||||
|
|
40
0010-Allow-downloadonly-on-read-only-bootc-system.patch
Normal file
40
0010-Allow-downloadonly-on-read-only-bootc-system.patch
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
From 86bc1d60e1b8188ca5a682974d734ac3a0cdc102 Mon Sep 17 00:00:00 2001
|
||||||
|
From: =?UTF-8?q?Petr=20P=C3=ADsa=C5=99?= <ppisar@redhat.com>
|
||||||
|
Date: Thu, 10 Oct 2024 10:57:48 +0200
|
||||||
|
Subject: [PATCH] Allow --downloadonly on read-only bootc system
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
Upstream commit: 8d888d26e6da27ba37243d7504eb42472f389bde
|
||||||
|
|
||||||
|
"dnf install --downloadonly" failed on read-only bootc system despite
|
||||||
|
not running the transaction. The downloaded packages are stored under
|
||||||
|
writable /var or to a directory explicitly choosen by a user.
|
||||||
|
|
||||||
|
This patch suppresses the bootc read-only bailout if --downloadonly
|
||||||
|
option is used.
|
||||||
|
|
||||||
|
https://issues.redhat.com/browse/RHEL-62028
|
||||||
|
Signed-off-by: Petr Písař <ppisar@redhat.com>
|
||||||
|
---
|
||||||
|
dnf/cli/cli.py | 3 ++-
|
||||||
|
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/dnf/cli/cli.py b/dnf/cli/cli.py
|
||||||
|
index 008262ea0..d3844df34 100644
|
||||||
|
--- a/dnf/cli/cli.py
|
||||||
|
+++ b/dnf/cli/cli.py
|
||||||
|
@@ -215,7 +215,8 @@ class BaseCli(dnf.Base):
|
||||||
|
logger.info(_("{prog} will only download packages, install gpg keys, and check the "
|
||||||
|
"transaction.").format(prog=dnf.util.MAIN_PROG_UPPER))
|
||||||
|
if dnf.util._is_bootc_host() and \
|
||||||
|
- os.path.realpath(self.conf.installroot) == "/":
|
||||||
|
+ os.path.realpath(self.conf.installroot) == "/" and \
|
||||||
|
+ not self.conf.downloadonly:
|
||||||
|
_bootc_host_msg = _("""
|
||||||
|
*** Error: system is configured to be read-only; for more
|
||||||
|
*** information run `bootc --help`.
|
||||||
|
--
|
||||||
|
2.47.0
|
||||||
|
|
131
0011-Automatic-check-availability-of-config-file.patch
Normal file
131
0011-Automatic-check-availability-of-config-file.patch
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
From 36389b63b12c2f6996772bd2bc7e98b6f3bacdf0 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Marek Blaha <mblaha@redhat.com>
|
||||||
|
Date: Thu, 17 Oct 2024 13:30:21 +0200
|
||||||
|
Subject: [PATCH 1/2] automatic: Check availability of config file
|
||||||
|
|
||||||
|
Upstream commit: 13ecc3921fb1566c2af7b80d5cb515d8fc4e5ec9
|
||||||
|
|
||||||
|
If a configuration file is explicitly specified on the command line,
|
||||||
|
ensure that it exists and is readable. If the file is not found, notify
|
||||||
|
the user immediately and terminate the process.
|
||||||
|
|
||||||
|
This resolves issues where users may run dnf-automatic with unrecognized
|
||||||
|
positional arguments, such as `dnf-automatic install`.
|
||||||
|
|
||||||
|
The most natural approach to handle a non-existing config file would be
|
||||||
|
by catching the exception thrown by the `read()` method of the
|
||||||
|
`libdnf.conf.ConfigParser` class. Unfortunately, the Python bindings
|
||||||
|
override the `read()` method at the SWIG level, causing it to suppress any
|
||||||
|
potentially raised IOError.
|
||||||
|
For details see this section of the commit
|
||||||
|
https://github.com/rpm-software-management/libdnf/commit/8f1fedf8551b72d6bc24018f5980714b3a103aeb
|
||||||
|
|
||||||
|
def ConfigParser__newRead(self, filenames):
|
||||||
|
parsedFNames = []
|
||||||
|
try:
|
||||||
|
if isinstance(filenames, str) or isinstance(filenames, unicode):
|
||||||
|
filenames = [filenames]
|
||||||
|
except NameError:
|
||||||
|
pass
|
||||||
|
for fname in filenames:
|
||||||
|
try:
|
||||||
|
self.readFileName(fname)
|
||||||
|
parsedFNames.append(fname)
|
||||||
|
except IOError:
|
||||||
|
pass
|
||||||
|
except Exception as e:
|
||||||
|
raise RuntimeError("Parsing file '%s' failed: %s" % (fname, str(e)))
|
||||||
|
return parsedFNames
|
||||||
|
ConfigParser.read = ConfigParser__newRead
|
||||||
|
|
||||||
|
Resolves: https://issues.redhat.com/browse/RHEL-68979
|
||||||
|
---
|
||||||
|
dnf/automatic/main.py | 19 +++++++++++++++----
|
||||||
|
1 file changed, 15 insertions(+), 4 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/dnf/automatic/main.py b/dnf/automatic/main.py
|
||||||
|
index 243e3015..21aa82b7 100644
|
||||||
|
--- a/dnf/automatic/main.py
|
||||||
|
+++ b/dnf/automatic/main.py
|
||||||
|
@@ -74,7 +74,7 @@ def build_emitters(conf):
|
||||||
|
|
||||||
|
def parse_arguments(args):
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
- parser.add_argument('conf_path', nargs='?', default=dnf.const.CONF_AUTOMATIC_FILENAME)
|
||||||
|
+ parser.add_argument('conf_path', nargs='?')
|
||||||
|
parser.add_argument('--timer', action='store_true')
|
||||||
|
parser.add_argument('--installupdates', dest='installupdates', action='store_true')
|
||||||
|
parser.add_argument('--downloadupdates', dest='downloadupdates', action='store_true')
|
||||||
|
@@ -89,7 +89,17 @@ def parse_arguments(args):
|
||||||
|
class AutomaticConfig(object):
|
||||||
|
def __init__(self, filename=None, downloadupdates=None,
|
||||||
|
installupdates=None):
|
||||||
|
- if not filename:
|
||||||
|
+ if filename:
|
||||||
|
+ # Specific config file was explicitely requested. Check that it exists
|
||||||
|
+ # and is readable.
|
||||||
|
+ if os.access(filename, os.F_OK):
|
||||||
|
+ if not os.access(filename, os.R_OK):
|
||||||
|
+ raise dnf.exceptions.Error(
|
||||||
|
+ "Configuration file \"{}\" is not readable.".format(filename))
|
||||||
|
+ else:
|
||||||
|
+ raise dnf.exceptions.Error(
|
||||||
|
+ "Configuration file \"{}\" not found.".format(filename))
|
||||||
|
+ else:
|
||||||
|
filename = dnf.const.CONF_AUTOMATIC_FILENAME
|
||||||
|
self.commands = CommandsConfig()
|
||||||
|
self.email = EmailConfig()
|
||||||
|
@@ -302,11 +312,12 @@ def wait_for_network(repos, timeout):
|
||||||
|
|
||||||
|
def main(args):
|
||||||
|
(opts, parser) = parse_arguments(args)
|
||||||
|
+ conf = None
|
||||||
|
+ emitters = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
conf = AutomaticConfig(opts.conf_path, opts.downloadupdates,
|
||||||
|
opts.installupdates)
|
||||||
|
- emitters = None
|
||||||
|
with dnf.Base() as base:
|
||||||
|
cli = dnf.cli.Cli(base)
|
||||||
|
cli._read_conf_file()
|
||||||
|
@@ -371,7 +382,7 @@ def main(args):
|
||||||
|
raise dnf.exceptions.Error('reboot command returned nonzero exit code: %d', exit_code)
|
||||||
|
except dnf.exceptions.Error as exc:
|
||||||
|
logger.error(_('Error: %s'), ucd(exc))
|
||||||
|
- if conf.emitters.send_error_messages and emitters != None:
|
||||||
|
+ if conf is not None and conf.emitters.send_error_messages and emitters is not None:
|
||||||
|
emitters.notify_error(_('Error: %s') % str(exc))
|
||||||
|
emitters.commit()
|
||||||
|
return 1
|
||||||
|
--
|
||||||
|
2.47.1
|
||||||
|
|
||||||
|
|
||||||
|
From bfc4d7ae2d9a567aaf656388c8b914b7a03db39d Mon Sep 17 00:00:00 2001
|
||||||
|
From: Marek Blaha <mblaha@redhat.com>
|
||||||
|
Date: Thu, 12 Dec 2024 08:09:48 +0100
|
||||||
|
Subject: [PATCH 2/2] automatic: Fix incorrect Error class instantiation
|
||||||
|
|
||||||
|
dnf.exceptions.Error class constructor accepts only one argument - error
|
||||||
|
message.
|
||||||
|
---
|
||||||
|
dnf/automatic/main.py | 2 +-
|
||||||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/dnf/automatic/main.py b/dnf/automatic/main.py
|
||||||
|
index 21aa82b7..1a1e1cf0 100644
|
||||||
|
--- a/dnf/automatic/main.py
|
||||||
|
+++ b/dnf/automatic/main.py
|
||||||
|
@@ -379,7 +379,7 @@ def main(args):
|
||||||
|
(conf.commands.reboot == 'when-needed' and base.reboot_needed())):
|
||||||
|
exit_code = os.waitstatus_to_exitcode(os.system(conf.commands.reboot_command))
|
||||||
|
if exit_code != 0:
|
||||||
|
- raise dnf.exceptions.Error('reboot command returned nonzero exit code: %d', exit_code)
|
||||||
|
+ raise dnf.exceptions.Error('reboot command returned nonzero exit code: %d' % exit_code)
|
||||||
|
except dnf.exceptions.Error as exc:
|
||||||
|
logger.error(_('Error: %s'), ucd(exc))
|
||||||
|
if conf is not None and conf.emitters.send_error_messages and emitters is not None:
|
||||||
|
--
|
||||||
|
2.47.1
|
||||||
|
|
181
0012-Add-support-for-transient.patch
Normal file
181
0012-Add-support-for-transient.patch
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
From 082973b36646945b1c60be8b96ab628d92d99b92 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Evan Goode <mail@evangoo.de>
|
||||||
|
Date: Thu, 7 Nov 2024 02:31:25 +0000
|
||||||
|
Subject: [PATCH 12/17] Add support for --transient
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
Upstream commit: 6091f3fccea988208ca417a504569947e4c99263
|
||||||
|
|
||||||
|
Adds support for the --transient option on all transactions. Passing
|
||||||
|
--transient on a bootc system will call `bootc usr-overlay` to create a
|
||||||
|
transient writeable /usr and continue the transaction.
|
||||||
|
|
||||||
|
Specifying --transient on a non-bootc system will throw an error; we
|
||||||
|
don't want to mislead users to thinking this feature works on non-bootc
|
||||||
|
systems.
|
||||||
|
|
||||||
|
If --transient is not specified and the bootc system is in a locked
|
||||||
|
state, the operation will be aborted and a message will be printed
|
||||||
|
suggesting to try again with --transient.
|
||||||
|
|
||||||
|
Resolves: https://issues.redhat.com/browse/RHEL-76849
|
||||||
|
Signed-off-by: Petr Písař <ppisar@redhat.com>
|
||||||
|
---
|
||||||
|
dnf/cli/cli.py | 40 ++++++++++++++++++++++++++++++---------
|
||||||
|
dnf/cli/option_parser.py | 3 +++
|
||||||
|
dnf/conf/config.py | 2 +-
|
||||||
|
dnf/util.py | 41 +++++++++++++++++++++++++++++-----------
|
||||||
|
4 files changed, 65 insertions(+), 21 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/dnf/cli/cli.py b/dnf/cli/cli.py
|
||||||
|
index d3844df34..53f2d9d50 100644
|
||||||
|
--- a/dnf/cli/cli.py
|
||||||
|
+++ b/dnf/cli/cli.py
|
||||||
|
@@ -205,28 +205,50 @@ class BaseCli(dnf.Base):
|
||||||
|
else:
|
||||||
|
self.output.reportDownloadSize(install_pkgs, install_only)
|
||||||
|
|
||||||
|
+ bootc_unlock_requested = False
|
||||||
|
+
|
||||||
|
if trans or self._moduleContainer.isChanged() or \
|
||||||
|
(self._history and (self._history.group or self._history.env)):
|
||||||
|
# confirm with user
|
||||||
|
if self.conf.downloadonly:
|
||||||
|
logger.info(_("{prog} will only download packages for the transaction.").format(
|
||||||
|
prog=dnf.util.MAIN_PROG_UPPER))
|
||||||
|
+
|
||||||
|
elif 'test' in self.conf.tsflags:
|
||||||
|
logger.info(_("{prog} will only download packages, install gpg keys, and check the "
|
||||||
|
"transaction.").format(prog=dnf.util.MAIN_PROG_UPPER))
|
||||||
|
- if dnf.util._is_bootc_host() and \
|
||||||
|
- os.path.realpath(self.conf.installroot) == "/" and \
|
||||||
|
- not self.conf.downloadonly:
|
||||||
|
- _bootc_host_msg = _("""
|
||||||
|
-*** Error: system is configured to be read-only; for more
|
||||||
|
-*** information run `bootc --help`.
|
||||||
|
-""")
|
||||||
|
- logger.info(_bootc_host_msg)
|
||||||
|
- raise CliError(_("Operation aborted."))
|
||||||
|
+
|
||||||
|
+ is_bootc_transaction = dnf.util._is_bootc_host() and \
|
||||||
|
+ os.path.realpath(self.conf.installroot) == "/" and \
|
||||||
|
+ not self.conf.downloadonly
|
||||||
|
+
|
||||||
|
+ # Handle bootc transactions. `--transient` must be specified if
|
||||||
|
+ # /usr is not already writeable.
|
||||||
|
+ if is_bootc_transaction:
|
||||||
|
+ if self.conf.persistence == "persist":
|
||||||
|
+ logger.info(_("Persistent transactions aren't supported on bootc systems."))
|
||||||
|
+ raise CliError(_("Operation aborted."))
|
||||||
|
+ assert self.conf.persistence in ("auto", "transient")
|
||||||
|
+ if not dnf.util._is_bootc_unlocked():
|
||||||
|
+ if self.conf.persistence == "auto":
|
||||||
|
+ logger.info(_("This bootc system is configured to be read-only. Pass --transient to "
|
||||||
|
+ "perform this and subsequent transactions in a transient overlay which "
|
||||||
|
+ "will reset when the system reboots."))
|
||||||
|
+ raise CliError(_("Operation aborted."))
|
||||||
|
+ assert self.conf.persistence == "transient"
|
||||||
|
+ logger.info(_("A transient overlay will be created on /usr that will be discarded on reboot. "
|
||||||
|
+ "Keep in mind that changes to /etc and /var will still persist, and packages "
|
||||||
|
+ "commonly modify these directories."))
|
||||||
|
+ bootc_unlock_requested = True
|
||||||
|
+ elif self.conf.persistence == "transient":
|
||||||
|
+ raise CliError(_("Transient transactions are only supported on bootc systems."))
|
||||||
|
|
||||||
|
if self._promptWanted():
|
||||||
|
if self.conf.assumeno or not self.output.userconfirm():
|
||||||
|
raise CliError(_("Operation aborted."))
|
||||||
|
+
|
||||||
|
+ if bootc_unlock_requested:
|
||||||
|
+ dnf.util._bootc_unlock()
|
||||||
|
else:
|
||||||
|
logger.info(_('Nothing to do.'))
|
||||||
|
return
|
||||||
|
diff --git a/dnf/cli/option_parser.py b/dnf/cli/option_parser.py
|
||||||
|
index 042d5fbbe..ec4696fd2 100644
|
||||||
|
--- a/dnf/cli/option_parser.py
|
||||||
|
+++ b/dnf/cli/option_parser.py
|
||||||
|
@@ -317,6 +317,9 @@ class OptionParser(argparse.ArgumentParser):
|
||||||
|
general_grp.add_argument("--downloadonly", dest="downloadonly",
|
||||||
|
action="store_true", default=False,
|
||||||
|
help=_("only download packages"))
|
||||||
|
+ general_grp.add_argument("--transient", dest="persistence",
|
||||||
|
+ action="store_const", const="transient", default=None,
|
||||||
|
+ help=_("Use a transient overlay which will reset on reboot"))
|
||||||
|
general_grp.add_argument("--comment", dest="comment", default=None,
|
||||||
|
help=_("add a comment to transaction"))
|
||||||
|
# Updateinfo options...
|
||||||
|
diff --git a/dnf/conf/config.py b/dnf/conf/config.py
|
||||||
|
index f9c8d932a..5210ffba2 100644
|
||||||
|
--- a/dnf/conf/config.py
|
||||||
|
+++ b/dnf/conf/config.py
|
||||||
|
@@ -343,7 +343,7 @@ class MainConf(BaseConfig):
|
||||||
|
'best', 'assumeyes', 'assumeno', 'clean_requirements_on_remove', 'gpgcheck',
|
||||||
|
'showdupesfromrepos', 'plugins', 'ip_resolve',
|
||||||
|
'rpmverbosity', 'disable_excludes', 'color',
|
||||||
|
- 'downloadonly', 'exclude', 'excludepkgs', 'skip_broken',
|
||||||
|
+ 'downloadonly', 'persistence', 'exclude', 'excludepkgs', 'skip_broken',
|
||||||
|
'tsflags', 'arch', 'basearch', 'ignorearch', 'cacheonly', 'comment']
|
||||||
|
|
||||||
|
for name in config_args:
|
||||||
|
diff --git a/dnf/util.py b/dnf/util.py
|
||||||
|
index 1ba2e27ff..2e270890c 100644
|
||||||
|
--- a/dnf/util.py
|
||||||
|
+++ b/dnf/util.py
|
||||||
|
@@ -38,6 +38,7 @@ import logging
|
||||||
|
import os
|
||||||
|
import pwd
|
||||||
|
import shutil
|
||||||
|
+import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import time
|
||||||
|
@@ -642,14 +643,32 @@ def _is_file_pattern_present(specs):
|
||||||
|
|
||||||
|
|
||||||
|
def _is_bootc_host():
|
||||||
|
- """Returns true is the system is managed as an immutable container,
|
||||||
|
- false otherwise. If msg is True, a warning message is displayed
|
||||||
|
- for the user.
|
||||||
|
- """
|
||||||
|
- ostree_booted = '/run/ostree-booted'
|
||||||
|
- usr = '/usr/'
|
||||||
|
- # Check if usr is writtable and we are in a running ostree system.
|
||||||
|
- # We want this code to return true only when the system is in locked state. If someone ran
|
||||||
|
- # bootc overlay or ostree admin unlock we would want normal DNF path to be ran as it will be
|
||||||
|
- # temporary changes (until reboot).
|
||||||
|
- return os.path.isfile(ostree_booted) and not os.access(usr, os.W_OK)
|
||||||
|
+ """Returns true is the system is managed as an immutable container, false
|
||||||
|
+ otherwise."""
|
||||||
|
+ ostree_booted = "/run/ostree-booted"
|
||||||
|
+ return os.path.isfile(ostree_booted)
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def _is_bootc_unlocked():
|
||||||
|
+ """Check whether /usr is writeable, e.g. if we are in a normal mutable
|
||||||
|
+ system or if we are in a bootc after `bootc usr-overlay` or `ostree admin
|
||||||
|
+ unlock` was run."""
|
||||||
|
+ usr = "/usr"
|
||||||
|
+ return os.access(usr, os.W_OK)
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def _bootc_unlock():
|
||||||
|
+ """Set up a writeable overlay on bootc systems."""
|
||||||
|
+
|
||||||
|
+ if _is_bootc_unlocked():
|
||||||
|
+ return
|
||||||
|
+
|
||||||
|
+ unlock_command = ["bootc", "usr-overlay"]
|
||||||
|
+
|
||||||
|
+ try:
|
||||||
|
+ completed_process = subprocess.run(unlock_command, text=True)
|
||||||
|
+ completed_process.check_returncode()
|
||||||
|
+ except FileNotFoundError:
|
||||||
|
+ raise dnf.exceptions.Error(_("bootc command not found. Is this a bootc system?"))
|
||||||
|
+ except subprocess.CalledProcessError:
|
||||||
|
+ raise dnf.exceptions.Error(_("Failed to unlock system: %s", completed_process.stderr))
|
||||||
|
--
|
||||||
|
2.48.1
|
||||||
|
|
74
0013-bootc-Document-transient-and-persistence.patch
Normal file
74
0013-bootc-Document-transient-and-persistence.patch
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
From 83177634e2a50887d9910048f87277b838eaa2f2 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Evan Goode <mail@evangoo.de>
|
||||||
|
Date: Tue, 17 Dec 2024 18:58:32 +0000
|
||||||
|
Subject: [PATCH 13/17] bootc: Document `--transient` and `persistence`
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
Upstream commit: 80a62d89ba3c00f4e0bd3fea995d04ccf9ba8098
|
||||||
|
|
||||||
|
Documents the new `--transient` command-line argument and `persistence`
|
||||||
|
configuration option. I tried to use a table for listing the valid
|
||||||
|
options for `persistence`, but RST does not automatically wrap table
|
||||||
|
cells containing long lines, so a list was much easier.
|
||||||
|
|
||||||
|
Resolves: https://issues.redhat.com/browse/RHEL-76849
|
||||||
|
Signed-off-by: Petr Písař <ppisar@redhat.com>
|
||||||
|
---
|
||||||
|
doc/command_ref.rst | 9 +++++++++
|
||||||
|
doc/conf_ref.rst | 11 +++++++++++
|
||||||
|
2 files changed, 20 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/doc/command_ref.rst b/doc/command_ref.rst
|
||||||
|
index 5684b0611..2337a2e29 100644
|
||||||
|
--- a/doc/command_ref.rst
|
||||||
|
+++ b/doc/command_ref.rst
|
||||||
|
@@ -388,6 +388,11 @@ Options
|
||||||
|
``--showduplicates``
|
||||||
|
Show duplicate packages in repositories. Applicable for the list and search commands.
|
||||||
|
|
||||||
|
+.. _transient_option-label:
|
||||||
|
+
|
||||||
|
+``--transient``
|
||||||
|
+ Applicable only on bootc (bootable containers) systems. Perform transactions using a transient overlay which will be lost on the next reboot. See also the :ref:`persistence <persistence-label>` configuration option.
|
||||||
|
+
|
||||||
|
.. _verbose_options-label:
|
||||||
|
|
||||||
|
``-v, --verbose``
|
||||||
|
@@ -707,6 +712,10 @@ transactions and act according to this information (assuming the
|
||||||
|
which specifies a transaction by a package which it manipulated. When no
|
||||||
|
transaction is specified, list all known transactions.
|
||||||
|
|
||||||
|
+ Note that transient transactions (see :ref:`--transient
|
||||||
|
+ <transient_option-label>`) will be listed even though they do not make
|
||||||
|
+ persistent changes to files under ``/usr`` or to the RPM database.
|
||||||
|
+
|
||||||
|
The "Action(s)" column lists each type of action taken in the transaction. The possible values are:
|
||||||
|
|
||||||
|
* Install (I): a new package was installed on the system
|
||||||
|
diff --git a/doc/conf_ref.rst b/doc/conf_ref.rst
|
||||||
|
index 240b35f96..d4e4a2770 100644
|
||||||
|
--- a/doc/conf_ref.rst
|
||||||
|
+++ b/doc/conf_ref.rst
|
||||||
|
@@ -442,6 +442,17 @@ configuration file by your distribution to override the DNF defaults.
|
||||||
|
|
||||||
|
Directory where DNF stores its persistent data between runs. Default is ``"/var/lib/dnf"``.
|
||||||
|
|
||||||
|
+.. _persistence-label:
|
||||||
|
+
|
||||||
|
+``persistence``
|
||||||
|
+ :ref:`string <string-label>`
|
||||||
|
+
|
||||||
|
+ Whether changes should persist across system reboots. Default is ``auto``. Passing :ref:`--transient <transient_option-label>` will override this setting to ``transient``. Valid values are:
|
||||||
|
+
|
||||||
|
+ * ``auto``: Changes will persist across reboots, unless the target is a running bootc system and the system is already in an unlocked state (i.e. ``/usr`` is writable).
|
||||||
|
+ * ``transient``: Changes will be lost on the next reboot. Only applicable on bootc systems. Beware that changes to ``/etc`` and ``/var`` will persist, depending on the configuration of your bootc system. See also https://containers.github.io/bootc/man/bootc-usr-overlay.html.
|
||||||
|
+ * ``persist``: Changes will persist across reboots.
|
||||||
|
+
|
||||||
|
.. _pluginconfpath-label:
|
||||||
|
|
||||||
|
``pluginconfpath``
|
||||||
|
--
|
||||||
|
2.48.1
|
||||||
|
|
166
0014-bootc-Use-ostree-GObject-API-to-get-deployment-statu.patch
Normal file
166
0014-bootc-Use-ostree-GObject-API-to-get-deployment-statu.patch
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
From c0f2329c2ed71f373aad26c7f1786494f6e75b76 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Evan Goode <mail@evangoo.de>
|
||||||
|
Date: Wed, 15 Jan 2025 21:43:58 +0000
|
||||||
|
Subject: [PATCH 14/17] bootc: Use ostree GObject API to get deployment status
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
Upstream commit: f3abee56452e40ce475f714666c3a16426759c96
|
||||||
|
|
||||||
|
Using libostree gives us more detail about the current state of the
|
||||||
|
deployment than only checking whether /usr is writable.
|
||||||
|
|
||||||
|
Resolves: https://issues.redhat.com/browse/RHEL-76849
|
||||||
|
Signed-off-by: Petr Písař <ppisar@redhat.com>
|
||||||
|
---
|
||||||
|
dnf/cli/cli.py | 13 ++++----
|
||||||
|
dnf/util.py | 80 +++++++++++++++++++++++++++++++++++---------------
|
||||||
|
2 files changed, 65 insertions(+), 28 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/dnf/cli/cli.py b/dnf/cli/cli.py
|
||||||
|
index 53f2d9d50..bc11505fc 100644
|
||||||
|
--- a/dnf/cli/cli.py
|
||||||
|
+++ b/dnf/cli/cli.py
|
||||||
|
@@ -218,18 +218,22 @@ class BaseCli(dnf.Base):
|
||||||
|
logger.info(_("{prog} will only download packages, install gpg keys, and check the "
|
||||||
|
"transaction.").format(prog=dnf.util.MAIN_PROG_UPPER))
|
||||||
|
|
||||||
|
- is_bootc_transaction = dnf.util._is_bootc_host() and \
|
||||||
|
+ is_bootc_transaction = dnf.util._Bootc.is_bootc_host() and \
|
||||||
|
os.path.realpath(self.conf.installroot) == "/" and \
|
||||||
|
not self.conf.downloadonly
|
||||||
|
|
||||||
|
# Handle bootc transactions. `--transient` must be specified if
|
||||||
|
# /usr is not already writeable.
|
||||||
|
+ bootc = None
|
||||||
|
if is_bootc_transaction:
|
||||||
|
if self.conf.persistence == "persist":
|
||||||
|
logger.info(_("Persistent transactions aren't supported on bootc systems."))
|
||||||
|
raise CliError(_("Operation aborted."))
|
||||||
|
assert self.conf.persistence in ("auto", "transient")
|
||||||
|
- if not dnf.util._is_bootc_unlocked():
|
||||||
|
+
|
||||||
|
+ bootc = dnf.util._Bootc()
|
||||||
|
+
|
||||||
|
+ if not bootc.is_unlocked():
|
||||||
|
if self.conf.persistence == "auto":
|
||||||
|
logger.info(_("This bootc system is configured to be read-only. Pass --transient to "
|
||||||
|
"perform this and subsequent transactions in a transient overlay which "
|
||||||
|
@@ -239,7 +243,6 @@ class BaseCli(dnf.Base):
|
||||||
|
logger.info(_("A transient overlay will be created on /usr that will be discarded on reboot. "
|
||||||
|
"Keep in mind that changes to /etc and /var will still persist, and packages "
|
||||||
|
"commonly modify these directories."))
|
||||||
|
- bootc_unlock_requested = True
|
||||||
|
elif self.conf.persistence == "transient":
|
||||||
|
raise CliError(_("Transient transactions are only supported on bootc systems."))
|
||||||
|
|
||||||
|
@@ -247,8 +250,8 @@ class BaseCli(dnf.Base):
|
||||||
|
if self.conf.assumeno or not self.output.userconfirm():
|
||||||
|
raise CliError(_("Operation aborted."))
|
||||||
|
|
||||||
|
- if bootc_unlock_requested:
|
||||||
|
- dnf.util._bootc_unlock()
|
||||||
|
+ if bootc:
|
||||||
|
+ bootc.unlock_and_prepare()
|
||||||
|
else:
|
||||||
|
logger.info(_('Nothing to do.'))
|
||||||
|
return
|
||||||
|
diff --git a/dnf/util.py b/dnf/util.py
|
||||||
|
index 2e270890c..51f853d8b 100644
|
||||||
|
--- a/dnf/util.py
|
||||||
|
+++ b/dnf/util.py
|
||||||
|
@@ -642,33 +642,67 @@ def _is_file_pattern_present(specs):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
-def _is_bootc_host():
|
||||||
|
- """Returns true is the system is managed as an immutable container, false
|
||||||
|
- otherwise."""
|
||||||
|
- ostree_booted = "/run/ostree-booted"
|
||||||
|
- return os.path.isfile(ostree_booted)
|
||||||
|
+class _Bootc:
|
||||||
|
+ usr = "/usr"
|
||||||
|
|
||||||
|
+ def __init__(self):
|
||||||
|
+ if not self.is_bootc_host():
|
||||||
|
+ raise RuntimeError(_("Not running on a bootc system."))
|
||||||
|
|
||||||
|
-def _is_bootc_unlocked():
|
||||||
|
- """Check whether /usr is writeable, e.g. if we are in a normal mutable
|
||||||
|
- system or if we are in a bootc after `bootc usr-overlay` or `ostree admin
|
||||||
|
- unlock` was run."""
|
||||||
|
- usr = "/usr"
|
||||||
|
- return os.access(usr, os.W_OK)
|
||||||
|
+ import gi
|
||||||
|
+ self._gi = gi
|
||||||
|
|
||||||
|
+ gi.require_version("OSTree", "1.0")
|
||||||
|
+ from gi.repository import OSTree
|
||||||
|
|
||||||
|
-def _bootc_unlock():
|
||||||
|
- """Set up a writeable overlay on bootc systems."""
|
||||||
|
+ self._OSTree = OSTree
|
||||||
|
|
||||||
|
- if _is_bootc_unlocked():
|
||||||
|
- return
|
||||||
|
+ self._sysroot = self._OSTree.Sysroot.new_default()
|
||||||
|
+ assert self._sysroot.load(None)
|
||||||
|
|
||||||
|
- unlock_command = ["bootc", "usr-overlay"]
|
||||||
|
+ self._booted_deployment = self._sysroot.require_booted_deployment()
|
||||||
|
+ assert self._booted_deployment is not None
|
||||||
|
|
||||||
|
- try:
|
||||||
|
- completed_process = subprocess.run(unlock_command, text=True)
|
||||||
|
- completed_process.check_returncode()
|
||||||
|
- except FileNotFoundError:
|
||||||
|
- raise dnf.exceptions.Error(_("bootc command not found. Is this a bootc system?"))
|
||||||
|
- except subprocess.CalledProcessError:
|
||||||
|
- raise dnf.exceptions.Error(_("Failed to unlock system: %s", completed_process.stderr))
|
||||||
|
+ @staticmethod
|
||||||
|
+ def is_bootc_host():
|
||||||
|
+ """Returns true is the system is managed as an immutable container, false
|
||||||
|
+ otherwise."""
|
||||||
|
+ ostree_booted = "/run/ostree-booted"
|
||||||
|
+ return os.path.isfile(ostree_booted)
|
||||||
|
+
|
||||||
|
+ def _get_unlocked_state(self):
|
||||||
|
+ return self._booted_deployment.get_unlocked()
|
||||||
|
+
|
||||||
|
+ def is_unlocked(self):
|
||||||
|
+ return self._get_unlocked_state() != self._OSTree.DeploymentUnlockedState.NONE
|
||||||
|
+
|
||||||
|
+ def unlock_and_prepare(self):
|
||||||
|
+ """Set up a writeable overlay on bootc systems."""
|
||||||
|
+
|
||||||
|
+ bootc_unlocked_state = self._get_unlocked_state()
|
||||||
|
+
|
||||||
|
+ valid_bootc_unlocked_states = (
|
||||||
|
+ self._OSTree.DeploymentUnlockedState.NONE,
|
||||||
|
+ self._OSTree.DeploymentUnlockedState.DEVELOPMENT,
|
||||||
|
+ # self._OSTree.DeploymentUnlockedState.TRANSIENT,
|
||||||
|
+ # self._OSTree.DeploymentUnlockedState.HOTFIX,
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ if bootc_unlocked_state not in valid_bootc_unlocked_states:
|
||||||
|
+ raise ValueError(_("Unhandled bootc unlocked state: %s") % bootc_unlocked_state.value_nick)
|
||||||
|
+
|
||||||
|
+ if bootc_unlocked_state == self._OSTree.DeploymentUnlockedState.DEVELOPMENT:
|
||||||
|
+ # System is already unlocked.
|
||||||
|
+ pass
|
||||||
|
+ elif bootc_unlocked_state == self._OSTree.DeploymentUnlockedState.NONE:
|
||||||
|
+ unlock_command = ["ostree", "admin", "unlock"]
|
||||||
|
+
|
||||||
|
+ try:
|
||||||
|
+ completed_process = subprocess.run(unlock_command, text=True)
|
||||||
|
+ completed_process.check_returncode()
|
||||||
|
+ except FileNotFoundError:
|
||||||
|
+ raise dnf.exceptions.Error(_("ostree command not found. Is this a bootc system?"))
|
||||||
|
+ except subprocess.CalledProcessError:
|
||||||
|
+ raise dnf.exceptions.Error(_("Failed to unlock system: %s", completed_process.stderr))
|
||||||
|
+
|
||||||
|
+ assert os.access(self.usr, os.W_OK)
|
||||||
|
--
|
||||||
|
2.48.1
|
||||||
|
|
237
0015-bootc-Re-locking-use-ostree-admin-unlock-transient.patch
Normal file
237
0015-bootc-Re-locking-use-ostree-admin-unlock-transient.patch
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
From 47cd04de2c099171002f2f474084e08eb5c7dddd Mon Sep 17 00:00:00 2001
|
||||||
|
From: Evan Goode <mail@evangoo.de>
|
||||||
|
Date: Thu, 16 Jan 2025 14:06:26 -0500
|
||||||
|
Subject: [PATCH 15/17] bootc: "Re-locking": use ostree admin unlock
|
||||||
|
--transient
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
Upstream commit: fa47a256ae7add2ce1c99ae8bedce7216001f396
|
||||||
|
|
||||||
|
To keep /usr read-only after DNF is finished with a transient
|
||||||
|
transaction, we call `ostree admin unlock --transient` to mount the /usr
|
||||||
|
overlay as read-only by default. Then, we create a private mount
|
||||||
|
namespace for DNF and its child processes and remount the /usr overlayfs
|
||||||
|
as read/write in the private mountns.
|
||||||
|
|
||||||
|
os.unshare is unfortunately only available in Python >= 3.12, so we have
|
||||||
|
to call libc.unshare via Python ctypes here and hardcode the CLONE_NEWNS
|
||||||
|
flag that we need to pass.
|
||||||
|
|
||||||
|
Resolves: https://issues.redhat.com/browse/RHEL-76849
|
||||||
|
Signed-off-by: Petr Písař <ppisar@redhat.com>
|
||||||
|
---
|
||||||
|
dnf/cli/cli.py | 33 ++++++++++---------
|
||||||
|
dnf/util.py | 86 ++++++++++++++++++++++++++++++++++++++------------
|
||||||
|
2 files changed, 83 insertions(+), 36 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/dnf/cli/cli.py b/dnf/cli/cli.py
|
||||||
|
index bc11505fc..e2700c092 100644
|
||||||
|
--- a/dnf/cli/cli.py
|
||||||
|
+++ b/dnf/cli/cli.py
|
||||||
|
@@ -205,8 +205,6 @@ class BaseCli(dnf.Base):
|
||||||
|
else:
|
||||||
|
self.output.reportDownloadSize(install_pkgs, install_only)
|
||||||
|
|
||||||
|
- bootc_unlock_requested = False
|
||||||
|
-
|
||||||
|
if trans or self._moduleContainer.isChanged() or \
|
||||||
|
(self._history and (self._history.group or self._history.env)):
|
||||||
|
# confirm with user
|
||||||
|
@@ -218,40 +216,45 @@ class BaseCli(dnf.Base):
|
||||||
|
logger.info(_("{prog} will only download packages, install gpg keys, and check the "
|
||||||
|
"transaction.").format(prog=dnf.util.MAIN_PROG_UPPER))
|
||||||
|
|
||||||
|
- is_bootc_transaction = dnf.util._Bootc.is_bootc_host() and \
|
||||||
|
+ is_bootc_transaction = dnf.util._BootcSystem.is_bootc_system() and \
|
||||||
|
os.path.realpath(self.conf.installroot) == "/" and \
|
||||||
|
not self.conf.downloadonly
|
||||||
|
|
||||||
|
# Handle bootc transactions. `--transient` must be specified if
|
||||||
|
# /usr is not already writeable.
|
||||||
|
- bootc = None
|
||||||
|
+ bootc_system = None
|
||||||
|
if is_bootc_transaction:
|
||||||
|
if self.conf.persistence == "persist":
|
||||||
|
logger.info(_("Persistent transactions aren't supported on bootc systems."))
|
||||||
|
raise CliError(_("Operation aborted."))
|
||||||
|
assert self.conf.persistence in ("auto", "transient")
|
||||||
|
|
||||||
|
- bootc = dnf.util._Bootc()
|
||||||
|
+ bootc_system = dnf.util._BootcSystem()
|
||||||
|
|
||||||
|
- if not bootc.is_unlocked():
|
||||||
|
+ if not bootc_system.is_writable():
|
||||||
|
if self.conf.persistence == "auto":
|
||||||
|
logger.info(_("This bootc system is configured to be read-only. Pass --transient to "
|
||||||
|
- "perform this and subsequent transactions in a transient overlay which "
|
||||||
|
- "will reset when the system reboots."))
|
||||||
|
+ "perform this transaction in a transient overlay which will reset when "
|
||||||
|
+ "the system reboots."))
|
||||||
|
raise CliError(_("Operation aborted."))
|
||||||
|
assert self.conf.persistence == "transient"
|
||||||
|
- logger.info(_("A transient overlay will be created on /usr that will be discarded on reboot. "
|
||||||
|
- "Keep in mind that changes to /etc and /var will still persist, and packages "
|
||||||
|
- "commonly modify these directories."))
|
||||||
|
- elif self.conf.persistence == "transient":
|
||||||
|
- raise CliError(_("Transient transactions are only supported on bootc systems."))
|
||||||
|
+ if not bootc_system.is_unlocked_transient():
|
||||||
|
+ # Only tell the user about the transient overlay if
|
||||||
|
+ # it's not already in place
|
||||||
|
+ logger.info(_("A transient overlay will be created on /usr that will be discarded on reboot. "
|
||||||
|
+ "Keep in mind that changes to /etc and /var will still persist, and packages "
|
||||||
|
+ "commonly modify these directories."))
|
||||||
|
+ else:
|
||||||
|
+ # Not a bootc transaction.
|
||||||
|
+ if self.conf.persistence == "transient":
|
||||||
|
+ raise CliError(_("Transient transactions are only supported on bootc systems."))
|
||||||
|
|
||||||
|
if self._promptWanted():
|
||||||
|
if self.conf.assumeno or not self.output.userconfirm():
|
||||||
|
raise CliError(_("Operation aborted."))
|
||||||
|
|
||||||
|
- if bootc:
|
||||||
|
- bootc.unlock_and_prepare()
|
||||||
|
+ if bootc_system:
|
||||||
|
+ bootc_system.make_writable()
|
||||||
|
else:
|
||||||
|
logger.info(_('Nothing to do.'))
|
||||||
|
return
|
||||||
|
diff --git a/dnf/util.py b/dnf/util.py
|
||||||
|
index 51f853d8b..eb987bb8a 100644
|
||||||
|
--- a/dnf/util.py
|
||||||
|
+++ b/dnf/util.py
|
||||||
|
@@ -25,6 +25,7 @@ from __future__ import unicode_literals
|
||||||
|
from .pycomp import PY3, basestring
|
||||||
|
from dnf.i18n import _, ucd
|
||||||
|
import argparse
|
||||||
|
+import ctypes
|
||||||
|
import dnf
|
||||||
|
import dnf.callback
|
||||||
|
import dnf.const
|
||||||
|
@@ -642,11 +643,12 @@ def _is_file_pattern_present(specs):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
-class _Bootc:
|
||||||
|
+class _BootcSystem:
|
||||||
|
usr = "/usr"
|
||||||
|
+ CLONE_NEWNS = 0x00020000 # defined in linux/include/uapi/linux/sched.h
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
- if not self.is_bootc_host():
|
||||||
|
+ if not self.is_bootc_system():
|
||||||
|
raise RuntimeError(_("Not running on a bootc system."))
|
||||||
|
|
||||||
|
import gi
|
||||||
|
@@ -664,45 +666,87 @@ class _Bootc:
|
||||||
|
assert self._booted_deployment is not None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
- def is_bootc_host():
|
||||||
|
+ def is_bootc_system():
|
||||||
|
"""Returns true is the system is managed as an immutable container, false
|
||||||
|
otherwise."""
|
||||||
|
ostree_booted = "/run/ostree-booted"
|
||||||
|
return os.path.isfile(ostree_booted)
|
||||||
|
|
||||||
|
+ @classmethod
|
||||||
|
+ def is_writable(cls):
|
||||||
|
+ """Returns true if and only if /usr is writable."""
|
||||||
|
+ return os.access(cls.usr, os.W_OK)
|
||||||
|
+
|
||||||
|
def _get_unlocked_state(self):
|
||||||
|
return self._booted_deployment.get_unlocked()
|
||||||
|
|
||||||
|
- def is_unlocked(self):
|
||||||
|
- return self._get_unlocked_state() != self._OSTree.DeploymentUnlockedState.NONE
|
||||||
|
+ def is_unlocked_transient(self):
|
||||||
|
+ """Returns true if and only if the bootc system is unlocked in a
|
||||||
|
+ transient state, i.e. a overlayfs is mounted as read-only on /usr.
|
||||||
|
+ Changes can be made to the overlayfs by remounting /usr as
|
||||||
|
+ read/write in a private mount namespace."""
|
||||||
|
+ return self._get_unlocked_state() == self._OSTree.DeploymentUnlockedState.TRANSIENT
|
||||||
|
+
|
||||||
|
+ @classmethod
|
||||||
|
+ def _set_up_mountns(cls):
|
||||||
|
+ # os.unshare is only available in Python >= 3.12.
|
||||||
|
+
|
||||||
|
+ # Access symbols in libraries loaded by the Python interpreter,
|
||||||
|
+ # which will include libc. See https://bugs.python.org/issue34592.
|
||||||
|
+ libc = ctypes.CDLL(None)
|
||||||
|
+ if libc.unshare(cls.CLONE_NEWNS) != 0:
|
||||||
|
+ raise OSError("Failed to unshare mount namespace")
|
||||||
|
+
|
||||||
|
+ mount_command = ["mount", "--options-source=disable", "-o", "remount,rw", cls.usr]
|
||||||
|
+ try:
|
||||||
|
+ completed_process = subprocess.run(mount_command, text=True)
|
||||||
|
+ completed_process.check_returncode()
|
||||||
|
+ except FileNotFoundError:
|
||||||
|
+ raise dnf.exceptions.Error(_("%s: command not found.") % mount_command[0])
|
||||||
|
+ except subprocess.CalledProcessError:
|
||||||
|
+ raise dnf.exceptions.Error(_("Failed to mount %s as read/write: %s", cls.usr, completed_process.stderr))
|
||||||
|
|
||||||
|
- def unlock_and_prepare(self):
|
||||||
|
- """Set up a writeable overlay on bootc systems."""
|
||||||
|
+ @staticmethod
|
||||||
|
+ def _unlock():
|
||||||
|
+ unlock_command = ["ostree", "admin", "unlock", "--transient"]
|
||||||
|
+ try:
|
||||||
|
+ completed_process = subprocess.run(unlock_command, text=True)
|
||||||
|
+ completed_process.check_returncode()
|
||||||
|
+ except FileNotFoundError:
|
||||||
|
+ raise dnf.exceptions.Error(_("%s: command not found. Is this a bootc system?") % unlock_command[0])
|
||||||
|
+ except subprocess.CalledProcessError:
|
||||||
|
+ raise dnf.exceptions.Error(_("Failed to unlock system: %s", completed_process.stderr))
|
||||||
|
+
|
||||||
|
+ def make_writable(self):
|
||||||
|
+ """Set up a writable overlay on bootc systems."""
|
||||||
|
|
||||||
|
bootc_unlocked_state = self._get_unlocked_state()
|
||||||
|
|
||||||
|
valid_bootc_unlocked_states = (
|
||||||
|
self._OSTree.DeploymentUnlockedState.NONE,
|
||||||
|
self._OSTree.DeploymentUnlockedState.DEVELOPMENT,
|
||||||
|
- # self._OSTree.DeploymentUnlockedState.TRANSIENT,
|
||||||
|
- # self._OSTree.DeploymentUnlockedState.HOTFIX,
|
||||||
|
+ self._OSTree.DeploymentUnlockedState.TRANSIENT,
|
||||||
|
+ self._OSTree.DeploymentUnlockedState.HOTFIX,
|
||||||
|
)
|
||||||
|
-
|
||||||
|
if bootc_unlocked_state not in valid_bootc_unlocked_states:
|
||||||
|
raise ValueError(_("Unhandled bootc unlocked state: %s") % bootc_unlocked_state.value_nick)
|
||||||
|
|
||||||
|
- if bootc_unlocked_state == self._OSTree.DeploymentUnlockedState.DEVELOPMENT:
|
||||||
|
- # System is already unlocked.
|
||||||
|
+ writable_unlocked_states = (
|
||||||
|
+ self._OSTree.DeploymentUnlockedState.DEVELOPMENT,
|
||||||
|
+ self._OSTree.DeploymentUnlockedState.HOTFIX,
|
||||||
|
+ )
|
||||||
|
+ if bootc_unlocked_state in writable_unlocked_states:
|
||||||
|
+ # System is already unlocked in development mode, and usr is
|
||||||
|
+ # already mounted read/write.
|
||||||
|
pass
|
||||||
|
elif bootc_unlocked_state == self._OSTree.DeploymentUnlockedState.NONE:
|
||||||
|
- unlock_command = ["ostree", "admin", "unlock"]
|
||||||
|
-
|
||||||
|
- try:
|
||||||
|
- completed_process = subprocess.run(unlock_command, text=True)
|
||||||
|
- completed_process.check_returncode()
|
||||||
|
- except FileNotFoundError:
|
||||||
|
- raise dnf.exceptions.Error(_("ostree command not found. Is this a bootc system?"))
|
||||||
|
- except subprocess.CalledProcessError:
|
||||||
|
- raise dnf.exceptions.Error(_("Failed to unlock system: %s", completed_process.stderr))
|
||||||
|
+ # System is not unlocked. Unlock it in transient mode, then set up
|
||||||
|
+ # a mount namespace for DNF.
|
||||||
|
+ self._unlock()
|
||||||
|
+ self._set_up_mountns()
|
||||||
|
+ elif bootc_unlocked_state == self._OSTree.DeploymentUnlockedState.TRANSIENT:
|
||||||
|
+ # System is unlocked in transient mode, so usr is mounted
|
||||||
|
+ # read-only. Set up a mount namespace for DNF.
|
||||||
|
+ self._set_up_mountns()
|
||||||
|
|
||||||
|
assert os.access(self.usr, os.W_OK)
|
||||||
|
--
|
||||||
|
2.48.1
|
||||||
|
|
56
0016-spec-Add-dnf-bootc-subpackage.patch
Normal file
56
0016-spec-Add-dnf-bootc-subpackage.patch
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
From dc461487b3a1323d96183e5c229a307d274a3ecc Mon Sep 17 00:00:00 2001
|
||||||
|
From: Evan Goode <mail@evangoo.de>
|
||||||
|
Date: Tue, 28 Jan 2025 11:27:00 -0500
|
||||||
|
Subject: [PATCH 16/17] spec: Add dnf-bootc subpackage
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
Upstream commit: 76a0c339eb172b1b2a4b1aa8b4db8d6a5145916b
|
||||||
|
|
||||||
|
dnf-bootc's only job is to Require python3-gobject-base, ostree,
|
||||||
|
ostree-libs, and util-linux-core, which are needed to interact with
|
||||||
|
bootc systems. We don't want to add these dependencies on `python3-dnf`
|
||||||
|
because we don't want them on non-bootc systems, so we use a subpackage.
|
||||||
|
|
||||||
|
Resolves: https://issues.redhat.com/browse/RHEL-76849
|
||||||
|
Signed-off-by: Petr Písař <ppisar@redhat.com>
|
||||||
|
---
|
||||||
|
dnf.spec | 14 ++++++++++++++
|
||||||
|
1 file changed, 14 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/dnf.spec b/dnf.spec
|
||||||
|
index 98b6be42c..c749ab41e 100644
|
||||||
|
--- a/dnf.spec
|
||||||
|
+++ b/dnf.spec
|
||||||
|
@@ -191,6 +191,17 @@ Requires: python3-%{name} = %{version}-%{release}
|
||||||
|
%description automatic
|
||||||
|
Systemd units that can periodically download package upgrades and apply them.
|
||||||
|
|
||||||
|
+%package bootc
|
||||||
|
+Summary: %{pkg_summary} - additional bootc dependencies
|
||||||
|
+Requires: python3-%{name} = %{version}-%{release}
|
||||||
|
+Requires: ostree
|
||||||
|
+Requires: ostree-libs
|
||||||
|
+Requires: python3-gobject-base
|
||||||
|
+Requires: util-linux-core
|
||||||
|
+
|
||||||
|
+%description bootc
|
||||||
|
+Additional dependencies needed to perform transactions on booted bootc (bootable containers) systems.
|
||||||
|
+
|
||||||
|
|
||||||
|
%prep
|
||||||
|
%autosetup
|
||||||
|
@@ -413,6 +424,9 @@ popd
|
||||||
|
%{_unitdir}/%{name}-automatic-install.timer
|
||||||
|
%{python3_sitelib}/%{name}/automatic/
|
||||||
|
|
||||||
|
+%files bootc
|
||||||
|
+# bootc subpackage does not include any files
|
||||||
|
+
|
||||||
|
%changelog
|
||||||
|
* Wed Apr 24 2024 Jan Kolarik <jkolarik@redhat.com> - 4.20.0-1
|
||||||
|
- repoquery: Fix loading filelists when -f is used (RhBug:2276012)
|
||||||
|
--
|
||||||
|
2.48.1
|
||||||
|
|
45
0017-Require-libdnf-0.74.0-with-persistence-option.patch
Normal file
45
0017-Require-libdnf-0.74.0-with-persistence-option.patch
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
From 7ffd97532e120f4391792b1bdfa0dbe1510409a7 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Evan Goode <mail@evangoo.de>
|
||||||
|
Date: Wed, 5 Feb 2025 10:35:08 -0500
|
||||||
|
Subject: [PATCH 17/17] Require libdnf >= 0.74.0 with `persistence` option
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
Upstream commit: 5a4f6c42e61ed764ff85eea69125947a280d665d
|
||||||
|
|
||||||
|
This backport actually uses RHEL 10 version of libdnf.
|
||||||
|
|
||||||
|
Resolves: https://issues.redhat.com/browse/RHEL-76849
|
||||||
|
Signed-off-by: Petr Písař <ppisar@redhat.com>
|
||||||
|
---
|
||||||
|
dnf.spec | 6 +++++-
|
||||||
|
1 file changed, 5 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/dnf.spec b/dnf.spec
|
||||||
|
index c749ab41e..66243c858 100644
|
||||||
|
--- a/dnf.spec
|
||||||
|
+++ b/dnf.spec
|
||||||
|
@@ -2,7 +2,7 @@
|
||||||
|
%define __cmake_in_source_build 1
|
||||||
|
|
||||||
|
# default dependencies
|
||||||
|
-%global hawkey_version 0.73.1
|
||||||
|
+%global hawkey_version 0.74.0
|
||||||
|
%global libcomps_version 0.1.8
|
||||||
|
%global libmodulemd_version 2.9.3
|
||||||
|
%global rpm_version 4.14.0
|
||||||
|
@@ -23,6 +23,10 @@
|
||||||
|
%global rpm_version 4.11.3-25.el7.centos.1
|
||||||
|
%endif
|
||||||
|
|
||||||
|
+%if 0%{?rhel} == 10
|
||||||
|
+ %global hawkey_version 0.73.1-7
|
||||||
|
+%endif
|
||||||
|
+
|
||||||
|
# override dependencies for fedora 26
|
||||||
|
%if 0%{?fedora} == 26
|
||||||
|
%global rpm_version 4.13.0.1-7
|
||||||
|
--
|
||||||
|
2.48.1
|
||||||
|
|
162
0018-Derive-releasever_-major-minor-in-conf-not-substitut.patch
Normal file
162
0018-Derive-releasever_-major-minor-in-conf-not-substitut.patch
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
From ad24340a6ccf06a4c948824f394a0de5b6a7826d Mon Sep 17 00:00:00 2001
|
||||||
|
From: Evan Goode <mail@evangoo.de>
|
||||||
|
Date: Mon, 20 Jan 2025 21:36:18 +0000
|
||||||
|
Subject: [PATCH] Derive releasever_{major,minor} in conf, not substitutions
|
||||||
|
|
||||||
|
Upstream commit: 0e283fb3b0b06f1d31c6d3bd9c14c5fa89bf64a4
|
||||||
|
|
||||||
|
This allows setting a releasever_major or releasever_minor
|
||||||
|
independent of releasever, which is needed by EPEL.
|
||||||
|
|
||||||
|
Related: https://issues.redhat.com/browse/RHEL-68034
|
||||||
|
---
|
||||||
|
dnf/conf/config.py | 26 ++++++++++++++++++++++++++
|
||||||
|
dnf/conf/substitutions.py | 17 +++--------------
|
||||||
|
tests/conf/test_substitutions.py | 19 +++++++++----------
|
||||||
|
tests/test_config.py | 16 ++++++++++++++++
|
||||||
|
4 files changed, 54 insertions(+), 24 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/dnf/conf/config.py b/dnf/conf/config.py
|
||||||
|
index 5210ffba..6cf28724 100644
|
||||||
|
--- a/dnf/conf/config.py
|
||||||
|
+++ b/dnf/conf/config.py
|
||||||
|
@@ -430,6 +430,32 @@ class MainConf(BaseConfig):
|
||||||
|
return
|
||||||
|
self.substitutions['releasever'] = str(val)
|
||||||
|
|
||||||
|
+ @property
|
||||||
|
+ def releasever_major(self):
|
||||||
|
+ # :api
|
||||||
|
+ return self.substitutions.get('releasever_major')
|
||||||
|
+
|
||||||
|
+ @releasever_major.setter
|
||||||
|
+ def releasever_major(self, val):
|
||||||
|
+ # :api
|
||||||
|
+ if val is None:
|
||||||
|
+ self.substitutions.pop('releasever_major', None)
|
||||||
|
+ return
|
||||||
|
+ self.substitutions['releasever_major'] = str(val)
|
||||||
|
+
|
||||||
|
+ @property
|
||||||
|
+ def releasever_minor(self):
|
||||||
|
+ # :api
|
||||||
|
+ return self.substitutions.get('releasever_minor')
|
||||||
|
+
|
||||||
|
+ @releasever_minor.setter
|
||||||
|
+ def releasever_minor(self, val):
|
||||||
|
+ # :api
|
||||||
|
+ if val is None:
|
||||||
|
+ self.substitutions.pop('releasever_minor', None)
|
||||||
|
+ return
|
||||||
|
+ self.substitutions['releasever_minor'] = str(val)
|
||||||
|
+
|
||||||
|
@property
|
||||||
|
def arch(self):
|
||||||
|
# :api
|
||||||
|
diff --git a/dnf/conf/substitutions.py b/dnf/conf/substitutions.py
|
||||||
|
index 5c736a8d..8582d5d8 100644
|
||||||
|
--- a/dnf/conf/substitutions.py
|
||||||
|
+++ b/dnf/conf/substitutions.py
|
||||||
|
@@ -22,11 +22,12 @@ import logging
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
|
+from libdnf.conf import ConfigParser
|
||||||
|
from dnf.i18n import _
|
||||||
|
from dnf.exceptions import ReadOnlyVariableError
|
||||||
|
|
||||||
|
ENVIRONMENT_VARS_RE = re.compile(r'^DNF_VAR_[A-Za-z0-9_]+$')
|
||||||
|
-READ_ONLY_VARIABLES = frozenset(("releasever_major", "releasever_minor"))
|
||||||
|
+READ_ONLY_VARIABLES = frozenset()
|
||||||
|
logger = logging.getLogger('dnf')
|
||||||
|
|
||||||
|
|
||||||
|
@@ -45,18 +46,6 @@ class Substitutions(dict):
|
||||||
|
elif key in numericvars:
|
||||||
|
self[key] = val
|
||||||
|
|
||||||
|
- @staticmethod
|
||||||
|
- def _split_releasever(releasever):
|
||||||
|
- # type: (str) -> tuple[str, str]
|
||||||
|
- pos = releasever.find(".")
|
||||||
|
- if pos == -1:
|
||||||
|
- releasever_major = releasever
|
||||||
|
- releasever_minor = ""
|
||||||
|
- else:
|
||||||
|
- releasever_major = releasever[:pos]
|
||||||
|
- releasever_minor = releasever[pos + 1:]
|
||||||
|
- return releasever_major, releasever_minor
|
||||||
|
-
|
||||||
|
def __setitem__(self, key, value):
|
||||||
|
if Substitutions.is_read_only(key):
|
||||||
|
raise ReadOnlyVariableError(f"Variable \"{key}\" is read-only", variable_name=key)
|
||||||
|
@@ -65,7 +54,7 @@ class Substitutions(dict):
|
||||||
|
setitem(key, value)
|
||||||
|
|
||||||
|
if key == "releasever" and value:
|
||||||
|
- releasever_major, releasever_minor = Substitutions._split_releasever(value)
|
||||||
|
+ releasever_major, releasever_minor = ConfigParser.splitReleasever(value)
|
||||||
|
setitem("releasever_major", releasever_major)
|
||||||
|
setitem("releasever_minor", releasever_minor)
|
||||||
|
|
||||||
|
diff --git a/tests/conf/test_substitutions.py b/tests/conf/test_substitutions.py
|
||||||
|
index d8ac3c20..78d3e727 100644
|
||||||
|
--- a/tests/conf/test_substitutions.py
|
||||||
|
+++ b/tests/conf/test_substitutions.py
|
||||||
|
@@ -56,16 +56,6 @@ class SubstitutionsFromEnvironmentTest(tests.support.TestCase):
|
||||||
|
self.assertEqual('opera', conf.substitutions['GENRE'])
|
||||||
|
|
||||||
|
|
||||||
|
-class SubstitutionsReadOnlyTest(tests.support.TestCase):
|
||||||
|
- def test_set_readonly(self):
|
||||||
|
- conf = dnf.conf.Conf()
|
||||||
|
- variable_name = "releasever_major"
|
||||||
|
- self.assertTrue(Substitutions.is_read_only(variable_name))
|
||||||
|
- with self.assertRaises(ReadOnlyVariableError) as cm:
|
||||||
|
- conf.substitutions["releasever_major"] = "1"
|
||||||
|
- self.assertEqual(cm.exception.variable_name, "releasever_major")
|
||||||
|
-
|
||||||
|
-
|
||||||
|
class SubstitutionsReleaseverTest(tests.support.TestCase):
|
||||||
|
def test_releasever_simple(self):
|
||||||
|
conf = dnf.conf.Conf()
|
||||||
|
@@ -84,3 +74,12 @@ class SubstitutionsReleaseverTest(tests.support.TestCase):
|
||||||
|
conf.substitutions["releasever"] = "1.23.45"
|
||||||
|
self.assertEqual(conf.substitutions["releasever_major"], "1")
|
||||||
|
self.assertEqual(conf.substitutions["releasever_minor"], "23.45")
|
||||||
|
+
|
||||||
|
+ def test_releasever_major_minor_overrides(self):
|
||||||
|
+ conf = dnf.conf.Conf()
|
||||||
|
+ conf.substitutions["releasever"] = "1.23"
|
||||||
|
+ conf.substitutions["releasever_major"] = "45"
|
||||||
|
+ conf.substitutions["releasever_minor"] = "67"
|
||||||
|
+ self.assertEqual(conf.substitutions["releasever"], "1.23")
|
||||||
|
+ self.assertEqual(conf.substitutions["releasever_major"], "45")
|
||||||
|
+ self.assertEqual(conf.substitutions["releasever_minor"], "67")
|
||||||
|
diff --git a/tests/test_config.py b/tests/test_config.py
|
||||||
|
index d8502670..16bdcccb 100644
|
||||||
|
--- a/tests/test_config.py
|
||||||
|
+++ b/tests/test_config.py
|
||||||
|
@@ -145,3 +145,19 @@ class ConfTest(tests.support.TestCase):
|
||||||
|
conf = Conf()
|
||||||
|
with self.assertRaises(dnf.exceptions.ConfigError):
|
||||||
|
conf.debuglevel = '11'
|
||||||
|
+
|
||||||
|
+ def test_releasever_major_minor(self):
|
||||||
|
+ conf = Conf()
|
||||||
|
+ conf.releasever = '1.2'
|
||||||
|
+ self.assertEqual(conf.releasever_major, '1')
|
||||||
|
+ self.assertEqual(conf.releasever_minor, '2')
|
||||||
|
+
|
||||||
|
+ # override releasever_major
|
||||||
|
+ conf.releasever_major = '3'
|
||||||
|
+ self.assertEqual(conf.releasever_major, '3')
|
||||||
|
+ self.assertEqual(conf.releasever_minor, '2')
|
||||||
|
+
|
||||||
|
+ # override releasever_minor
|
||||||
|
+ conf.releasever_minor = '4'
|
||||||
|
+ self.assertEqual(conf.releasever_major, '3')
|
||||||
|
+ self.assertEqual(conf.releasever_minor, '4')
|
||||||
|
--
|
||||||
|
2.48.1
|
||||||
|
|
179
0019-Override-releasever_-major-minor-with-provides.patch
Normal file
179
0019-Override-releasever_-major-minor-with-provides.patch
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
From 66fb0b5ec8ea3d3165e6b2f38e1a01f692d1ec93 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Evan Goode <mail@evangoo.de>
|
||||||
|
Date: Tue, 21 Jan 2025 19:16:13 +0000
|
||||||
|
Subject: [PATCH] Override releasever_{major,minor} with provides
|
||||||
|
|
||||||
|
Upstream commit: 75e3ff0c43a5d605aa43d6ddc2d9e2ef89942999
|
||||||
|
|
||||||
|
The releasever_major and releasever_minor substitution variables are
|
||||||
|
usually derived by splitting releasever on the first `.`. However, to
|
||||||
|
support EPEL 10 [1], we would like a way for distributions to override these
|
||||||
|
values. Specifically, we would like RHEL 10 to have a releasever of `10`
|
||||||
|
with a releasever_major of `10` and a releasever_minor of `0` (later
|
||||||
|
incrementing to `1`, `2`, to correspond with the RHEL minor version).
|
||||||
|
|
||||||
|
This commit adds a new API function, `detect_releasevers`, which derives
|
||||||
|
releasever, releasever_major, and releasever_minor from virtual provides
|
||||||
|
on the system-release package (any of `DISTROVERPKG`). The detection of
|
||||||
|
releasever is unchanged. releasever_major and releasever_minor are
|
||||||
|
specified by the versions of the `system-release-major` and
|
||||||
|
`system-release-minor` provides, respectively.
|
||||||
|
|
||||||
|
If the user specifies a `--releasever=X.Y` on the command line, the
|
||||||
|
distribution settings for releasever, releasever_major, and releasever_minor
|
||||||
|
will all be overridden: releasever will be set to X.Y, releasever_major will be
|
||||||
|
set to X, and releasever_minor will be set to Y, same as before. If a user
|
||||||
|
wants to specify a custom releasever_major and releasever_minor, they have to
|
||||||
|
set all three with `--setopt=releasever=X --setopt=releasever_major=Y
|
||||||
|
--setopt=releasever_minor=z`, taking care to put `releasever_major` and
|
||||||
|
`releasever_minor` after `releasever` so they are not overridden.
|
||||||
|
|
||||||
|
[1] https://issues.redhat.com/browse/RHEL-68034
|
||||||
|
---
|
||||||
|
dnf/base.py | 10 ++++++++--
|
||||||
|
dnf/cli/cli.py | 11 +++++++++--
|
||||||
|
dnf/const.py.in | 2 ++
|
||||||
|
dnf/rpm/__init__.py | 43 +++++++++++++++++++++++++++++++++++++++----
|
||||||
|
4 files changed, 58 insertions(+), 8 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/dnf/base.py b/dnf/base.py
|
||||||
|
index dac3cefd..c4a4f854 100644
|
||||||
|
--- a/dnf/base.py
|
||||||
|
+++ b/dnf/base.py
|
||||||
|
@@ -157,8 +157,14 @@ class Base(object):
|
||||||
|
conf = dnf.conf.Conf()
|
||||||
|
subst = conf.substitutions
|
||||||
|
if 'releasever' not in subst:
|
||||||
|
- subst['releasever'] = \
|
||||||
|
- dnf.rpm.detect_releasever(conf.installroot)
|
||||||
|
+ releasever, major, minor = \
|
||||||
|
+ dnf.rpm.detect_releasevers(conf.installroot)
|
||||||
|
+ subst['releasever'] = releasever
|
||||||
|
+ if major is not None:
|
||||||
|
+ subst['releasever_major'] = major
|
||||||
|
+ if minor is not None:
|
||||||
|
+ subst['releasever_minor'] = minor
|
||||||
|
+
|
||||||
|
return conf
|
||||||
|
|
||||||
|
def _setup_modular_excludes(self):
|
||||||
|
diff --git a/dnf/cli/cli.py b/dnf/cli/cli.py
|
||||||
|
index e2700c09..118ce4e7 100644
|
||||||
|
--- a/dnf/cli/cli.py
|
||||||
|
+++ b/dnf/cli/cli.py
|
||||||
|
@@ -971,13 +971,20 @@ class Cli(object):
|
||||||
|
from_root = "/"
|
||||||
|
subst = conf.substitutions
|
||||||
|
subst.update_from_etc(from_root, varsdir=conf._get_value('varsdir'))
|
||||||
|
+
|
||||||
|
# cachedir, logs, releasever, and gpgkey are taken from or stored in installroot
|
||||||
|
+ major = None
|
||||||
|
+ minor = None
|
||||||
|
if releasever is None and conf.releasever is None:
|
||||||
|
- releasever = dnf.rpm.detect_releasever(conf.installroot)
|
||||||
|
+ releasever, major, minor = dnf.rpm.detect_releasevers(conf.installroot)
|
||||||
|
elif releasever == '/':
|
||||||
|
- releasever = dnf.rpm.detect_releasever(releasever)
|
||||||
|
+ releasever, major, minor = dnf.rpm.detect_releasevers(releasever)
|
||||||
|
if releasever is not None:
|
||||||
|
conf.releasever = releasever
|
||||||
|
+ if major is not None:
|
||||||
|
+ conf.releasever_major = major
|
||||||
|
+ if minor is not None:
|
||||||
|
+ conf.releasever_minor = minor
|
||||||
|
if conf.releasever is None:
|
||||||
|
logger.warning(_("Unable to detect release version (use '--releasever' to specify "
|
||||||
|
"release version)"))
|
||||||
|
diff --git a/dnf/const.py.in b/dnf/const.py.in
|
||||||
|
index bcadc804..07aab7a4 100644
|
||||||
|
--- a/dnf/const.py.in
|
||||||
|
+++ b/dnf/const.py.in
|
||||||
|
@@ -25,6 +25,8 @@ CONF_AUTOMATIC_FILENAME='/etc/dnf/automatic.conf'
|
||||||
|
DISTROVERPKG=('system-release(releasever)', 'system-release',
|
||||||
|
'distribution-release(releasever)', 'distribution-release',
|
||||||
|
'redhat-release', 'suse-release')
|
||||||
|
+DISTROVER_MAJOR_PKG='system-release(releasever_major)'
|
||||||
|
+DISTROVER_MINOR_PKG='system-release(releasever_minor)'
|
||||||
|
GROUP_PACKAGE_TYPES = ('mandatory', 'default', 'conditional') # :api
|
||||||
|
INSTALLONLYPKGS=['kernel', 'kernel-PAE',
|
||||||
|
'installonlypkg(kernel)',
|
||||||
|
diff --git a/dnf/rpm/__init__.py b/dnf/rpm/__init__.py
|
||||||
|
index 12efca7f..d4be4d03 100644
|
||||||
|
--- a/dnf/rpm/__init__.py
|
||||||
|
+++ b/dnf/rpm/__init__.py
|
||||||
|
@@ -26,12 +26,21 @@ import dnf.exceptions
|
||||||
|
import rpm # used by ansible (dnf.rpm.rpm.labelCompare in lib/ansible/modules/packaging/os/dnf.py)
|
||||||
|
|
||||||
|
|
||||||
|
-def detect_releasever(installroot):
|
||||||
|
+def detect_releasevers(installroot):
|
||||||
|
# :api
|
||||||
|
- """Calculate the release version for the system."""
|
||||||
|
+ """Calculate the release version for the system, including releasever_major
|
||||||
|
+ and releasever_minor if they are overriden by the system-release-major or
|
||||||
|
+ system-release-minor provides."""
|
||||||
|
|
||||||
|
ts = transaction.initReadOnlyTransaction(root=installroot)
|
||||||
|
ts.pushVSFlags(~(rpm._RPMVSF_NOSIGNATURES | rpm._RPMVSF_NODIGESTS))
|
||||||
|
+
|
||||||
|
+ distrover_major_pkg = dnf.const.DISTROVER_MAJOR_PKG
|
||||||
|
+ distrover_minor_pkg = dnf.const.DISTROVER_MINOR_PKG
|
||||||
|
+ if dnf.pycomp.PY3:
|
||||||
|
+ distrover_major_pkg = bytes(distrover_major_pkg, 'utf-8')
|
||||||
|
+ distrover_minor_pkg = bytes(distrover_minor_pkg, 'utf-8')
|
||||||
|
+
|
||||||
|
for distroverpkg in dnf.const.DISTROVERPKG:
|
||||||
|
if dnf.pycomp.PY3:
|
||||||
|
distroverpkg = bytes(distroverpkg, 'utf-8')
|
||||||
|
@@ -47,6 +56,8 @@ def detect_releasever(installroot):
|
||||||
|
msg = 'Error: rpmdb failed to list provides. Try: rpm --rebuilddb'
|
||||||
|
raise dnf.exceptions.Error(msg)
|
||||||
|
releasever = hdr['version']
|
||||||
|
+ releasever_major = None
|
||||||
|
+ releasever_minor = None
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
# header returns bytes -> look for bytes
|
||||||
|
@@ -61,13 +72,37 @@ def detect_releasever(installroot):
|
||||||
|
if hdr['name'] not in (distroverpkg, distroverpkg.decode("utf8")):
|
||||||
|
# override the package version
|
||||||
|
releasever = ver
|
||||||
|
+
|
||||||
|
+ for provide, flag, ver in zip(
|
||||||
|
+ hdr[rpm.RPMTAG_PROVIDENAME],
|
||||||
|
+ hdr[rpm.RPMTAG_PROVIDEFLAGS],
|
||||||
|
+ hdr[rpm.RPMTAG_PROVIDEVERSION]):
|
||||||
|
+ if isinstance(provide, str):
|
||||||
|
+ provide = bytes(provide, "utf-8")
|
||||||
|
+ if provide == distrover_major_pkg and flag == rpm.RPMSENSE_EQUAL and ver:
|
||||||
|
+ releasever_major = ver
|
||||||
|
+ if provide == distrover_minor_pkg and flag == rpm.RPMSENSE_EQUAL and ver:
|
||||||
|
+ releasever_minor = ver
|
||||||
|
+
|
||||||
|
except (ValueError, KeyError, IndexError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
if is_py3bytes(releasever):
|
||||||
|
releasever = str(releasever, "utf-8")
|
||||||
|
- return releasever
|
||||||
|
- return None
|
||||||
|
+ if is_py3bytes(releasever_major):
|
||||||
|
+ releasever_major = str(releasever_major, "utf-8")
|
||||||
|
+ if is_py3bytes(releasever_minor):
|
||||||
|
+ releasever_minor = str(releasever_minor, "utf-8")
|
||||||
|
+ return releasever, releasever_major, releasever_minor
|
||||||
|
+ return (None, None, None)
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def detect_releasever(installroot):
|
||||||
|
+ # :api
|
||||||
|
+ """Calculate the release version for the system."""
|
||||||
|
+
|
||||||
|
+ releasever, _, _ = detect_releasevers(installroot)
|
||||||
|
+ return releasever
|
||||||
|
|
||||||
|
|
||||||
|
def _header(path):
|
||||||
|
--
|
||||||
|
2.48.1
|
||||||
|
|
124
0020-Add-releasever-major-and-releasever-minor-options.patch
Normal file
124
0020-Add-releasever-major-and-releasever-minor-options.patch
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
From 310def9d6995728d6bcaa7bc8bb5f311bcd64ca3 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Evan Goode <mail@evangoo.de>
|
||||||
|
Date: Fri, 24 Jan 2025 22:50:22 +0000
|
||||||
|
Subject: [PATCH] Add --releasever-major and --releasever-minor options
|
||||||
|
|
||||||
|
Upstream commit: 017bbab0a253d84978f645cd358cdeb63e9ecb18
|
||||||
|
|
||||||
|
Allows the user to override the $releasever_major and $releasever_minor
|
||||||
|
variables on the command line, like --releasever.
|
||||||
|
---
|
||||||
|
dnf/cli/cli.py | 29 +++++++++++++++++------------
|
||||||
|
dnf/cli/option_parser.py | 6 ++++++
|
||||||
|
doc/command_ref.rst | 8 ++++++++
|
||||||
|
doc/conf_ref.rst | 2 ++
|
||||||
|
4 files changed, 33 insertions(+), 12 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/dnf/cli/cli.py b/dnf/cli/cli.py
|
||||||
|
index 118ce4e7..0be9559d 100644
|
||||||
|
--- a/dnf/cli/cli.py
|
||||||
|
+++ b/dnf/cli/cli.py
|
||||||
|
@@ -836,7 +836,7 @@ class Cli(object):
|
||||||
|
dnf.conf.PRIO_DEFAULT)
|
||||||
|
self.demands.cacheonly = True
|
||||||
|
self.base.conf._configure_from_options(opts)
|
||||||
|
- self._read_conf_file(opts.releasever)
|
||||||
|
+ self._read_conf_file(opts.releasever, opts.releasever_major, opts.releasever_minor)
|
||||||
|
if 'arch' in opts:
|
||||||
|
self.base.conf.arch = opts.arch
|
||||||
|
self.base.conf._adjust_conf_options()
|
||||||
|
@@ -945,7 +945,7 @@ class Cli(object):
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
- def _read_conf_file(self, releasever=None):
|
||||||
|
+ def _read_conf_file(self, releasever=None, releasever_major=None, releasever_minor=None):
|
||||||
|
timer = dnf.logging.Timer('config')
|
||||||
|
conf = self.base.conf
|
||||||
|
|
||||||
|
@@ -973,18 +973,23 @@ class Cli(object):
|
||||||
|
subst.update_from_etc(from_root, varsdir=conf._get_value('varsdir'))
|
||||||
|
|
||||||
|
# cachedir, logs, releasever, and gpgkey are taken from or stored in installroot
|
||||||
|
- major = None
|
||||||
|
- minor = None
|
||||||
|
+
|
||||||
|
+ det_major = None
|
||||||
|
+ det_minor = None
|
||||||
|
if releasever is None and conf.releasever is None:
|
||||||
|
- releasever, major, minor = dnf.rpm.detect_releasevers(conf.installroot)
|
||||||
|
+ releasever, det_major, det_minor = dnf.rpm.detect_releasevers(conf.installroot)
|
||||||
|
elif releasever == '/':
|
||||||
|
- releasever, major, minor = dnf.rpm.detect_releasevers(releasever)
|
||||||
|
- if releasever is not None:
|
||||||
|
- conf.releasever = releasever
|
||||||
|
- if major is not None:
|
||||||
|
- conf.releasever_major = major
|
||||||
|
- if minor is not None:
|
||||||
|
- conf.releasever_minor = minor
|
||||||
|
+ releasever, det_major, det_minor = dnf.rpm.detect_releasevers(releasever)
|
||||||
|
+
|
||||||
|
+ def or_else(*args):
|
||||||
|
+ for arg in args:
|
||||||
|
+ if arg is not None:
|
||||||
|
+ return arg
|
||||||
|
+ return None
|
||||||
|
+ conf.releasever = or_else(releasever, conf.releasever)
|
||||||
|
+ conf.releasever_major = or_else(releasever_major, det_major, conf.releasever_major)
|
||||||
|
+ conf.releasever_minor = or_else(releasever_minor, det_minor, conf.releasever_minor)
|
||||||
|
+
|
||||||
|
if conf.releasever is None:
|
||||||
|
logger.warning(_("Unable to detect release version (use '--releasever' to specify "
|
||||||
|
"release version)"))
|
||||||
|
diff --git a/dnf/cli/option_parser.py b/dnf/cli/option_parser.py
|
||||||
|
index ec4696fd..fba37614 100644
|
||||||
|
--- a/dnf/cli/option_parser.py
|
||||||
|
+++ b/dnf/cli/option_parser.py
|
||||||
|
@@ -199,6 +199,12 @@ class OptionParser(argparse.ArgumentParser):
|
||||||
|
general_grp.add_argument("--releasever", default=None,
|
||||||
|
help=_("override the value of $releasever"
|
||||||
|
" in config and repo files"))
|
||||||
|
+ general_grp.add_argument("--releasever-major", default=None,
|
||||||
|
+ help=_("override the value of $releasever_major"
|
||||||
|
+ " in config and repo files"))
|
||||||
|
+ general_grp.add_argument("--releasever-minor", default=None,
|
||||||
|
+ help=_("override the value of $releasever_minor"
|
||||||
|
+ " in config and repo files"))
|
||||||
|
general_grp.add_argument("--setopt", dest="setopts", default=[],
|
||||||
|
action=self._SetoptsCallback,
|
||||||
|
help=_("set arbitrary config and repo options"))
|
||||||
|
diff --git a/doc/command_ref.rst b/doc/command_ref.rst
|
||||||
|
index 2337a2e2..9ffd4c1c 100644
|
||||||
|
--- a/doc/command_ref.rst
|
||||||
|
+++ b/doc/command_ref.rst
|
||||||
|
@@ -334,6 +334,14 @@ Options
|
||||||
|
Configure DNF as if the distribution release was ``<release>``. This can
|
||||||
|
affect cache paths, values in configuration files and mirrorlist URLs.
|
||||||
|
|
||||||
|
+``--releasever_major=<major version>``
|
||||||
|
+ Override the releasever_major variable, which is usually automatically
|
||||||
|
+ detected or taken from the part of ``$releasever`` before the first ``.``.
|
||||||
|
+
|
||||||
|
+``--releasever_minor=<minor version>``
|
||||||
|
+ Override the releasever_minor variable, which is usually automatically
|
||||||
|
+ detected or taken from the part of ``$releasever`` after the first ``.``.
|
||||||
|
+
|
||||||
|
.. _repofrompath_options-label:
|
||||||
|
|
||||||
|
|
||||||
|
diff --git a/doc/conf_ref.rst b/doc/conf_ref.rst
|
||||||
|
index d4e4a277..441aa77b 100644
|
||||||
|
--- a/doc/conf_ref.rst
|
||||||
|
+++ b/doc/conf_ref.rst
|
||||||
|
@@ -503,6 +503,8 @@ configuration file by your distribution to override the DNF defaults.
|
||||||
|
|
||||||
|
The ``$releasever_major`` and ``$releasever_minor`` variables will be automatically derived from ``$releasever`` by splitting it on the first ``.``. For example, if ``$releasever`` is set to ``1.23``, then ``$releasever_major`` will be ``1`` and ``$releasever_minor`` will be ``23``.
|
||||||
|
|
||||||
|
+ ``$releasever_major`` and ``$releasever_minor`` can also be set by the distribution.
|
||||||
|
+
|
||||||
|
See also :ref:`repo variables <repo-variables-label>`.
|
||||||
|
|
||||||
|
.. _reposdir-label:
|
||||||
|
--
|
||||||
|
2.48.1
|
||||||
|
|
@ -0,0 +1,57 @@
|
|||||||
|
From ce877f038577a0080ca9e8f69a7fe28047e829ad Mon Sep 17 00:00:00 2001
|
||||||
|
From: Evan Goode <mail@evangoo.de>
|
||||||
|
Date: Mon, 27 Jan 2025 18:49:11 +0000
|
||||||
|
Subject: [PATCH] doc: Document detect_releasevers and update example
|
||||||
|
|
||||||
|
Upstream commit: 593ab0c377cdca78895628c9f7a4676ca220215c
|
||||||
|
|
||||||
|
Adds dnf.rpm.detect_releasevers to the API docs and mention it is
|
||||||
|
now preferred over dnf.rpm.detect_releasever.
|
||||||
|
|
||||||
|
Updates examples/install_extension.py to use detect_releasevers and set
|
||||||
|
the releasever_major and releasever_minor substitution variables.
|
||||||
|
---
|
||||||
|
doc/api_rpm.rst | 8 ++++++++
|
||||||
|
doc/examples/install_extension.py | 6 +++++-
|
||||||
|
2 files changed, 13 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/doc/api_rpm.rst b/doc/api_rpm.rst
|
||||||
|
index c59ed67d..562be41a 100644
|
||||||
|
--- a/doc/api_rpm.rst
|
||||||
|
+++ b/doc/api_rpm.rst
|
||||||
|
@@ -27,6 +27,14 @@
|
||||||
|
|
||||||
|
Returns ``None`` if the information can not be determined (perhaps because the tree has no RPMDB).
|
||||||
|
|
||||||
|
+.. function:: detect_releasevers(installroot)
|
||||||
|
+
|
||||||
|
+ Returns a tuple of the release name, overridden major release, and overridden minor release of the distribution of the tree rooted at `installroot`. The function uses information from RPMDB found under the tree. The major and minor release versions are usually derived from the release version by splitting it on the first ``.``, but distributions can override the derived major and minor versions. It's preferred to use ``detect_releasevers`` over ``detect_releasever``; if you use the latter, you will not be aware of distribution overrides for the major and minor release versions.
|
||||||
|
+
|
||||||
|
+ Returns ``(None, None, None)`` if the information can not be determined (perhaps because the tree has no RPMDB).
|
||||||
|
+
|
||||||
|
+ If the distribution does not override the release major version, then the second item of the returned tuple will be ``None``. Likewise, if the release minor version is not overridden, the third return value will be ``None``.
|
||||||
|
+
|
||||||
|
.. function:: basearch(arch)
|
||||||
|
|
||||||
|
Return base architecture of the processor based on `arch` type given. E.g. when `arch` i686 is given then the returned value will be i386.
|
||||||
|
diff --git a/doc/examples/install_extension.py b/doc/examples/install_extension.py
|
||||||
|
index dbd3b890..b1540e12 100644
|
||||||
|
--- a/doc/examples/install_extension.py
|
||||||
|
+++ b/doc/examples/install_extension.py
|
||||||
|
@@ -32,8 +32,12 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
|
with dnf.Base() as base:
|
||||||
|
# Substitutions are needed for correct interpretation of repo files.
|
||||||
|
- RELEASEVER = dnf.rpm.detect_releasever(base.conf.installroot)
|
||||||
|
+ RELEASEVER, MAJOR, MINOR = dnf.rpm.detect_releasever(base.conf.installroot)
|
||||||
|
base.conf.substitutions['releasever'] = RELEASEVER
|
||||||
|
+ if MAJOR is not None:
|
||||||
|
+ base.conf.substitutions['releasever_major'] = MAJOR
|
||||||
|
+ if MINOR is not None:
|
||||||
|
+ base.conf.substitutions['releasever_minor'] = MINOR
|
||||||
|
# Repositories are needed if we want to install anything.
|
||||||
|
base.read_all_repos()
|
||||||
|
# A sack is required by marking methods and dependency resolving.
|
||||||
|
--
|
||||||
|
2.48.1
|
||||||
|
|
129
0022-tests-Patch-detect_releasevers-not-detect_releasever.patch
Normal file
129
0022-tests-Patch-detect_releasevers-not-detect_releasever.patch
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
From 71fda4446391fc17e121d4c8c5eea70c981a2f0f Mon Sep 17 00:00:00 2001
|
||||||
|
From: Evan Goode <mail@evangoo.de>
|
||||||
|
Date: Mon, 27 Jan 2025 19:20:14 +0000
|
||||||
|
Subject: [PATCH] tests: Patch detect_releasevers, not detect_releasever
|
||||||
|
|
||||||
|
Upstream commit: ba5ddbddf1ffb983fe9dc1a329e22eacd2ef35f7
|
||||||
|
---
|
||||||
|
tests/api/test_dnf_rpm.py | 4 ++++
|
||||||
|
tests/cli/commands/test_clean.py | 2 +-
|
||||||
|
tests/support.py | 2 +-
|
||||||
|
tests/test_base.py | 4 ++--
|
||||||
|
tests/test_cli.py | 10 +++++-----
|
||||||
|
5 files changed, 13 insertions(+), 9 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/tests/api/test_dnf_rpm.py b/tests/api/test_dnf_rpm.py
|
||||||
|
index e6d8de84..fb606ffc 100644
|
||||||
|
--- a/tests/api/test_dnf_rpm.py
|
||||||
|
+++ b/tests/api/test_dnf_rpm.py
|
||||||
|
@@ -14,6 +14,10 @@ class DnfRpmApiTest(TestCase):
|
||||||
|
# dnf.rpm.detect_releasever
|
||||||
|
self.assertHasAttr(dnf.rpm, "detect_releasever")
|
||||||
|
|
||||||
|
+ def test_detect_releasevers(self):
|
||||||
|
+ # dnf.rpm.detect_releasevers
|
||||||
|
+ self.assertHasAttr(dnf.rpm, "detect_releasevers")
|
||||||
|
+
|
||||||
|
def test_basearch(self):
|
||||||
|
# dnf.rpm.basearch
|
||||||
|
self.assertHasAttr(dnf.rpm, "basearch")
|
||||||
|
diff --git a/tests/cli/commands/test_clean.py b/tests/cli/commands/test_clean.py
|
||||||
|
index cc0a5df3..c77cb3ef 100644
|
||||||
|
--- a/tests/cli/commands/test_clean.py
|
||||||
|
+++ b/tests/cli/commands/test_clean.py
|
||||||
|
@@ -31,7 +31,7 @@ from tests.support import mock
|
||||||
|
'''
|
||||||
|
def _run(cli, args):
|
||||||
|
with mock.patch('sys.stdout', new_callable=StringIO), \
|
||||||
|
- mock.patch('dnf.rpm.detect_releasever', return_value=69):
|
||||||
|
+ mock.patch('dnf.rpm.detect_releasevers', return_value=(69, None, None)):
|
||||||
|
cli.configure(['clean', '--config', '/dev/null'] + args)
|
||||||
|
cli.run()
|
||||||
|
|
||||||
|
diff --git a/tests/support.py b/tests/support.py
|
||||||
|
index e50684ef..d03683ed 100644
|
||||||
|
--- a/tests/support.py
|
||||||
|
+++ b/tests/support.py
|
||||||
|
@@ -177,7 +177,7 @@ def command_run(cmd, args):
|
||||||
|
|
||||||
|
class Base(dnf.Base):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
- with mock.patch('dnf.rpm.detect_releasever', return_value=69):
|
||||||
|
+ with mock.patch('dnf.rpm.detect_releasevers', return_value=(69, None, None)):
|
||||||
|
super(Base, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
# mock objects
|
||||||
|
diff --git a/tests/test_base.py b/tests/test_base.py
|
||||||
|
index ad3ef675..9e0a656d 100644
|
||||||
|
--- a/tests/test_base.py
|
||||||
|
+++ b/tests/test_base.py
|
||||||
|
@@ -57,7 +57,7 @@ class BaseTest(tests.support.TestCase):
|
||||||
|
self.assertIsNotNone(base)
|
||||||
|
base.close()
|
||||||
|
|
||||||
|
- @mock.patch('dnf.rpm.detect_releasever', lambda x: 'x')
|
||||||
|
+ @mock.patch('dnf.rpm.detect_releasevers', lambda x: ('x', None, None))
|
||||||
|
@mock.patch('dnf.util.am_i_root', lambda: True)
|
||||||
|
def test_default_config_root(self):
|
||||||
|
base = dnf.Base()
|
||||||
|
@@ -67,7 +67,7 @@ class BaseTest(tests.support.TestCase):
|
||||||
|
self.assertIsNotNone(reg.match(base.conf.cachedir))
|
||||||
|
base.close()
|
||||||
|
|
||||||
|
- @mock.patch('dnf.rpm.detect_releasever', lambda x: 'x')
|
||||||
|
+ @mock.patch('dnf.rpm.detect_releasevers', lambda x: ('x', None, None))
|
||||||
|
@mock.patch('dnf.util.am_i_root', lambda: False)
|
||||||
|
def test_default_config_user(self):
|
||||||
|
base = dnf.Base()
|
||||||
|
diff --git a/tests/test_cli.py b/tests/test_cli.py
|
||||||
|
index 9c130c36..573d4ae2 100644
|
||||||
|
--- a/tests/test_cli.py
|
||||||
|
+++ b/tests/test_cli.py
|
||||||
|
@@ -191,7 +191,7 @@ class ConfigureTest(tests.support.DnfBaseTestCase):
|
||||||
|
# call setUp() once again *after* am_i_root() is mocked so the cachedir is set as expected
|
||||||
|
self.setUp()
|
||||||
|
self.base._conf.installroot = self._installroot
|
||||||
|
- with mock.patch('dnf.rpm.detect_releasever', return_value=69):
|
||||||
|
+ with mock.patch('dnf.rpm.detect_releasevers', return_value=(69, None, None)):
|
||||||
|
self.cli.configure(['update', '-c', self.conffile])
|
||||||
|
reg = re.compile('^' + self._installroot + '/var/tmp/dnf-[.a-zA-Z0-9_-]+$')
|
||||||
|
self.assertIsNotNone(reg.match(self.base.conf.cachedir))
|
||||||
|
@@ -203,7 +203,7 @@ class ConfigureTest(tests.support.DnfBaseTestCase):
|
||||||
|
def test_configure_root(self):
|
||||||
|
""" Test Cli.configure as root."""
|
||||||
|
self.base._conf = dnf.conf.Conf()
|
||||||
|
- with mock.patch('dnf.rpm.detect_releasever', return_value=69):
|
||||||
|
+ with mock.patch('dnf.rpm.detect_releasevers', return_value=(69, None, None)):
|
||||||
|
self.cli.configure(['update', '--nogpgcheck', '-c', self.conffile])
|
||||||
|
reg = re.compile('^/var/cache/dnf$')
|
||||||
|
self.assertIsNotNone(reg.match(self.base.conf.system_cachedir))
|
||||||
|
@@ -213,7 +213,7 @@ class ConfigureTest(tests.support.DnfBaseTestCase):
|
||||||
|
|
||||||
|
def test_configure_verbose(self):
|
||||||
|
self.base._conf.installroot = self._installroot
|
||||||
|
- with mock.patch('dnf.rpm.detect_releasever', return_value=69):
|
||||||
|
+ with mock.patch('dnf.rpm.detect_releasevers', return_value=(69, None, None)):
|
||||||
|
self.cli.configure(['-v', 'update', '-c', self.conffile])
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
expected = "%s -v update -c %s " % (parser.prog, self.conffile)
|
||||||
|
@@ -225,7 +225,7 @@ class ConfigureTest(tests.support.DnfBaseTestCase):
|
||||||
|
@mock.patch('os.path.exists', return_value=True)
|
||||||
|
def test_conf_exists_in_installroot(self, ospathexists):
|
||||||
|
with mock.patch('logging.Logger.warning'), \
|
||||||
|
- mock.patch('dnf.rpm.detect_releasever', return_value=69):
|
||||||
|
+ mock.patch('dnf.rpm.detect_releasevers', return_value=(69, None, None)):
|
||||||
|
self.cli.configure(['--installroot', '/roots/dnf', 'update'])
|
||||||
|
self.assertEqual(self.base.conf.config_file_path, '/roots/dnf/etc/dnf/dnf.conf')
|
||||||
|
self.assertEqual(self.base.conf.installroot, '/roots/dnf')
|
||||||
|
@@ -233,7 +233,7 @@ class ConfigureTest(tests.support.DnfBaseTestCase):
|
||||||
|
@mock.patch('dnf.cli.cli.Cli._parse_commands', new=mock.MagicMock)
|
||||||
|
@mock.patch('os.path.exists', return_value=False)
|
||||||
|
def test_conf_notexists_in_installroot(self, ospathexists):
|
||||||
|
- with mock.patch('dnf.rpm.detect_releasever', return_value=69):
|
||||||
|
+ with mock.patch('dnf.rpm.detect_releasevers', return_value=(69, None, None)):
|
||||||
|
self.cli.configure(['--installroot', '/roots/dnf', 'update'])
|
||||||
|
self.assertEqual(self.base.conf.config_file_path, '/etc/dnf/dnf.conf')
|
||||||
|
self.assertEqual(self.base.conf.installroot, '/roots/dnf')
|
||||||
|
--
|
||||||
|
2.48.1
|
||||||
|
|
@ -0,0 +1,97 @@
|
|||||||
|
From 0d750818c8aa92fd08dd5179839f5734a1b1be96 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Evan Goode <mail@evangoo.de>
|
||||||
|
Date: Tue, 4 Feb 2025 23:01:43 +0000
|
||||||
|
Subject: [PATCH] Document how --releasever, --releasever_{major,minor} affect
|
||||||
|
each other
|
||||||
|
|
||||||
|
Upstream commit: e931960d26a0782a23e8d89a6a662ee2442153fc
|
||||||
|
---
|
||||||
|
dnf/conf/config.py | 16 ++++++++++++++++
|
||||||
|
doc/command_ref.rst | 2 ++
|
||||||
|
tests/test_config.py | 3 +++
|
||||||
|
3 files changed, 21 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/dnf/conf/config.py b/dnf/conf/config.py
|
||||||
|
index 6cf28724..7e5e8a38 100644
|
||||||
|
--- a/dnf/conf/config.py
|
||||||
|
+++ b/dnf/conf/config.py
|
||||||
|
@@ -425,6 +425,12 @@ class MainConf(BaseConfig):
|
||||||
|
@releasever.setter
|
||||||
|
def releasever(self, val):
|
||||||
|
# :api
|
||||||
|
+ """
|
||||||
|
+ Sets the releasever variable and sets releasever_major and
|
||||||
|
+ releasever_minor accordingly. releasever_major is set to the part of
|
||||||
|
+ $releasever before the first ".". releasever_minor is set to the part
|
||||||
|
+ after the first ".".
|
||||||
|
+ """
|
||||||
|
if val is None:
|
||||||
|
self.substitutions.pop('releasever', None)
|
||||||
|
return
|
||||||
|
@@ -438,6 +444,11 @@ class MainConf(BaseConfig):
|
||||||
|
@releasever_major.setter
|
||||||
|
def releasever_major(self, val):
|
||||||
|
# :api
|
||||||
|
+ """
|
||||||
|
+ Override the releasever_major variable, which is usually derived from
|
||||||
|
+ the releasever variable. This setter does not update the value of
|
||||||
|
+ $releasever.
|
||||||
|
+ """
|
||||||
|
if val is None:
|
||||||
|
self.substitutions.pop('releasever_major', None)
|
||||||
|
return
|
||||||
|
@@ -446,6 +457,11 @@ class MainConf(BaseConfig):
|
||||||
|
@property
|
||||||
|
def releasever_minor(self):
|
||||||
|
# :api
|
||||||
|
+ """
|
||||||
|
+ Override the releasever_minor variable, which is usually derived from
|
||||||
|
+ the releasever variable. This setter does not update the value of
|
||||||
|
+ $releasever.
|
||||||
|
+ """
|
||||||
|
return self.substitutions.get('releasever_minor')
|
||||||
|
|
||||||
|
@releasever_minor.setter
|
||||||
|
diff --git a/doc/command_ref.rst b/doc/command_ref.rst
|
||||||
|
index 9ffd4c1c..f7b8e22c 100644
|
||||||
|
--- a/doc/command_ref.rst
|
||||||
|
+++ b/doc/command_ref.rst
|
||||||
|
@@ -337,10 +337,12 @@ Options
|
||||||
|
``--releasever_major=<major version>``
|
||||||
|
Override the releasever_major variable, which is usually automatically
|
||||||
|
detected or taken from the part of ``$releasever`` before the first ``.``.
|
||||||
|
+ This option does not affect the ``$releasever`` variable.
|
||||||
|
|
||||||
|
``--releasever_minor=<minor version>``
|
||||||
|
Override the releasever_minor variable, which is usually automatically
|
||||||
|
detected or taken from the part of ``$releasever`` after the first ``.``.
|
||||||
|
+ This option does not affect the ``$releasever`` variable.
|
||||||
|
|
||||||
|
.. _repofrompath_options-label:
|
||||||
|
|
||||||
|
diff --git a/tests/test_config.py b/tests/test_config.py
|
||||||
|
index 16bdcccb..69ba988c 100644
|
||||||
|
--- a/tests/test_config.py
|
||||||
|
+++ b/tests/test_config.py
|
||||||
|
@@ -149,15 +149,18 @@ class ConfTest(tests.support.TestCase):
|
||||||
|
def test_releasever_major_minor(self):
|
||||||
|
conf = Conf()
|
||||||
|
conf.releasever = '1.2'
|
||||||
|
+ self.assertEqual(conf.releasever, '1.2')
|
||||||
|
self.assertEqual(conf.releasever_major, '1')
|
||||||
|
self.assertEqual(conf.releasever_minor, '2')
|
||||||
|
|
||||||
|
# override releasever_major
|
||||||
|
conf.releasever_major = '3'
|
||||||
|
+ self.assertEqual(conf.releasever, '1.2')
|
||||||
|
self.assertEqual(conf.releasever_major, '3')
|
||||||
|
self.assertEqual(conf.releasever_minor, '2')
|
||||||
|
|
||||||
|
# override releasever_minor
|
||||||
|
conf.releasever_minor = '4'
|
||||||
|
+ self.assertEqual(conf.releasever, '1.2')
|
||||||
|
self.assertEqual(conf.releasever_major, '3')
|
||||||
|
self.assertEqual(conf.releasever_minor, '4')
|
||||||
|
--
|
||||||
|
2.48.1
|
||||||
|
|
62
dnf.spec
62
dnf.spec
@ -2,7 +2,7 @@
|
|||||||
%define __cmake_in_source_build 1
|
%define __cmake_in_source_build 1
|
||||||
|
|
||||||
# default dependencies
|
# default dependencies
|
||||||
%global hawkey_version 0.73.1
|
%global hawkey_version 0.74.0
|
||||||
%global libcomps_version 0.1.8
|
%global libcomps_version 0.1.8
|
||||||
%global libmodulemd_version 2.9.3
|
%global libmodulemd_version 2.9.3
|
||||||
%global rpm_version 4.14.0
|
%global rpm_version 4.14.0
|
||||||
@ -23,6 +23,10 @@
|
|||||||
%global rpm_version 4.11.3-25.el7.centos.1
|
%global rpm_version 4.11.3-25.el7.centos.1
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
|
%if 0%{?rhel} == 10
|
||||||
|
%global hawkey_version 0.73.1-8
|
||||||
|
%endif
|
||||||
|
|
||||||
# override dependencies for fedora 26
|
# override dependencies for fedora 26
|
||||||
%if 0%{?fedora} == 26
|
%if 0%{?fedora} == 26
|
||||||
%global rpm_version 4.13.0.1-7
|
%global rpm_version 4.13.0.1-7
|
||||||
@ -68,7 +72,7 @@ It supports RPMs, modules and comps groups & environments.
|
|||||||
|
|
||||||
Name: dnf
|
Name: dnf
|
||||||
Version: 4.20.0
|
Version: 4.20.0
|
||||||
Release: 6%{?dist}
|
Release: 12%{?dist}
|
||||||
Summary: %{pkg_summary}
|
Summary: %{pkg_summary}
|
||||||
# For a breakdown of the licensing, see PACKAGE-LICENSING
|
# For a breakdown of the licensing, see PACKAGE-LICENSING
|
||||||
License: GPL-2.0-or-later AND GPL-1.0-only
|
License: GPL-2.0-or-later AND GPL-1.0-only
|
||||||
@ -79,6 +83,25 @@ Patch2: 0002-Limit-queries-to-nevra-forms-when-provided-by-comman.patch
|
|||||||
Patch3: 0003-doc-Remove-provide-of-spec-definition-for-repoquery-.patch
|
Patch3: 0003-doc-Remove-provide-of-spec-definition-for-repoquery-.patch
|
||||||
Patch4: 0004-Drop-collect-file-for-ABRT.patch
|
Patch4: 0004-Drop-collect-file-for-ABRT.patch
|
||||||
Patch5: 0005-tests-Use-PGP-keys-without-SHA-1.patch
|
Patch5: 0005-tests-Use-PGP-keys-without-SHA-1.patch
|
||||||
|
Patch6: 0006-Add-detection-for-ostree-based-systems-and-warn-user.patch
|
||||||
|
Patch7: 0007-Update-ostree-bootc-host-system-check.patch
|
||||||
|
Patch8: 0008-Update-bootc-hosts-message-to-point-to-bootc-help.patch
|
||||||
|
Patch9: 0009-Allow-installroot-on-read-only-bootc-system.patch
|
||||||
|
Patch10: 0010-Allow-downloadonly-on-read-only-bootc-system.patch
|
||||||
|
Patch11: 0011-Automatic-check-availability-of-config-file.patch
|
||||||
|
Patch12: 0012-Add-support-for-transient.patch
|
||||||
|
Patch13: 0013-bootc-Document-transient-and-persistence.patch
|
||||||
|
Patch14: 0014-bootc-Use-ostree-GObject-API-to-get-deployment-statu.patch
|
||||||
|
Patch15: 0015-bootc-Re-locking-use-ostree-admin-unlock-transient.patch
|
||||||
|
Patch16: 0016-spec-Add-dnf-bootc-subpackage.patch
|
||||||
|
Patch17: 0017-Require-libdnf-0.74.0-with-persistence-option.patch
|
||||||
|
Patch18: 0018-Derive-releasever_-major-minor-in-conf-not-substitut.patch
|
||||||
|
Patch19: 0019-Override-releasever_-major-minor-with-provides.patch
|
||||||
|
Patch20: 0020-Add-releasever-major-and-releasever-minor-options.patch
|
||||||
|
Patch21: 0021-doc-Document-detect_releasevers-and-update-example.patch
|
||||||
|
Patch22: 0022-tests-Patch-detect_releasevers-not-detect_releasever.patch
|
||||||
|
Patch23: 0023-Document-how-releasever-releasever_-major-minor-affe.patch
|
||||||
|
|
||||||
BuildArch: noarch
|
BuildArch: noarch
|
||||||
BuildRequires: cmake
|
BuildRequires: cmake
|
||||||
BuildRequires: gettext
|
BuildRequires: gettext
|
||||||
@ -196,6 +219,17 @@ Requires: python3-%{name} = %{version}-%{release}
|
|||||||
%description automatic
|
%description automatic
|
||||||
Systemd units that can periodically download package upgrades and apply them.
|
Systemd units that can periodically download package upgrades and apply them.
|
||||||
|
|
||||||
|
%package bootc
|
||||||
|
Summary: %{pkg_summary} - additional bootc dependencies
|
||||||
|
Requires: python3-%{name} = %{version}-%{release}
|
||||||
|
Requires: ostree
|
||||||
|
Requires: ostree-libs
|
||||||
|
Requires: python3-gobject-base
|
||||||
|
Requires: util-linux-core
|
||||||
|
|
||||||
|
%description bootc
|
||||||
|
Additional dependencies needed to perform transactions on booted bootc (bootable containers) systems.
|
||||||
|
|
||||||
|
|
||||||
%prep
|
%prep
|
||||||
%autosetup -p1
|
%autosetup -p1
|
||||||
@ -418,7 +452,31 @@ popd
|
|||||||
%{_unitdir}/%{name}-automatic-install.timer
|
%{_unitdir}/%{name}-automatic-install.timer
|
||||||
%{python3_sitelib}/%{name}/automatic/
|
%{python3_sitelib}/%{name}/automatic/
|
||||||
|
|
||||||
|
%files bootc
|
||||||
|
# bootc subpackage does not include any files
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Fri Feb 07 2025 Carl George <carl@redhat.com> - 4.20.0-12
|
||||||
|
- Override releasever_{major,minor} with system-release provides (RHEL-68034)
|
||||||
|
|
||||||
|
* Thu Feb 06 2025 Petr Pisar <ppisar@redhat.com> - 4.20.0-11
|
||||||
|
- Add support for transient transactions (RHEL-76849)
|
||||||
|
|
||||||
|
* Fri Dec 13 2024 Marek Blaha <mblaha@redhat.com> - 4.20.0-10
|
||||||
|
- automatic: Check availability of config file
|
||||||
|
|
||||||
|
* Tue Oct 29 2024 Troy Dawson <tdawson@redhat.com> - 4.20.0-9
|
||||||
|
- Bump release for October 2024 mass rebuild:
|
||||||
|
Resolves: RHEL-64018
|
||||||
|
|
||||||
|
* Tue Oct 15 2024 Petr Pisar <ppisar@redhat.com> - 4.20.0-8
|
||||||
|
- Allow "dnf install --downloadonly" on locked OSTree and bootc systems
|
||||||
|
(RHEL-62028)
|
||||||
|
|
||||||
|
* Fri Sep 20 2024 Petr Pisar <ppisar@redhat.com> - 4.20.0-7
|
||||||
|
- More specific error message on a locked OSTree system or a bootc system
|
||||||
|
without a usr-overlay (RHEL-49671)
|
||||||
|
|
||||||
* Tue Aug 06 2024 Petr Pisar <ppisar@redhat.com> - 4.20.0-6
|
* Tue Aug 06 2024 Petr Pisar <ppisar@redhat.com> - 4.20.0-6
|
||||||
- Revert more specific error message on a locked OSTree system or a bootc system
|
- Revert more specific error message on a locked OSTree system or a bootc system
|
||||||
without a usr-overlay (RHEL-49671)
|
without a usr-overlay (RHEL-49671)
|
||||||
|
Loading…
Reference in New Issue
Block a user