Skip to content

Commit c8f36c6

Browse files
authored
Fix finish/shell scripts never running in image mode (#5125)
This PR fixes `finish/shell` scripts being skipped on image-mode guests. Finish scripts now run immediately, `prepare/shell` is unchanged. Fixes #4917
1 parent 2b59267 commit c8f36c6

5 files changed

Lines changed: 60 additions & 9 deletions

File tree

docs/releases/pending/4917.fmf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
description: |
2+
The :ref:`finish shell</plugins/finish/shell>` plugin now runs
3+
scripts immediately on image-mode guests instead of collecting
4+
them for deferred execution that never happened.

tests/prepare/shell/test.sh

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,7 @@ rlJournalStart
6161
rlPhaseStartTest "Remote Script"
6262
rlRun -s "tmt -vvv run provision --how=$PROVISION_HOW $image_opt prepare finish cleanup plan -n url" 0 "Prepare using a remote script"
6363
rlAssertGrep "Hello world" "$rlRun_LOG" #check for the prepare script
64-
# TODO: #4785 Preparing from a remote script is broken in Image Mode (finish)
65-
# https://github.com/teemtee/tmt/issues/4917
66-
if [ "$IMAGE_MODE" != "yes" ]; then
67-
rlAssertGrep "third" "$rlRun_LOG" # check for the finish script
68-
fi
64+
rlAssertGrep "third" "$rlRun_LOG" # check for the finish script
6965
assert_image_mode
7066
rlPhaseEnd
7167
done <<< "$IMAGES"

tmt/guest/__init__.py

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2533,6 +2533,26 @@ def execute(
25332533
) -> Optional[tmt.utils.CommandOutput]:
25342534
pass
25352535

2536+
@overload
2537+
def execute(
2538+
self,
2539+
command: Union[tmt.utils.Command, tmt.utils.ShellScript],
2540+
cwd: Optional[Path] = None,
2541+
environment: Optional[Environment] = None,
2542+
friendly_command: Optional[str] = None,
2543+
test_session: bool = False,
2544+
immediately: bool = True,
2545+
tty: bool = False,
2546+
silent: bool = False,
2547+
log: Optional[tmt.log.VerboseLoggingFunction] = None,
2548+
interactive: bool = False,
2549+
on_process_start: Optional[OnProcessStartCallback] = None,
2550+
on_process_end: Optional[OnProcessEndCallback] = None,
2551+
sourced_files: Optional[list[Path]] = None,
2552+
**kwargs: Any,
2553+
) -> Optional[tmt.utils.CommandOutput]:
2554+
pass
2555+
25362556
@abc.abstractmethod
25372557
def execute(
25382558
self,
@@ -2550,19 +2570,24 @@ def execute(
25502570
on_process_end: Optional[OnProcessEndCallback] = None,
25512571
sourced_files: Optional[list[Path]] = None,
25522572
**kwargs: Any,
2553-
) -> tmt.utils.CommandOutput:
2573+
) -> Optional[tmt.utils.CommandOutput]:
25542574
"""
25552575
Execute a command on the guest.
25562576
25572577
:param command: either a command or a shell script to execute.
2558-
:param cwd: if set, execute command in this directory on the guest.
2578+
:param cwd: execute command in this directory on the guest.
25592579
:param environment: if set, set these environment variables before running the command.
25602580
:param friendly_command: nice, human-friendly representation of the command.
2581+
:param test_session: if True, this is the actual test being run.
25612582
:param immediately: if False, the command may be collected for later
25622583
batch execution on guests that support it (e.g., bootc guests).
25632584
Commands with ``immediately=True`` (default) are always executed
25642585
right away. Use ``immediately=False`` for commands that modify
25652586
system state and can be batched (e.g., package installation).
2587+
When a command is deferred, ``None`` is returned instead of
2588+
:py:class:`CommandOutput`.
2589+
:returns: command output, or ``None`` if the command was deferred
2590+
for batch execution (when ``immediately=False`` on supported guests).
25662591
"""
25672592

25682593
raise NotImplementedError
@@ -3768,6 +3793,26 @@ def execute(
37683793
) -> Optional[tmt.utils.CommandOutput]:
37693794
pass
37703795

3796+
@overload
3797+
def execute(
3798+
self,
3799+
command: Union[tmt.utils.Command, tmt.utils.ShellScript],
3800+
cwd: Optional[Path] = None,
3801+
environment: Optional[Environment] = None,
3802+
friendly_command: Optional[str] = None,
3803+
test_session: bool = False,
3804+
immediately: bool = True,
3805+
tty: bool = False,
3806+
silent: bool = False,
3807+
log: Optional[tmt.log.VerboseLoggingFunction] = None,
3808+
interactive: bool = False,
3809+
on_process_start: Optional[OnProcessStartCallback] = None,
3810+
on_process_end: Optional[OnProcessEndCallback] = None,
3811+
sourced_files: Optional[list[Path]] = None,
3812+
**kwargs: Any,
3813+
) -> Optional[tmt.utils.CommandOutput]:
3814+
pass
3815+
37713816
def execute(
37723817
self,
37733818
command: Union[tmt.utils.Command, tmt.utils.ShellScript],

tmt/steps/finish/shell.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ class FinishShell(tmt.steps.finish.FinishPlugin[tmt.steps.finish.FinishStepData]
4444

4545
_cloned_repo_path_envvar_name = "TMT_FINISH_SHELL_URL_REPOSITORY"
4646

47+
# Finish scripts must run immediately rather than being deferred.
48+
_execute_immediately = True
49+
4750
# We are reusing "prepare" step for "finish",
4851
# and they both have different expectations.
4952
# Mypy is not happy about this though.

tmt/steps/prepare/shell.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import threading
2-
from typing import Any, Optional, cast
2+
from typing import Any, ClassVar, Optional, cast
33

44
import fmf.utils
55

@@ -109,6 +109,9 @@ class PrepareShell(tmt.steps.prepare.PreparePlugin[PrepareShellData]):
109109
_url_clone_lock = threading.Lock()
110110
_cloned_repo_path_envvar_name = 'TMT_PREPARE_SHELL_URL_REPOSITORY'
111111

112+
#: Whether to run scripts immediately or defer them when the guest supports it.
113+
_execute_immediately: ClassVar[bool] = False
114+
112115
@property
113116
def _preserved_workdir_members(self) -> set[str]:
114117
return {
@@ -248,7 +251,7 @@ def _invoke_script(
248251
cwd=worktree,
249252
environment=environment,
250253
sourced_files=[self.step.plan.plan_source_script],
251-
immediately=False,
254+
immediately=self._execute_immediately,
252255
)
253256

254257
script_queue = self.data.script[:]

0 commit comments

Comments
 (0)