Added patch for almalinux bugtracker
This commit is contained in:
commit
33be678ace
57
SOURCES/0070-bootc-unlock-only-if-usr-is-read-only.patch
Normal file
57
SOURCES/0070-bootc-unlock-only-if-usr-is-read-only.patch
Normal file
@ -0,0 +1,57 @@
|
||||
From a8608927848b918e82d4b78bc0312cfdb58210de Mon Sep 17 00:00:00 2001
|
||||
From: Evan Goode <mail@evangoo.de>
|
||||
Date: Mon, 1 Dec 2025 13:40:26 -0500
|
||||
Subject: [PATCH 1/2] bootc: unlock only if /usr is read-only
|
||||
|
||||
DNF should only run `ostree admin unlock --transient` if `/usr` is
|
||||
actually read-only. `/usr` may be writable via OSTree's `root.transient =
|
||||
true` even if the `ostree admin status` is not transient.
|
||||
|
||||
Resolves: https://redhat.atlassian.net/browse/RHEL-138512
|
||||
---
|
||||
dnf/cli/cli.py | 4 +++-
|
||||
dnf/util.py | 2 +-
|
||||
2 files changed, 4 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/dnf/cli/cli.py b/dnf/cli/cli.py
|
||||
index 21e8764d0..f855c5eec 100644
|
||||
--- a/dnf/cli/cli.py
|
||||
+++ b/dnf/cli/cli.py
|
||||
@@ -225,6 +225,7 @@ class BaseCli(dnf.Base):
|
||||
# Handle bootc transactions. `--transient` must be specified if
|
||||
# /usr is not already writeable.
|
||||
bootc_system = None
|
||||
+ bootc_system_needs_unlock = False
|
||||
if is_bootc_transaction:
|
||||
if self.conf.persistence == "persist":
|
||||
logger.info(_("Persistent transactions aren't supported on bootc systems."))
|
||||
@@ -246,6 +247,7 @@ 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_system_needs_unlock = True
|
||||
self._persistence = libdnf.transaction.TransactionPersistence_TRANSIENT
|
||||
|
||||
# Check whether the transaction modifies usr_drift_protected_paths
|
||||
@@ -276,7 +278,7 @@ class BaseCli(dnf.Base):
|
||||
if self.conf.assumeno or not self.output.userconfirm():
|
||||
raise CliError(_("Operation aborted."))
|
||||
|
||||
- if bootc_system:
|
||||
+ if bootc_system and bootc_system_needs_unlock:
|
||||
bootc_system.make_writable()
|
||||
else:
|
||||
logger.info(_('Nothing to do.'))
|
||||
diff --git a/dnf/util.py b/dnf/util.py
|
||||
index 0161f80d8..70bae605f 100644
|
||||
--- a/dnf/util.py
|
||||
+++ b/dnf/util.py
|
||||
@@ -741,4 +741,4 @@ class _BootcSystem:
|
||||
# read-only. Set up a mount namespace for DNF.
|
||||
self._set_up_mountns()
|
||||
|
||||
- assert os.access(self.usr, os.W_OK)
|
||||
+ assert self.is_writable()
|
||||
--
|
||||
2.53.0
|
||||
|
||||
@ -0,0 +1,37 @@
|
||||
From b9c0a2ab768c624ad7746a175379358479df6f55 Mon Sep 17 00:00:00 2001
|
||||
From: Evan Goode <mail@evangoo.de>
|
||||
Date: Fri, 20 Mar 2026 16:28:14 -0400
|
||||
Subject: [PATCH 2/2] bootc: Call make_writable when
|
||||
DeploymentUnlockedState.TRANSIENT
|
||||
|
||||
Fixes a bug in 1afe4328334f27b45b5c4599b6f1e8ac69d465e4.
|
||||
bootc_system.make_writable should still be called even when the system
|
||||
is already in DeploymentUnlockedState.TRANSIENT, since the DNF mount
|
||||
namespace needs to be set up either way.
|
||||
---
|
||||
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 f855c5eec..ce81f262b 100644
|
||||
--- a/dnf/cli/cli.py
|
||||
+++ b/dnf/cli/cli.py
|
||||
@@ -235,6 +235,7 @@ class BaseCli(dnf.Base):
|
||||
bootc_system = dnf.util._BootcSystem()
|
||||
|
||||
if not bootc_system.is_writable():
|
||||
+ bootc_system_needs_unlock = True
|
||||
if self.conf.persistence == "auto":
|
||||
logger.info(_("This bootc system is configured to be read-only. Pass --transient to "
|
||||
"perform this transaction in a transient overlay which will reset when "
|
||||
@@ -247,7 +248,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_system_needs_unlock = True
|
||||
self._persistence = libdnf.transaction.TransactionPersistence_TRANSIENT
|
||||
|
||||
# Check whether the transaction modifies usr_drift_protected_paths
|
||||
--
|
||||
2.53.0
|
||||
|
||||
64
SOURCES/0072-Preserve-ACL-when-rotating-logs.patch
Normal file
64
SOURCES/0072-Preserve-ACL-when-rotating-logs.patch
Normal file
@ -0,0 +1,64 @@
|
||||
From 51e0b326e3d33644a0e46635055ada7f4f0006d7 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Petr=20P=C3=ADsa=C5=99?= <ppisar@redhat.com>
|
||||
Date: Wed, 19 Nov 2025 16:05:25 +0100
|
||||
Subject: [PATCH] Preserve ACL when rotating logs
|
||||
|
||||
Upstream commit: 4324b297da2fd6a15670241398a31e7f462e44e4
|
||||
|
||||
When DNF rotated /var/log/dnf.log, it preserved a file mode, but it
|
||||
lost an access control list:
|
||||
|
||||
# getfacl -c /var/log/dnf.log
|
||||
getfacl: Removing leading '/' from absolute path names
|
||||
user::rw-
|
||||
user:root:r--
|
||||
group::r--
|
||||
mask::r--
|
||||
other::r--
|
||||
|
||||
# dnf4 --setopt log_rotate=4 --setopt log_size=1 upgrade --assumeno
|
||||
|
||||
# getfacl -c /var/log/dnf.log
|
||||
getfacl: Removing leading '/' from absolute path names
|
||||
user::rw-
|
||||
group::r--
|
||||
other::r--
|
||||
|
||||
This patch fixes it by copying an extended attribute which stores the access
|
||||
control list. (Python does not have an interface for handling the access
|
||||
control lists.)
|
||||
|
||||
Resolve: #2279
|
||||
Resolve: https://redhat.atlassian.net/browse/RHEL-122013
|
||||
---
|
||||
dnf/logging.py | 11 ++++++++++-
|
||||
1 file changed, 10 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/dnf/logging.py b/dnf/logging.py
|
||||
index ef0b25f33..7dc655932 100644
|
||||
--- a/dnf/logging.py
|
||||
+++ b/dnf/logging.py
|
||||
@@ -125,10 +125,19 @@ class MultiprocessRotatingFileHandler(logging.handlers.RotatingFileHandler):
|
||||
try:
|
||||
if self.shouldRollover(record):
|
||||
with self.rotate_lock:
|
||||
- # Do rollover while preserving the mode of the new log file
|
||||
+ # Do rollover while preserving the mode and ACL of the new log file
|
||||
mode = os.stat(self.baseFilename).st_mode
|
||||
+ acl = None
|
||||
+ try:
|
||||
+ acl = os.getxattr(self.baseFilename, "system.posix_acl_access")
|
||||
+ except:
|
||||
+ # The extended attribute does not exist or the
|
||||
+ # file system does not support them.
|
||||
+ pass
|
||||
self.doRollover()
|
||||
os.chmod(self.baseFilename, mode)
|
||||
+ if acl is not None:
|
||||
+ os.setxattr(self.baseFilename, "system.posix_acl_access", acl)
|
||||
logging.FileHandler.emit(self, record)
|
||||
return
|
||||
except (dnf.exceptions.ProcessLockError, dnf.exceptions.ThreadLockError):
|
||||
--
|
||||
2.53.0
|
||||
|
||||
@ -0,0 +1,38 @@
|
||||
From 056c452cb8031766fa46c1aa6ac949018505abf6 Mon Sep 17 00:00:00 2001
|
||||
From: Evan Goode <mail@evangoo.de>
|
||||
Date: Mon, 9 Mar 2026 22:23:50 -0400
|
||||
Subject: [PATCH] history info: Fix Persistence display for interval queries
|
||||
|
||||
print_persistence() took a persistence parameter but compared against
|
||||
old.persistence (the property on the transaction object) instead. For
|
||||
merged transactions, old.persistence returns a list, so comparisons
|
||||
against scalar enum values always failed, displaying 'Unknown'.
|
||||
|
||||
Use the function parameter instead of old.persistence.
|
||||
|
||||
For: https://github.com/rpm-software-management/libdnf/issues/1744
|
||||
|
||||
Assisted-by: Claude Opus 4.6
|
||||
---
|
||||
dnf/cli/output.py | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/dnf/cli/output.py b/dnf/cli/output.py
|
||||
index 820ab88fa..3fdd68432 100644
|
||||
--- a/dnf/cli/output.py
|
||||
+++ b/dnf/cli/output.py
|
||||
@@ -1773,9 +1773,9 @@ Transaction Summary
|
||||
print(_("Command Line :"), old.cmdline)
|
||||
|
||||
def print_persistence(persistence):
|
||||
- if old.persistence == libdnf.transaction.TransactionPersistence_PERSIST:
|
||||
+ if persistence == libdnf.transaction.TransactionPersistence_PERSIST:
|
||||
persistence_str = "Persist"
|
||||
- elif old.persistence == libdnf.transaction.TransactionPersistence_TRANSIENT:
|
||||
+ elif persistence == libdnf.transaction.TransactionPersistence_TRANSIENT:
|
||||
persistence_str = "Transient"
|
||||
else:
|
||||
persistence_str = "Unknown"
|
||||
--
|
||||
2.54.0
|
||||
|
||||
@ -0,0 +1,59 @@
|
||||
From 5819856c5960f4c4d7a9acb6aaa43aca3fe68211 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= <amatej@redhat.com>
|
||||
Date: Wed, 8 Jul 2026 13:16:43 +0200
|
||||
Subject: [PATCH 1/4] Add `FileLock` for read/write and blocking/non blocking
|
||||
locks
|
||||
|
||||
ProcessLock doesn't support read only locks and it works differently: it
|
||||
effectively achieves the locking by storing a PID, flock is only used to
|
||||
guard writing the PID.
|
||||
---
|
||||
dnf/lock.py | 35 +++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 35 insertions(+)
|
||||
|
||||
diff --git a/dnf/lock.py b/dnf/lock.py
|
||||
index 6817aac9..cb189c35 100644
|
||||
--- a/dnf/lock.py
|
||||
+++ b/dnf/lock.py
|
||||
@@ -146,3 +146,38 @@ class ProcessLock(object):
|
||||
if self.count == 1:
|
||||
os.unlink(self.target)
|
||||
self._unlock_thread()
|
||||
+
|
||||
+
|
||||
+class FileLock(object):
|
||||
+ """
|
||||
+ A file lock with read/write access and blocking/non blocking mode.
|
||||
+ """
|
||||
+
|
||||
+ def __init__(self, target, description, blocking=False, read=False):
|
||||
+ self.blocking = blocking
|
||||
+ self.read = read
|
||||
+ self.description = description
|
||||
+ self.target = target
|
||||
+ self.fd = None
|
||||
+
|
||||
+ def __enter__(self):
|
||||
+ dnf.util.ensure_dir(os.path.dirname(self.target))
|
||||
+ self.fd = os.open(self.target, os.O_CREAT | os.O_RDWR, 0o644)
|
||||
+ flags = fcntl.LOCK_SH if self.read else fcntl.LOCK_EX
|
||||
+ if not self.blocking:
|
||||
+ flags |= fcntl.LOCK_NB
|
||||
+ try:
|
||||
+ fcntl.flock(self.fd, flags)
|
||||
+ except OSError as e:
|
||||
+ os.close(self.fd)
|
||||
+ self.fd = None
|
||||
+ if e.errno == errno.EWOULDBLOCK:
|
||||
+ msg = '%s already locked' % self.description
|
||||
+ raise ProcessLockError(msg, -1)
|
||||
+ raise
|
||||
+
|
||||
+ def __exit__(self, *exc_args):
|
||||
+ if self.fd is not None:
|
||||
+ fcntl.flock(self.fd, fcntl.LOCK_UN)
|
||||
+ os.close(self.fd)
|
||||
+ self.fd = None
|
||||
--
|
||||
2.54.0
|
||||
|
||||
@ -0,0 +1,50 @@
|
||||
From 181a8762ccd830ebc1c7077cbbeaf621d2a0d08b Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= <amatej@redhat.com>
|
||||
Date: Tue, 7 Jul 2026 07:48:29 +0200
|
||||
Subject: [PATCH 2/4] Generalize _BoolDefault to just _Default, allow choices
|
||||
|
||||
This is useful if we want a demand with more states than just True/False
|
||||
---
|
||||
dnf/cli/demand.py | 11 +++++++++--
|
||||
1 file changed, 9 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/dnf/cli/demand.py b/dnf/cli/demand.py
|
||||
index f82a75b1..ac19930b 100644
|
||||
--- a/dnf/cli/demand.py
|
||||
+++ b/dnf/cli/demand.py
|
||||
@@ -21,9 +21,10 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
-class _BoolDefault(object):
|
||||
- def __init__(self, default):
|
||||
+class _Default(object):
|
||||
+ def __init__(self, default, choices=None):
|
||||
self.default = default
|
||||
+ self.choices = choices
|
||||
self._storing_name = '__%s%x' % (self.__class__.__name__, id(self))
|
||||
|
||||
def __get__(self, obj, objtype=None):
|
||||
@@ -33,6 +34,8 @@ class _BoolDefault(object):
|
||||
return self.default
|
||||
|
||||
def __set__(self, obj, val):
|
||||
+ if self.choices is not None and val not in self.choices:
|
||||
+ raise ValueError('Invalid demand value: %s' % val)
|
||||
objdict = obj.__dict__
|
||||
if self._storing_name in objdict:
|
||||
current_val = objdict[self._storing_name]
|
||||
@@ -40,6 +43,10 @@ class _BoolDefault(object):
|
||||
raise AttributeError('Demand already set.')
|
||||
objdict[self._storing_name] = val
|
||||
|
||||
+
|
||||
+_BoolDefault = _Default
|
||||
+
|
||||
+
|
||||
class DemandSheet(object):
|
||||
"""Collection of demands that different CLI parts have on other parts. :api"""
|
||||
|
||||
--
|
||||
2.54.0
|
||||
|
||||
200
SOURCES/0076-Add-clean-command-lock-demand.patch
Normal file
200
SOURCES/0076-Add-clean-command-lock-demand.patch
Normal file
@ -0,0 +1,200 @@
|
||||
From ac21a60646f98d2391900f57b2cfb5af4f745f48 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= <amatej@redhat.com>
|
||||
Date: Wed, 8 Jul 2026 13:47:31 +0200
|
||||
Subject: [PATCH 3/4] Add clean command lock demand
|
||||
|
||||
---
|
||||
dnf/cli/demand.py | 14 +++++++
|
||||
dnf/cli/main.py | 84 +++++++++++++++++++++++-----------------
|
||||
dnf/lock.py | 5 +++
|
||||
tests/cli/test_demand.py | 2 +
|
||||
4 files changed, 70 insertions(+), 35 deletions(-)
|
||||
|
||||
diff --git a/dnf/cli/demand.py b/dnf/cli/demand.py
|
||||
index ac19930b..2dc3a3d4 100644
|
||||
--- a/dnf/cli/demand.py
|
||||
+++ b/dnf/cli/demand.py
|
||||
@@ -20,6 +20,15 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
+from enum import Enum
|
||||
+
|
||||
+
|
||||
+class CleanCommandLock(Enum):
|
||||
+ """Clean command lock modes. :api"""
|
||||
+ NONE = 0
|
||||
+ READ = 1
|
||||
+ WRITE = 2
|
||||
+
|
||||
|
||||
class _Default(object):
|
||||
def __init__(self, default, choices=None):
|
||||
@@ -70,3 +79,8 @@ class DemandSheet(object):
|
||||
# repositories packages (e.g. versionlock).
|
||||
# If it stays None, the demands.resolving is used as a fallback.
|
||||
plugin_filtering_enabled = _BoolDefault(None)
|
||||
+
|
||||
+ # Determines if/how to lock the clean command lock.
|
||||
+ # This lock is used to ensure clean command (which uses WRITE mode) running concurrently
|
||||
+ # with another dnf process doesn't delete required data (packages).
|
||||
+ clean_command_lock = _Default(CleanCommandLock.NONE, tuple(CleanCommandLock))
|
||||
diff --git a/dnf/cli/main.py b/dnf/cli/main.py
|
||||
index 2a7f92d5..fcb62245 100644
|
||||
--- a/dnf/cli/main.py
|
||||
+++ b/dnf/cli/main.py
|
||||
@@ -24,6 +24,7 @@ from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
from dnf.conf import Conf
|
||||
from dnf.cli.cli import Cli
|
||||
+from dnf.cli.demand import CleanCommandLock
|
||||
from dnf.cli.option_parser import OptionParser
|
||||
from dnf.i18n import ucd
|
||||
from dnf.cli.utils import show_lock_owner
|
||||
@@ -33,6 +34,7 @@ import dnf.cli
|
||||
import dnf.cli.cli
|
||||
import dnf.cli.option_parser
|
||||
import dnf.exceptions
|
||||
+import dnf.lock
|
||||
import dnf.i18n
|
||||
import dnf.logging
|
||||
import dnf.util
|
||||
@@ -43,6 +45,7 @@ import logging
|
||||
import os
|
||||
import os.path
|
||||
import sys
|
||||
+from contextlib import nullcontext
|
||||
|
||||
logger = logging.getLogger("dnf")
|
||||
|
||||
@@ -118,45 +121,56 @@ def cli_run(cli, base):
|
||||
else:
|
||||
f.close()
|
||||
|
||||
- try:
|
||||
- cli.run()
|
||||
- except dnf.exceptions.LockError:
|
||||
- raise
|
||||
- except (IOError, OSError) as e:
|
||||
- return ex_IOError(e)
|
||||
+ lock = None
|
||||
+ if cli.demands.clean_command_lock == CleanCommandLock.NONE:
|
||||
+ lock = nullcontext()
|
||||
+ elif cli.demands.clean_command_lock == CleanCommandLock.READ:
|
||||
+ lock = dnf.lock.build_clean_command_lock(base.conf.cachedir, base.conf.exit_on_lock, True)
|
||||
+ elif cli.demands.clean_command_lock == CleanCommandLock.WRITE:
|
||||
+ lock = dnf.lock.build_clean_command_lock(base.conf.cachedir, base.conf.exit_on_lock, False)
|
||||
+ else:
|
||||
+ raise RuntimeError('Invalid demands.clean_command_lock: %s' % cli.demands.clean_command_lock)
|
||||
|
||||
- if cli.demands.resolving:
|
||||
+ with lock:
|
||||
try:
|
||||
- ret = resolving(cli, base)
|
||||
- except dnf.exceptions.DepsolveError as e:
|
||||
- ex_Error(e)
|
||||
- msg = ""
|
||||
- if not cli.demands.allow_erasing and base._goal.problem_conflicts(available=True):
|
||||
- msg += _("try to add '{}' to command line to replace conflicting "
|
||||
- "packages").format("--allowerasing")
|
||||
- if cli.base.conf.strict:
|
||||
- if not msg:
|
||||
- msg += _("try to add '{}' to skip uninstallable packages").format(
|
||||
- "--skip-broken")
|
||||
- else:
|
||||
- msg += _(" or '{}' to skip uninstallable packages").format("--skip-broken")
|
||||
- if cli.base.conf.best:
|
||||
- prio = cli.base.conf._get_priority("best")
|
||||
- if prio <= dnf.conf.PRIO_MAINCONFIG:
|
||||
+ cli.run()
|
||||
+ except dnf.exceptions.LockError:
|
||||
+ raise
|
||||
+ except (IOError, OSError) as e:
|
||||
+ return ex_IOError(e)
|
||||
+
|
||||
+ if cli.demands.resolving:
|
||||
+ try:
|
||||
+ ret = resolving(cli, base)
|
||||
+ except dnf.exceptions.DepsolveError as e:
|
||||
+ ex_Error(e)
|
||||
+ msg = ""
|
||||
+ if not cli.demands.allow_erasing and base._goal.problem_conflicts(available=True):
|
||||
+ msg += _("try to add '{}' to command line to replace conflicting "
|
||||
+ "packages").format("--allowerasing")
|
||||
+ if cli.base.conf.strict:
|
||||
if not msg:
|
||||
- msg += _("try to add '{}' to use not only best candidate packages").format(
|
||||
- "--nobest")
|
||||
+ msg += _("try to add '{}' to skip uninstallable packages").format(
|
||||
+ "--skip-broken")
|
||||
else:
|
||||
- msg += _(" or '{}' to use not only best candidate packages").format(
|
||||
- "--nobest")
|
||||
- if msg:
|
||||
- logger.info("({})".format(msg))
|
||||
- raise
|
||||
- if ret:
|
||||
- return ret
|
||||
-
|
||||
- cli.command.run_transaction()
|
||||
- return cli.demands.success_exit_status
|
||||
+ msg += _(" or '{}' to skip uninstallable packages").format("--skip-broken")
|
||||
+ if cli.base.conf.best:
|
||||
+ prio = cli.base.conf._get_priority("best")
|
||||
+ if prio <= dnf.conf.PRIO_MAINCONFIG:
|
||||
+ if not msg:
|
||||
+ msg += _("try to add '{}' to use not only best candidate packages").format(
|
||||
+ "--nobest")
|
||||
+ else:
|
||||
+ msg += _(" or '{}' to use not only best candidate packages").format(
|
||||
+ "--nobest")
|
||||
+ if msg:
|
||||
+ logger.info("({})".format(msg))
|
||||
+ raise
|
||||
+ if ret:
|
||||
+ return ret
|
||||
+
|
||||
+ cli.command.run_transaction()
|
||||
+ return cli.demands.success_exit_status
|
||||
|
||||
|
||||
def resolving(cli, base):
|
||||
diff --git a/dnf/lock.py b/dnf/lock.py
|
||||
index cb189c35..aae8a51b 100644
|
||||
--- a/dnf/lock.py
|
||||
+++ b/dnf/lock.py
|
||||
@@ -63,6 +63,11 @@ def build_log_lock(logdir, exit_on_lock):
|
||||
'log', not exit_on_lock)
|
||||
|
||||
|
||||
+def build_clean_command_lock(cachedir, exit_on_lock, read):
|
||||
+ return FileLock(os.path.join(_fit_lock_dir(cachedir), 'clean_command.lock'),
|
||||
+ 'clean command', not exit_on_lock, read)
|
||||
+
|
||||
+
|
||||
class ProcessLock(object):
|
||||
def __init__(self, target, description, blocking=False):
|
||||
self.blocking = blocking
|
||||
diff --git a/tests/cli/test_demand.py b/tests/cli/test_demand.py
|
||||
index fc9cfcec..7f99100a 100644
|
||||
--- a/tests/cli/test_demand.py
|
||||
+++ b/tests/cli/test_demand.py
|
||||
@@ -21,6 +21,7 @@ from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import dnf.cli.demand
|
||||
+from dnf.cli.demand import CleanCommandLock
|
||||
|
||||
import tests.support
|
||||
|
||||
@@ -42,6 +43,7 @@ class DemandTest(tests.support.TestCase):
|
||||
self.assertFalse(demands.sack_activation)
|
||||
self.assertFalse(demands.root_user)
|
||||
self.assertEqual(demands.success_exit_status, 0)
|
||||
+ self.assertEqual(demands.clean_command_lock, CleanCommandLock.NONE)
|
||||
|
||||
def test_independence(self):
|
||||
d1 = dnf.cli.demand.DemandSheet()
|
||||
--
|
||||
2.54.0
|
||||
|
||||
@ -0,0 +1,330 @@
|
||||
From 57292d5670793081a92f42e2bb4b3cafd8642d05 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= <amatej@redhat.com>
|
||||
Date: Wed, 8 Jul 2026 14:19:29 +0200
|
||||
Subject: [PATCH 4/4] Configure `demands.clean_command_lock` for commands that
|
||||
need it
|
||||
|
||||
---
|
||||
dnf/cli/commands/__init__.py | 7 +++++++
|
||||
dnf/cli/commands/clean.py | 4 ++++
|
||||
dnf/cli/commands/distrosync.py | 2 ++
|
||||
dnf/cli/commands/downgrade.py | 2 ++
|
||||
dnf/cli/commands/group.py | 2 ++
|
||||
dnf/cli/commands/history.py | 3 +++
|
||||
dnf/cli/commands/install.py | 2 ++
|
||||
dnf/cli/commands/module.py | 4 ++++
|
||||
dnf/cli/commands/reinstall.py | 2 ++
|
||||
dnf/cli/commands/shell.py | 2 ++
|
||||
dnf/cli/commands/swap.py | 2 ++
|
||||
dnf/cli/commands/upgrade.py | 2 ++
|
||||
12 files changed, 34 insertions(+)
|
||||
|
||||
diff --git a/dnf/cli/commands/__init__.py b/dnf/cli/commands/__init__.py
|
||||
index 80d67578..93d076de 100644
|
||||
--- a/dnf/cli/commands/__init__.py
|
||||
+++ b/dnf/cli/commands/__init__.py
|
||||
@@ -24,6 +24,7 @@ Classes for subcommands of the yum command line interface.
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
|
||||
+from dnf.cli.demand import CleanCommandLock
|
||||
from dnf.cli.option_parser import OptionParser
|
||||
from dnf.i18n import _
|
||||
|
||||
@@ -340,6 +341,7 @@ class RepoPkgsCommand(Command):
|
||||
demands.sack_activation = True
|
||||
demands.resolving = True
|
||||
demands.root_user = True
|
||||
+ demands.clean_command_lock = CleanCommandLock.READ
|
||||
|
||||
def run_on_repo(self):
|
||||
self.cli._populate_update_security_filter(self.opts)
|
||||
@@ -392,6 +394,7 @@ class RepoPkgsCommand(Command):
|
||||
demands.available_repos = True
|
||||
demands.resolving = True
|
||||
demands.root_user = True
|
||||
+ demands.clean_command_lock = CleanCommandLock.READ
|
||||
|
||||
def run_on_repo(self):
|
||||
"""Execute the command with respect to given arguments *cli_args*."""
|
||||
@@ -447,6 +450,7 @@ class RepoPkgsCommand(Command):
|
||||
demands.available_repos = True
|
||||
demands.resolving = True
|
||||
demands.root_user = True
|
||||
+ demands.clean_command_lock = CleanCommandLock.READ
|
||||
|
||||
def run_on_repo(self):
|
||||
"""Execute the command with respect to given arguments *cli_args*."""
|
||||
@@ -535,6 +539,7 @@ class RepoPkgsCommand(Command):
|
||||
demands.sack_activation = True
|
||||
demands.resolving = True
|
||||
demands.root_user = True
|
||||
+ demands.clean_command_lock = CleanCommandLock.READ
|
||||
|
||||
def _replace(self, pkg_spec, reponame):
|
||||
"""Synchronize a package with another repository or remove it."""
|
||||
@@ -597,6 +602,7 @@ class RepoPkgsCommand(Command):
|
||||
demands.available_repos = True
|
||||
demands.resolving = True
|
||||
demands.root_user = True
|
||||
+ demands.clean_command_lock = CleanCommandLock.READ
|
||||
|
||||
def run_on_repo(self):
|
||||
"""Execute the command with respect to given arguments *cli_args*."""
|
||||
@@ -686,6 +692,7 @@ class RepoPkgsCommand(Command):
|
||||
demands.available_repos = True
|
||||
demands.resolving = True
|
||||
demands.root_user = True
|
||||
+ demands.clean_command_lock = CleanCommandLock.READ
|
||||
|
||||
def run_on_repo(self):
|
||||
"""Execute the command with respect to given arguments *cli_args*."""
|
||||
diff --git a/dnf/cli/commands/clean.py b/dnf/cli/commands/clean.py
|
||||
index 77f83f02..df14ebd7 100644
|
||||
--- a/dnf/cli/commands/clean.py
|
||||
+++ b/dnf/cli/commands/clean.py
|
||||
@@ -21,6 +21,7 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
from dnf.cli import commands
|
||||
+from dnf.cli.demand import CleanCommandLock
|
||||
from dnf.i18n import _, P_
|
||||
from dnf.yum import misc
|
||||
|
||||
@@ -92,6 +93,9 @@ class CleanCommand(commands.Command):
|
||||
choices=_CACHE_TYPES.keys(),
|
||||
help=_('Metadata type to clean'))
|
||||
|
||||
+ def configure(self):
|
||||
+ self.cli.demands.clean_command_lock = CleanCommandLock.WRITE
|
||||
+
|
||||
def run(self):
|
||||
cachedir = self.base.conf.cachedir
|
||||
md_lock = dnf.lock.build_metadata_lock(cachedir, True)
|
||||
diff --git a/dnf/cli/commands/distrosync.py b/dnf/cli/commands/distrosync.py
|
||||
index 3d472e5f..7896f61f 100644
|
||||
--- a/dnf/cli/commands/distrosync.py
|
||||
+++ b/dnf/cli/commands/distrosync.py
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
from __future__ import absolute_import
|
||||
from dnf.cli import commands
|
||||
+from dnf.cli.demand import CleanCommandLock
|
||||
from dnf.i18n import _
|
||||
|
||||
|
||||
@@ -41,6 +42,7 @@ class DistroSyncCommand(commands.Command):
|
||||
demands.available_repos = True
|
||||
demands.resolving = True
|
||||
demands.root_user = True
|
||||
+ demands.clean_command_lock = CleanCommandLock.READ
|
||||
commands._checkGPGKey(self.base, self.cli)
|
||||
commands._checkEnabledRepo(self.base, self.opts.package)
|
||||
|
||||
diff --git a/dnf/cli/commands/downgrade.py b/dnf/cli/commands/downgrade.py
|
||||
index 9e27962b..187e56d5 100644
|
||||
--- a/dnf/cli/commands/downgrade.py
|
||||
+++ b/dnf/cli/commands/downgrade.py
|
||||
@@ -21,6 +21,7 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
from dnf.cli import commands
|
||||
+from dnf.cli.demand import CleanCommandLock
|
||||
from dnf.cli.option_parser import OptionParser
|
||||
from dnf.i18n import _
|
||||
|
||||
@@ -44,6 +45,7 @@ class DowngradeCommand(commands.Command):
|
||||
demands.available_repos = True
|
||||
demands.resolving = True
|
||||
demands.root_user = True
|
||||
+ demands.clean_command_lock = CleanCommandLock.READ
|
||||
|
||||
commands._checkGPGKey(self.base, self.cli)
|
||||
if not self.opts.filenames:
|
||||
diff --git a/dnf/cli/commands/group.py b/dnf/cli/commands/group.py
|
||||
index 6de8baa1..9375f81d 100644
|
||||
--- a/dnf/cli/commands/group.py
|
||||
+++ b/dnf/cli/commands/group.py
|
||||
@@ -22,6 +22,7 @@ from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
from dnf.comps import CompsQuery
|
||||
from dnf.cli import commands
|
||||
+from dnf.cli.demand import CleanCommandLock
|
||||
from dnf.i18n import _, ucd
|
||||
|
||||
import libdnf.transaction
|
||||
@@ -363,6 +364,7 @@ class GroupCommand(commands.Command):
|
||||
|
||||
if cmd in ('install', 'upgrade'):
|
||||
commands._checkGPGKey(self.base, self.cli)
|
||||
+ demands.clean_command_lock = CleanCommandLock.READ
|
||||
|
||||
def run(self):
|
||||
cmd = self.opts.subcmd
|
||||
diff --git a/dnf/cli/commands/history.py b/dnf/cli/commands/history.py
|
||||
index 21d04a1a..d01858b3 100644
|
||||
--- a/dnf/cli/commands/history.py
|
||||
+++ b/dnf/cli/commands/history.py
|
||||
@@ -25,6 +25,7 @@ import hawkey
|
||||
from dnf.i18n import _, ucd
|
||||
from dnf.cli import commands
|
||||
from dnf.transaction_sr import TransactionReplay, serialize_transaction
|
||||
+from dnf.cli.demand import CleanCommandLock
|
||||
|
||||
import dnf.cli
|
||||
import dnf.exceptions
|
||||
@@ -110,6 +111,7 @@ class HistoryCommand(commands.Command):
|
||||
demands.available_repos = True
|
||||
demands.resolving = True
|
||||
demands.root_user = True
|
||||
+ demands.clean_command_lock = CleanCommandLock.READ
|
||||
|
||||
# Override configuration options that affect how the transaction is resolved
|
||||
self.base.conf.clean_requirements_on_remove = False
|
||||
@@ -124,6 +126,7 @@ class HistoryCommand(commands.Command):
|
||||
demands.available_repos = True
|
||||
demands.resolving = True
|
||||
demands.root_user = True
|
||||
+ demands.clean_command_lock = CleanCommandLock.READ
|
||||
|
||||
self._require_one_transaction_id = True
|
||||
if not self.opts.transactions:
|
||||
diff --git a/dnf/cli/commands/install.py b/dnf/cli/commands/install.py
|
||||
index e13cc669..59bc2991 100644
|
||||
--- a/dnf/cli/commands/install.py
|
||||
+++ b/dnf/cli/commands/install.py
|
||||
@@ -28,6 +28,7 @@ import hawkey
|
||||
|
||||
import dnf.exceptions
|
||||
from dnf.cli import commands
|
||||
+from dnf.cli.demand import CleanCommandLock
|
||||
from dnf.cli.option_parser import OptionParser
|
||||
from dnf.i18n import _
|
||||
|
||||
@@ -62,6 +63,7 @@ class InstallCommand(commands.Command):
|
||||
demands.available_repos = True
|
||||
demands.resolving = True
|
||||
demands.root_user = True
|
||||
+ demands.clean_command_lock = CleanCommandLock.READ
|
||||
commands._checkGPGKey(self.base, self.cli)
|
||||
if not self.opts.filenames:
|
||||
commands._checkEnabledRepo(self.base)
|
||||
diff --git a/dnf/cli/commands/module.py b/dnf/cli/commands/module.py
|
||||
index 0f584f90..9b55de80 100644
|
||||
--- a/dnf/cli/commands/module.py
|
||||
+++ b/dnf/cli/commands/module.py
|
||||
@@ -20,6 +20,7 @@
|
||||
from __future__ import print_function
|
||||
|
||||
from dnf.cli import commands, CliError
|
||||
+from dnf.cli.demand import CleanCommandLock
|
||||
from dnf.i18n import _
|
||||
from dnf.module.exceptions import NoModuleException
|
||||
from dnf.util import logger
|
||||
@@ -210,6 +211,7 @@ class ModuleCommand(commands.Command):
|
||||
demands.sack_activation = True
|
||||
demands.resolving = True
|
||||
demands.root_user = True
|
||||
+ demands.clean_command_lock = CleanCommandLock.READ
|
||||
|
||||
def run_on_module(self):
|
||||
try:
|
||||
@@ -231,6 +233,7 @@ class ModuleCommand(commands.Command):
|
||||
demands.sack_activation = True
|
||||
demands.resolving = True
|
||||
demands.root_user = True
|
||||
+ demands.clean_command_lock = CleanCommandLock.READ
|
||||
|
||||
def run_on_module(self):
|
||||
module_specs = self.module_base.upgrade(self.opts.module_spec)
|
||||
@@ -285,6 +288,7 @@ class ModuleCommand(commands.Command):
|
||||
demands.sack_activation = True
|
||||
demands.resolving = True
|
||||
demands.root_user = True
|
||||
+ demands.clean_command_lock = CleanCommandLock.READ
|
||||
self.base.conf.module_stream_switch = True
|
||||
|
||||
def run_on_module(self):
|
||||
diff --git a/dnf/cli/commands/reinstall.py b/dnf/cli/commands/reinstall.py
|
||||
index 2b3ceac7..9d95cea0 100644
|
||||
--- a/dnf/cli/commands/reinstall.py
|
||||
+++ b/dnf/cli/commands/reinstall.py
|
||||
@@ -21,6 +21,7 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
from dnf.cli import commands
|
||||
+from dnf.cli.demand import CleanCommandLock
|
||||
from dnf.cli.option_parser import OptionParser
|
||||
from dnf.i18n import _
|
||||
|
||||
@@ -54,6 +55,7 @@ class ReinstallCommand(commands.Command):
|
||||
demands.available_repos = True
|
||||
demands.resolving = True
|
||||
demands.root_user = True
|
||||
+ demands.clean_command_lock = CleanCommandLock.READ
|
||||
commands._checkGPGKey(self.base, self.cli)
|
||||
if not self.opts.filenames:
|
||||
commands._checkEnabledRepo(self.base)
|
||||
diff --git a/dnf/cli/commands/shell.py b/dnf/cli/commands/shell.py
|
||||
index 18c886ff..f2a1bf32 100644
|
||||
--- a/dnf/cli/commands/shell.py
|
||||
+++ b/dnf/cli/commands/shell.py
|
||||
@@ -19,6 +19,7 @@
|
||||
#
|
||||
|
||||
from dnf.cli import commands
|
||||
+from dnf.cli.demand import CleanCommandLock
|
||||
from dnf.i18n import _, ucd
|
||||
|
||||
import dnf.util
|
||||
@@ -39,6 +40,7 @@ class ShellDemandSheet(object):
|
||||
resolving = True
|
||||
root_user = True
|
||||
sack_activation = True
|
||||
+ clean_command_lock = CleanCommandLock.READ
|
||||
|
||||
|
||||
class ShellCommand(commands.Command, cmd.Cmd):
|
||||
diff --git a/dnf/cli/commands/swap.py b/dnf/cli/commands/swap.py
|
||||
index d44b3f4f..e61a24dc 100644
|
||||
--- a/dnf/cli/commands/swap.py
|
||||
+++ b/dnf/cli/commands/swap.py
|
||||
@@ -20,6 +20,7 @@ from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
from dnf.i18n import _
|
||||
from dnf.cli import commands
|
||||
+from dnf.cli.demand import CleanCommandLock
|
||||
|
||||
import dnf.util
|
||||
import logging
|
||||
@@ -47,6 +48,7 @@ class SwapCommand(commands.Command):
|
||||
demands.available_repos = True
|
||||
demands.resolving = True
|
||||
demands.root_user = True
|
||||
+ demands.clean_command_lock = CleanCommandLock.READ
|
||||
commands._checkGPGKey(self.base, self.cli)
|
||||
commands._checkEnabledRepo(self.base, [self.opts.install_spec])
|
||||
|
||||
diff --git a/dnf/cli/commands/upgrade.py b/dnf/cli/commands/upgrade.py
|
||||
index 15e63136..fea0cb2d 100644
|
||||
--- a/dnf/cli/commands/upgrade.py
|
||||
+++ b/dnf/cli/commands/upgrade.py
|
||||
@@ -26,6 +26,7 @@ import logging
|
||||
import dnf.exceptions
|
||||
import dnf.base
|
||||
from dnf.cli import commands
|
||||
+from dnf.cli.demand import CleanCommandLock
|
||||
from dnf.cli.option_parser import OptionParser
|
||||
from dnf.i18n import _
|
||||
|
||||
@@ -56,6 +57,7 @@ class UpgradeCommand(commands.Command):
|
||||
demands.available_repos = True
|
||||
demands.resolving = True
|
||||
demands.root_user = True
|
||||
+ demands.clean_command_lock = CleanCommandLock.READ
|
||||
commands._checkGPGKey(self.base, self.cli)
|
||||
if not self.opts.filenames:
|
||||
commands._checkEnabledRepo(self.base)
|
||||
--
|
||||
2.54.0
|
||||
|
||||
@ -73,7 +73,7 @@ It supports RPMs, modules and comps groups & environments.
|
||||
|
||||
Name: dnf
|
||||
Version: 4.14.0
|
||||
Release: 33%{?dist}.alma.1
|
||||
Release: 37%{?dist}.alma.1
|
||||
Summary: %{pkg_summary}
|
||||
# For a breakdown of the licensing, see PACKAGE-LICENSING
|
||||
License: GPLv2+
|
||||
@ -148,6 +148,14 @@ Patch66: 0066-Document-how-releasever-releasever_-major-minor-affe.patch
|
||||
Patch67: 0067-Move-releasever_minor-setter-docstring-to-the-correc.patch
|
||||
Patch68: 0068-automatic-Expand-email_to-in-command_email-emitter-t.patch
|
||||
Patch69: 0069-autoremove-warn-and-skip-dangling-protected-dependen.patch
|
||||
Patch70: 0070-bootc-unlock-only-if-usr-is-read-only.patch
|
||||
Patch71: 0071-bootc-Call-make_writable-when-DeploymentUnlockedStat.patch
|
||||
Patch72: 0072-Preserve-ACL-when-rotating-logs.patch
|
||||
Patch73: 0073-history-info-Fix-Persistence-display-for-interval-qu.patch
|
||||
Patch74: 0074-Add-FileLock-for-read-write-and-blocking-non-blockin.patch
|
||||
Patch75: 0075-Generalize-_BoolDefault-to-just-_Default-allow-choic.patch
|
||||
Patch76: 0076-Add-clean-command-lock-demand.patch
|
||||
Patch77: 0077-Configure-demands.clean_command_lock-for-commands-th.patch
|
||||
|
||||
# AlmaLinux Patch
|
||||
Patch10000: almalinux_bugtracker.patch
|
||||
@ -457,9 +465,21 @@ popd
|
||||
# bootc subpackage does not include any files
|
||||
|
||||
%changelog
|
||||
* Mon Mar 30 2026 Eduard Abdullin <eabdullin@almalinux.org> - 4.14.0-33.alma.1
|
||||
* Mon Aug 24 2026 Eduard Abdullin <eabdullin@almalinux.org> - 4.14.0-37.alma.1
|
||||
- Added patch for almalinux bugtracker
|
||||
|
||||
* Tue Jul 14 2026 Ales Matej <amatej@redhat.com> - 4.14.0-37
|
||||
- Add dedicated lock for clean command (RHEL-83124)
|
||||
|
||||
* Wed May 20 2026 Evan Goode <egoode@redhat.com> - 4.14.0-36
|
||||
- history info: Fix Persistence display for interval queries (RHEL-154737)
|
||||
|
||||
* Thu Mar 26 2026 Petr Pisar <ppisar@redhat.com> - 4.14.0-35
|
||||
- Preserve ACL when rotating logs (RHEL-122013)
|
||||
|
||||
* Wed Mar 25 2026 Evan Goode <egoode@redhat.com> - 4.14.0-34
|
||||
- bootc: unlock only if /usr is read-only (RHEL-138512)
|
||||
|
||||
* Wed Feb 11 2026 Ales Matej <amatej@redhat.com> - 4.14.0-33
|
||||
- autoremove: when a dangling protected dependency is found produce a wanrning
|
||||
and skip it (RHEL-76112)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user