diff --git a/tests/core/environment-precedence/data/.fmf/version b/tests/core/environment-precedence/data/.fmf/version new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/core/environment-precedence/data/.fmf/version @@ -0,0 +1 @@ +1 diff --git a/tests/core/environment-precedence/data/dump-envvars.sh b/tests/core/environment-precedence/data/dump-envvars.sh new file mode 100755 index 0000000000..2f6a5861e5 --- /dev/null +++ b/tests/core/environment-precedence/data/dump-envvars.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +LINE_PREFIX="${LINE_PREFIX:-}" + +for line in $(env -0 | sort -z | tr '\0' '\n'); do echo "${LINE_PREFIX}: ${line}"; done diff --git a/tests/core/environment-precedence/data/main.fmf b/tests/core/environment-precedence/data/main.fmf new file mode 100644 index 0000000000..fe739385fc --- /dev/null +++ b/tests/core/environment-precedence/data/main.fmf @@ -0,0 +1,62 @@ +# In general, we need variables that are defined by multiple sources, +# and we observe who overwrites who. We also need variables that stay +# not overwritten, so we could verify all sources contributed. +# +# VAR1: claimed by `test[].environment` +# VAR2: claimed by `plan.environment-file` key +# VAR3: claimed by `plan.environment` key +# VAR4: claimed by `provision[].environment` key +# VAR5: claimed by plan environment file +# VAR6: claimed by `tmt run --environment-file` CLI option +# VAR7: claimed by `tmt run --environment` CLI option + +/test: + test: LINE_PREFIX=execute.0 ./dump-envvars.sh + + environment: + VAR1: test.environment + VAR2: test.environment + VAR3: test.environment + VAR4: test.environment + VAR5: test.environment + VAR6: test.environment + VAR7: test.environment + +/plan: + environment: + VAR3: plan.environment + VAR4: plan.environment + VAR5: plan.environment + VAR6: plan.environment + VAR7: plan.environment + + environment-file: + - plan-environment.env + + discover: + how: fmf + + provision: + how: local + environment: + VAR4: provision.environment + VAR5: provision.environment + VAR6: provision.environment + VAR7: provision.environment + + prepare: + - how: shell + script: LINE_PREFIX=prepare.0 ./dump-envvars.sh + + - how: shell + script: ./populate-plan-environment-file.sh + + - how: shell + - script: LINE_PREFIX=prepare.2 ./dump-envvars.sh + + execute: + how: tmt + + finish: + how: shell + script: LINE_PREFIX=finish.0 ./dump-envvars.sh diff --git a/tests/core/environment-precedence/data/plan-environment.env b/tests/core/environment-precedence/data/plan-environment.env new file mode 100644 index 0000000000..c3d86d7c37 --- /dev/null +++ b/tests/core/environment-precedence/data/plan-environment.env @@ -0,0 +1,6 @@ +VAR2=plan.environment-file +VAR3=plan.environment-file +VAR4=plan.environment-file +VAR5=plan.environment-file +VAR6=plan.environment-file +VAR7=plan.environment-file diff --git a/tests/core/environment-precedence/data/populate-plan-environment-file.sh b/tests/core/environment-precedence/data/populate-plan-environment-file.sh new file mode 100755 index 0000000000..cc779e7bcb --- /dev/null +++ b/tests/core/environment-precedence/data/populate-plan-environment-file.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +echo "VAR5=plan.plan-environment-file" >> $TMT_PLAN_ENVIRONMENT_FILE +echo "VAR6=plan.plan-environment-file" >> $TMT_PLAN_ENVIRONMENT_FILE +echo "VAR7=plan.plan-environment-file" >> $TMT_PLAN_ENVIRONMENT_FILE diff --git a/tests/core/environment-precedence/data/run-environment.env b/tests/core/environment-precedence/data/run-environment.env new file mode 100644 index 0000000000..908b79c0f9 --- /dev/null +++ b/tests/core/environment-precedence/data/run-environment.env @@ -0,0 +1,2 @@ +VAR6=cli.run.environment-file +VAR7=cli.run.environment-file diff --git a/tests/core/environment-precedence/main.fmf b/tests/core/environment-precedence/main.fmf new file mode 100644 index 0000000000..9fd32ce7d4 --- /dev/null +++ b/tests/core/environment-precedence/main.fmf @@ -0,0 +1,4 @@ +summary: Verify environment variable precedence +description: | + Verify whether various sources of environment variables are correctly + incorporated into the final environment. diff --git a/tests/core/environment-precedence/test.sh b/tests/core/environment-precedence/test.sh new file mode 100755 index 0000000000..511c6d3af6 --- /dev/null +++ b/tests/core/environment-precedence/test.sh @@ -0,0 +1,48 @@ +#!/bin/bash +. /usr/share/beakerlib/beakerlib.sh || exit 1 + +rlJournalStart + rlPhaseStartSetup + rlRun 'pushd data' + rlPhaseEnd + + rlPhaseStartTest 'Check environment variable precedence' + rlRun -s "tmt -vvv --feeling-safe run -a --environment VAR7=cli.run.environment --environment-file run-environment.env" + + rlAssertNotGrep "prepare.0: VAR1=test.environment" $rlRun_LOG + rlAssertGrep "prepare.0: VAR2=plan.environment-file" $rlRun_LOG + rlAssertGrep "prepare.0: VAR3=plan.environment" $rlRun_LOG + rlAssertGrep "prepare.0: VAR4=provision.environment" $rlRun_LOG + rlAssertGrep "prepare.0: VAR5=provision.environment" $rlRun_LOG + rlAssertGrep "prepare.0: VAR6=cli.run.environment-file" $rlRun_LOG + rlAssertGrep "prepare.0: VAR7=cli.run.environment" $rlRun_LOG + + rlAssertNotGrep "prepare.2: VAR1=test.environment" $rlRun_LOG + rlAssertGrep "prepare.2: VAR2=plan.environment-file" $rlRun_LOG + rlAssertGrep "prepare.2: VAR3=plan.environment" $rlRun_LOG + rlAssertGrep "prepare.2: VAR4=provision.environment" $rlRun_LOG + rlAssertGrep "prepare.2: VAR5=plan.plan-environment-file" $rlRun_LOG + rlAssertGrep "prepare.2: VAR6=cli.run.environment-file" $rlRun_LOG + rlAssertGrep "prepare.2: VAR7=cli.run.environment" $rlRun_LOG + + rlAssertGrep "execute.0: VAR1=test.environment" $rlRun_LOG + rlAssertGrep "execute.0: VAR2=plan.environment-file" $rlRun_LOG + rlAssertGrep "execute.0: VAR3=plan.environment" $rlRun_LOG + rlAssertGrep "execute.0: VAR4=provision.environment" $rlRun_LOG + rlAssertGrep "execute.0: VAR5=plan.plan-environment-file" $rlRun_LOG + rlAssertGrep "execute.0: VAR6=cli.run.environment-file" $rlRun_LOG + rlAssertGrep "execute.0: VAR7=cli.run.environment" $rlRun_LOG + + rlAssertNotGrep "finish.0: VAR1=test.environment" $rlRun_LOG + rlAssertGrep "finish.0: VAR2=plan.environment-file" $rlRun_LOG + rlAssertGrep "finish.0: VAR3=plan.environment" $rlRun_LOG + rlAssertGrep "finish.0: VAR4=provision.environment" $rlRun_LOG + rlAssertGrep "finish.0: VAR5=plan.plan-environment-file" $rlRun_LOG + rlAssertGrep "finish.0: VAR6=cli.run.environment-file" $rlRun_LOG + rlAssertGrep "finish.0: VAR7=cli.run.environment" $rlRun_LOG + rlPhaseEnd + + rlPhaseStartCleanup + rlRun 'popd' + rlPhaseEnd +rlJournalEnd diff --git a/tests/core/plan-env-file/data/main.fmf b/tests/core/plan-env-file/data/main.fmf index e81d7d79e1..160486a33f 100644 --- a/tests/core/plan-env-file/data/main.fmf +++ b/tests/core/plan-env-file/data/main.fmf @@ -22,20 +22,6 @@ how: shell script: ./setup_env.sh - /env-file-override: - prepare: - how: shell - script: echo "MYVAR1=MYVAR1_VALUE" >> $TMT_PLAN_ENVIRONMENT_FILE - environment-file: - - override.env - - /env-override: - prepare: - how: shell - script: echo "MYVAR1=MYVAR1_VALUE" >> $TMT_PLAN_ENVIRONMENT_FILE - environment: - MYVAR1: MYVAR1_OVERRIDE - /execute-override: discover: how: shell diff --git a/tests/core/plan-env-file/main.fmf b/tests/core/plan-env-file/main.fmf index cfed34bb0e..3afba98f07 100644 --- a/tests/core/plan-env-file/main.fmf +++ b/tests/core/plan-env-file/main.fmf @@ -2,5 +2,4 @@ summary: Verify proper application of TMT_PLAN_ENVIRONMENT_FILE variables description: Verify proper application of environment variables provided by 'TMT_PLAN_ENVIRONMENT_FILE' and that they are available - during the execute and finish phase. Also, confirm that the - variables have a lower priority than the 'environment-file' key. + during the execute and finish phase. diff --git a/tests/core/plan-env-file/test.sh b/tests/core/plan-env-file/test.sh index 6486054f17..8554625aeb 100755 --- a/tests/core/plan-env-file/test.sh +++ b/tests/core/plan-env-file/test.sh @@ -25,29 +25,6 @@ rlJournalStart rlAssertGrep 'total: 1 test passed' $rlRun_LOG rlPhaseEnd - rlPhaseStartTest 'Check environment variable is overridden by "environment-file" key' - rlRun -s 'tmt run -rvvv plan --name /plan/env-file-override' - rlAssertGrep 'MYVAR1="MYVAR1_OVERRIDE"' $rlRun_LOG - rlAssertGrep 'FINISH1="MYVAR1_OVERRIDE"' $rlRun_LOG - rlAssertGrep 'total: 1 test passed' $rlRun_LOG - rlPhaseEnd - - rlPhaseStartTest 'Check environment variable is overridden by "environment" key' - rlRun -s 'tmt run -rvvv plan --name /plan/env-override' - rlAssertGrep 'MYVAR1="MYVAR1_OVERRIDE"' $rlRun_LOG - rlAssertGrep 'FINISH1="MYVAR1_OVERRIDE"' $rlRun_LOG - rlAssertGrep 'total: 1 test passed' $rlRun_LOG - rlPhaseEnd - - rlPhaseStartTest 'Check environment variable is overridden by command line' - rlRun -s 'tmt run -rvvv -e MYVAR1=MYVAR1_OVERRIDE plan --name /plan/multiple' - rlAssertGrep 'MYVAR1="MYVAR1_OVERRIDE"' $rlRun_LOG - rlAssertGrep 'MYVAR2="MYVAR2_VALUE"' $rlRun_LOG - rlAssertGrep 'FINISH1="MYVAR1_OVERRIDE"' $rlRun_LOG - rlAssertGrep 'FINISH2="MYVAR2_VALUE"' $rlRun_LOG - rlAssertGrep 'total: 1 test passed' $rlRun_LOG - rlPhaseEnd - rlPhaseStartTest 'Check "execute" step can override environment variable' rlRun -s 'tmt run -rvvv plan --name /plan/execute-override' rlAssertGrep 'FINISH1="MYVAR1_OVERRIDE"' $rlRun_LOG diff --git a/tmt/base/plan.py b/tmt/base/plan.py index bd5c661a8e..9eaf46cc43 100644 --- a/tmt/base/plan.py +++ b/tmt/base/plan.py @@ -336,11 +336,9 @@ def __init__( self._initialize_worktree() # Expand all environment and context variables in the node - environment = Environment() - - environment.update(self.environment, self.intrinsic_environment) - - with environment.as_environ(): + with Environment.build_environment( + plan=self, run=self.my_run, logger=self._logger + ).as_environ(): expand_node_data(node.data, self.fmf_context) # pyright: ignore[reportUnknownVariableType, reportUnknownArgumentType] # Initialize test steps @@ -549,7 +547,6 @@ def environment(self) -> Environment: **self._environment_from_fmf, **self._environment_from_importing, **self._environment_from_cli, - **self.my_run.environment, } ) diff --git a/tmt/guest/__init__.py b/tmt/guest/__init__.py index cc0d5cb85d..afdda98cdf 100644 --- a/tmt/guest/__init__.py +++ b/tmt/guest/__init__.py @@ -2263,17 +2263,12 @@ def _prepare_command_environment( """ if environment is None: - environment = Environment() - - environment.update(self.environment) - - if isinstance(self.parent, tmt.steps.Step): - environment.update(self.parent.plan.environment) - - environment.update(self.intrinsic_environment) - - if isinstance(self.parent, tmt.steps.Step): - environment.update(self.parent.plan.intrinsic_environment) + environment = Environment.build_environment( + plan=self.parent.plan if isinstance(self.parent, tmt.steps.Step) else None, + run=self.parent.plan.my_run if isinstance(self.parent, tmt.steps.Step) else None, + guest=self, + logger=self._logger, + ) else: # Create a copy of given environment - this prevents any diff --git a/tmt/steps/context/__init__.py b/tmt/steps/context/__init__.py index a94c3fec59..67f7b360b1 100644 --- a/tmt/steps/context/__init__.py +++ b/tmt/steps/context/__init__.py @@ -1,5 +1,9 @@ +import abc from typing import TYPE_CHECKING, Optional +from tmt.container import container +from tmt.utils.environment import HasIntrinsicEnvironment + if TYPE_CHECKING: from tmt.steps.context.reboot import RebootContext from tmt.steps.context.restart import RestartContext @@ -15,3 +19,8 @@ def is_guest_healthy( return False return True + + +@container +class StepContext(HasIntrinsicEnvironment, abc.ABC): + pass diff --git a/tmt/steps/context/abort.py b/tmt/steps/context/abort.py index bc7edd21d8..6b2e45831d 100644 --- a/tmt/steps/context/abort.py +++ b/tmt/steps/context/abort.py @@ -4,8 +4,9 @@ import tmt.steps.scripts import tmt.utils from tmt.container import container +from tmt.steps.context import StepContext from tmt.utils import Path -from tmt.utils.environment import Environment, HasIntrinsicEnvironment +from tmt.utils.environment import Environment class AbortStep(tmt.utils.GeneralError): @@ -15,7 +16,7 @@ class AbortStep(tmt.utils.GeneralError): @container -class AbortContext(HasIntrinsicEnvironment): +class AbortContext(StepContext): """ Provides API for handling a phase-requested abort of a step. """ diff --git a/tmt/steps/context/pidfile.py b/tmt/steps/context/pidfile.py index 1b42425233..da1265a783 100644 --- a/tmt/steps/context/pidfile.py +++ b/tmt/steps/context/pidfile.py @@ -83,8 +83,9 @@ from tmt.container import container from tmt.guest import Guest, TransferOptions from tmt.steps import safe_filename +from tmt.steps.context import StepContext from tmt.utils import Path, ShellScript -from tmt.utils.environment import Environment, EnvVarValue, HasIntrinsicEnvironment +from tmt.utils.environment import Environment, EnvVarValue from tmt.utils.templates import render_template TEST_PIDFILE_FILENAME = 'tmt-test.pid' @@ -193,7 +194,7 @@ def effective_pidfile_root() -> Path: @container -class PidFileContext(HasIntrinsicEnvironment): +class PidFileContext(StepContext): #: Phase owning this context. phase: tmt.steps.BasePlugin[Any, Any] diff --git a/tmt/steps/context/reboot.py b/tmt/steps/context/reboot.py index ae2a456ed9..71940cad44 100644 --- a/tmt/steps/context/reboot.py +++ b/tmt/steps/context/reboot.py @@ -8,8 +8,9 @@ import tmt.utils from tmt.container import MetadataContainer, container from tmt.guest import Guest, RebootMode, SoftRebootModes +from tmt.steps.context import StepContext from tmt.utils import Path, ShellScript -from tmt.utils.environment import Environment, EnvVarValue, HasIntrinsicEnvironment +from tmt.utils.environment import Environment, EnvVarValue from tmt.utils.wait import Deadline, Waiting if TYPE_CHECKING: @@ -27,7 +28,7 @@ class RebootData(MetadataContainer): @container -class RebootContext(HasIntrinsicEnvironment): +class RebootContext(StepContext): """ Tracks information about guest reboots. """ diff --git a/tmt/steps/context/restart.py b/tmt/steps/context/restart.py index 3a55bc8a29..834c73712f 100644 --- a/tmt/steps/context/restart.py +++ b/tmt/steps/context/restart.py @@ -4,14 +4,15 @@ import tmt.utils from tmt.container import container from tmt.guest import Guest -from tmt.utils.environment import Environment, EnvVarValue, HasIntrinsicEnvironment +from tmt.steps.context import StepContext +from tmt.utils.environment import Environment, EnvVarValue if TYPE_CHECKING: from tmt.steps.context.reboot import RebootContext @container -class RestartContext(HasIntrinsicEnvironment): +class RestartContext(StepContext): """ Tracks information about restarts of an action, e.g. a test script. """ diff --git a/tmt/steps/context/restraint.py b/tmt/steps/context/restraint.py index 8519dd91dc..22364b3c01 100644 --- a/tmt/steps/context/restraint.py +++ b/tmt/steps/context/restraint.py @@ -2,11 +2,12 @@ import tmt.log from tmt.container import container -from tmt.utils.environment import Environment, EnvVarValue, HasIntrinsicEnvironment +from tmt.steps.context import StepContext +from tmt.utils.environment import Environment, EnvVarValue @container -class RestraintContext(HasIntrinsicEnvironment): +class RestraintContext(StepContext): """ Provides restraint-related context for execution. """ diff --git a/tmt/steps/execute/__init__.py b/tmt/steps/execute/__init__.py index 2670edd256..ad9df401ad 100644 --- a/tmt/steps/execute/__init__.py +++ b/tmt/steps/execute/__init__.py @@ -392,26 +392,23 @@ def intrinsic_environment(self) -> Environment: @property def environment(self) -> Environment: if self._environment is None: - # narrow type - assert isinstance(self.phase.step.plan.my_run, tmt.base.run.Run) - - environment = Environment() - - environment.update( - self.guest.environment, - self.test.environment, - self.guest.plan_environment, - self.phase.step.plan.environment, + self._environment = Environment.build_environment( + test=self.test, + plan=self.phase.step.plan, + run=self.phase.step.plan.my_run, + guest=self.guest, + test_invocation=self, + logger=self.logger, ) - else: - environment = self._environment - - environment.update(self.intrinsic_environment) - - self._environment = environment - - return environment + return self._environment.refresh_intrinsics( + test=self.test, + plan=self.phase.step.plan, + run=self.phase.step.plan.my_run, + guest=self.guest, + test_invocation=self, + logger=self.logger, + ) def invoke_check(self, event: CheckEvent, check: Check) -> list[CheckResult]: results, exc, timer = Stopwatch.measure( diff --git a/tmt/steps/prepare/shell.py b/tmt/steps/prepare/shell.py index cbe3d2aba1..75725e63ec 100644 --- a/tmt/steps/prepare/shell.py +++ b/tmt/steps/prepare/shell.py @@ -144,11 +144,11 @@ def go( phase=self, guest=guest, logger=logger ) - environment = environment or Environment() - environment.update( - guest.environment, - guest.plan_environment, - self.step.plan.environment, + environment = Environment.build_environment( + plan=self.step.plan, + run=self.step.plan.my_run, + guest=guest, + logger=logger, ) # Give a short summary @@ -261,12 +261,12 @@ def _invoke_script( script_log_filepath.parent.mkdir(parents=True, exist_ok=True) script_log_filepath.touch() - script_environment = environment.copy() - script_environment.update( - self.step.plan.intrinsic_environment, - guest.intrinsic_environment, - reboot_context.intrinsic_environment, - pidfile_context.intrinsic_environment, + script_environment = environment.copy().refresh_intrinsics( + plan=self.step.plan, + run=self.step.plan.my_run, + guest=guest, + step_contexts=(reboot_context, pidfile_context), + logger=logger, ) pull_options = DEFAULT_PULL_OPTIONS.copy() diff --git a/tmt/steps/provision/local.py b/tmt/steps/provision/local.py index b1c8d625c6..37cb33c8e8 100644 --- a/tmt/steps/provision/local.py +++ b/tmt/steps/provision/local.py @@ -70,20 +70,18 @@ def _prepare_command_environment( if environment is None: environment = Environment.from_environ() - environment.update(self.environment) - - if isinstance(self.parent, tmt.steps.Step): - environment.update(self.parent.plan.environment) - - environment.update(self.intrinsic_environment) - - if isinstance(self.parent, tmt.steps.Step): - environment.update(self.parent.plan.intrinsic_environment) + environment.update( + Environment.build_environment( + plan=self.parent.plan if self.parent else None, + run=self.parent.plan.my_run if self.parent else None, + guest=self, + logger=self._logger, + ) + ) - else: - environment = super()._prepare_command_environment(environment=environment) + return environment - return environment + return super()._prepare_command_environment(environment=environment) def _run_ansible( self, diff --git a/tmt/steps/provision/podman.py b/tmt/steps/provision/podman.py index 96b2f29f87..e7d05cb52f 100644 --- a/tmt/steps/provision/podman.py +++ b/tmt/steps/provision/podman.py @@ -252,7 +252,9 @@ def _setup_environment(self) -> list[str]: assert isinstance(self.parent, Provision) # narrow type - environment = self.parent.plan.environment + environment = Environment.build_environment( + plan=self.parent.plan, run=self.parent.plan.my_run, guest=self, logger=self._logger + ) if not environment: return [] diff --git a/tmt/utils/environment.py b/tmt/utils/environment.py index 69e61476e7..cda6ae6361 100644 --- a/tmt/utils/environment.py +++ b/tmt/utils/environment.py @@ -42,6 +42,12 @@ if TYPE_CHECKING: from tmt._compat.typing import TypeAlias + from tmt.base.core import Test + from tmt.base.plan import Plan + from tmt.base.run import Run + from tmt.guest import Guest + from tmt.steps.context import StepContext + from tmt.steps.execute import TestInvocation from tmt.utils import FmfContext, ShellScript @@ -634,3 +640,128 @@ def as_environ(self) -> Generator[None]: finally: os.environ.clear() os.environ.update(environ_backup) + + @classmethod + def _build_environment( + cls, + *, + base_environment: Optional[Self] = None, + test: Optional['Test'] = None, + plan: Optional['Plan'] = None, + run: Optional['Run'] = None, + guest: Optional['Guest'] = None, + test_invocation: Optional['TestInvocation'] = None, + step_contexts: Optional[Sequence['StepContext']] = None, + intrinsic_only: bool = False, + logger: tmt.log.Logger, + ) -> Self: + """ + Build environment from various sources, in the correct order. + + :param base_environment: if set, add variables to this + environment. Otherwise, new empty one is used. + :param intrinsic_only: add intrinsic environment variables only, + user-owned variables would not be included. + """ + + base_environment = base_environment or cls() + + # 1. test[].environment + if not intrinsic_only and test is not None: + base_environment.update(test.environment) + + # 2. plan.environment-file + # 3. plan.environment + # 4. importing plan "native" environment + if not intrinsic_only and plan is not None: + base_environment.update(plan.environment) + + # 5. provision[].environment + if not intrinsic_only and guest is not None: + base_environment.update(guest.environment) + + # 6. $TMT_PLAN_ENVIRONMENT_FILE + if not intrinsic_only and guest is not None: + base_environment.update(guest.plan_environment) + + # 7. command-line input + # 7.1 `tmt run` + if not intrinsic_only and run is not None: + base_environment.update(run.environment) + + # 7.2 `tmt * export` - TODO + # 7.3 `tmt try` - TODO + + # 8. intrinsic variables + if test is not None and test_invocation is not None: + base_environment.update( + test.test_framework.get_environment_variables(test_invocation, logger) + ) + + if plan is not None: + base_environment.update(plan.intrinsic_environment) + + if guest is not None: + base_environment.update(guest.intrinsic_environment) + + if test_invocation is not None: + base_environment.update(test_invocation.intrinsic_environment) + + for context in step_contexts or []: + base_environment.update(context.intrinsic_environment) + + return base_environment + + @classmethod + def build_environment( + cls, + *, + test: Optional['Test'] = None, + plan: Optional['Plan'] = None, + run: Optional['Run'] = None, + guest: Optional['Guest'] = None, + test_invocation: Optional['TestInvocation'] = None, + step_contexts: Optional[Sequence['StepContext']] = None, + logger: tmt.log.Logger, + ) -> Self: + """ + Build environment from various sources, in the correct order. + """ + + return cls._build_environment( + test=test, + plan=plan, + run=run, + guest=guest, + test_invocation=test_invocation, + step_contexts=step_contexts, + intrinsic_only=False, + logger=logger, + ) + + def refresh_intrinsics( + self, + *, + test: Optional['Test'] = None, + plan: Optional['Plan'] = None, + run: Optional['Run'] = None, + guest: Optional['Guest'] = None, + test_invocation: Optional['TestInvocation'] = None, + step_contexts: Optional[Sequence['StepContext']] = None, + logger: tmt.log.Logger, + ) -> Self: + """ + Refresh intrinsic variables from various sources. + """ + + return self._build_environment( + base_environment=self, + test=test, + plan=plan, + run=run, + guest=guest, + test_invocation=test_invocation, + step_contexts=step_contexts, + intrinsic_only=True, + logger=logger, + )