Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 57 additions & 1 deletion tmt/package_managers/bootc.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import re
import uuid
from collections.abc import Iterator
from typing import Any, Optional
from typing import TYPE_CHECKING, Any, Optional

if TYPE_CHECKING:
from tmt._compat.typing import TypeAlias

Repository: TypeAlias = Any
else:
Repository: Any = None # type: ignore[assignment]

import tmt.utils
from tmt.container import PYDANTIC_V1, ConfigDict, MetadataContainer
Expand Down Expand Up @@ -381,6 +388,55 @@ def install_local(

return CommandOutput(stdout=None, stderr=None)

def enable_copr(self, *repositories: str) -> None:
"""
Enable COPR repositories on an image mode guest.

Installs the copr plugin via the Containerfile mechanism
(``/usr`` is read-only on image mode) and then runs
``dnf copr enable`` on the live system which writes the
``.repo`` file to ``/etc/yum.repos.d/``.
"""
if not repositories:
return

from tmt.package_managers import Package

# Install the copr plugin through bootc (Containerfile + rebuild).
# The bootc_builder is always a Dnf subclass with copr_plugin attribute.
copr_plugin: str = getattr(self.guest.bootc_builder, 'copr_plugin', 'dnf-plugins-core')
self.install(Package(copr_plugin))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The self.install() call triggers a guest reboot. The subsequent loop to enable COPR repositories runs on the old guest instance before the reboot, not on the new guest where the copr plugin would be available.

Manually sequence the plugin installation and guest reboot to ensure the repository enabling commands are executed on the new, updated guest.

Suggested change
self.install(Package(copr_plugin))
copr_plugin_pkg = Package(copr_plugin)
if not self.check_presence(copr_plugin_pkg).get(copr_plugin_pkg, False):
self.engine.install(copr_plugin_pkg)
self.build_container()


# Enable each repository on the live system
for repository in repositories:
self.info('copr', repository, 'green')
self.guest.execute(
ShellScript(
f"{self.engine.aux_engine.command.to_script()} copr "
f"{self.engine.aux_engine.options.to_script()} enable -y {repository}"
)
)

def create_repository(self, directory: Path) -> CommandOutput:
"""
Create repository metadata by delegating to the underlying engine.

Runs on the live system, writing to the workdir which is writable
in image mode.
"""
return self.guest.execute(self.engine.aux_engine.create_repository(directory))

def install_repository(self, repository: Repository) -> CommandOutput:
"""
Install a repository by delegating to the underlying engine.

Writes the ``.repo`` file to ``/etc/yum.repos.d/`` which persists
in image mode. Refreshes the package cache on the live system
without triggering a container rebuild.
"""
self.guest.execute(self.engine.aux_engine.install_repository(repository))
return self.guest.execute(self.engine.aux_engine.refresh_metadata())

def finalize_installation(self) -> CommandOutput:
"""
Coordinate installation process through containerfile building and switching
Expand Down