diff --git a/kiwi/defaults.py b/kiwi/defaults.py index fc7607e476e..274c20db9fa 100644 --- a/kiwi/defaults.py +++ b/kiwi/defaults.py @@ -96,6 +96,9 @@ PLATFORM_MACHINE = platform.machine() EFI_FAT_IMAGE_SIZE = 20 +# optional package manager environment variables +PACKAGE_MANAGER_ENV_VARS = '/.kiwi.package_manager.env' + log = logging.getLogger('kiwi') diff --git a/kiwi/exceptions.py b/kiwi/exceptions.py index 63f8e85fdf9..7f91736d355 100644 --- a/kiwi/exceptions.py +++ b/kiwi/exceptions.py @@ -444,6 +444,13 @@ class KiwiOSReleaseImportError(KiwiError): """ +class KiwiEnvImportError(KiwiError): + """ + Exception raised if extending os.environ with another + env file caused an issue + """ + + class KiwiPackageManagerSetupError(KiwiError): """ Exception raised if an attempt was made to create a package diff --git a/kiwi/repository/apt.py b/kiwi/repository/apt.py index 529f9d00a40..54896e64cc2 100644 --- a/kiwi/repository/apt.py +++ b/kiwi/repository/apt.py @@ -21,6 +21,7 @@ from typing import List, Dict # project +import kiwi.defaults as defaults from kiwi.utils.temporary import ( Temporary, TmpT ) @@ -28,6 +29,7 @@ from kiwi.repository.base import RepositoryBase from kiwi.path import Path from kiwi.command import Command +from kiwi.utils.toenv import ToEnv log = logging.getLogger('kiwi') @@ -306,6 +308,7 @@ def _add_components(self, components: str) -> None: def _create_apt_get_runtime_environment(self) -> Dict: for apt_get_dir in list(self.shared_apt_get_dir.values()): Path.create(apt_get_dir) + ToEnv(self.root_dir, defaults.PACKAGE_MANAGER_ENV_VARS) return dict( os.environ, LANG='C', DEBIAN_FRONTEND='noninteractive' ) diff --git a/kiwi/repository/dnf4.py b/kiwi/repository/dnf4.py index 5136ccaff4f..a3cf44dd883 100644 --- a/kiwi/repository/dnf4.py +++ b/kiwi/repository/dnf4.py @@ -21,6 +21,7 @@ from typing import List, Dict # project +import kiwi.defaults as defaults from kiwi.utils.temporary import ( Temporary, TmpT ) @@ -29,6 +30,7 @@ from kiwi.repository.base import RepositoryBase from kiwi.path import Path from kiwi.utils.rpm_database import RpmDataBase +from kiwi.utils.toenv import ToEnv class RepositoryDnf4(RepositoryBase): @@ -315,6 +317,7 @@ def cleanup_unused_repos(self) -> None: def _create_dnf_runtime_environment(self) -> Dict: for dnf_dir in list(self.shared_dnf_dir.values()): Path.create(dnf_dir) + ToEnv(self.root_dir, defaults.PACKAGE_MANAGER_ENV_VARS) return dict( os.environ, LANG='C' ) diff --git a/kiwi/repository/dnf5.py b/kiwi/repository/dnf5.py index 45b8cef3db1..d49e6b2cfaa 100644 --- a/kiwi/repository/dnf5.py +++ b/kiwi/repository/dnf5.py @@ -21,6 +21,7 @@ from typing import List, Dict # project +import kiwi.defaults as defaults from kiwi.utils.temporary import ( Temporary, TmpT ) @@ -29,6 +30,7 @@ from kiwi.repository.base import RepositoryBase from kiwi.path import Path from kiwi.utils.rpm_database import RpmDataBase +from kiwi.utils.toenv import ToEnv class RepositoryDnf5(RepositoryBase): @@ -315,6 +317,7 @@ def cleanup_unused_repos(self) -> None: def _create_dnf_runtime_environment(self) -> Dict: for dnf_dir in list(self.shared_dnf_dir.values()): Path.create(dnf_dir) + ToEnv(self.root_dir, defaults.PACKAGE_MANAGER_ENV_VARS) return dict( os.environ, LANG='C' ) diff --git a/kiwi/repository/pacman.py b/kiwi/repository/pacman.py index 60946ab757d..9059bff5435 100644 --- a/kiwi/repository/pacman.py +++ b/kiwi/repository/pacman.py @@ -22,12 +22,14 @@ ) # project +import kiwi.defaults as defaults from kiwi.utils.temporary import ( Temporary, TmpT ) from kiwi.repository.base import RepositoryBase from kiwi.path import Path from kiwi.command import Command +from kiwi.utils.toenv import ToEnv class RepositoryPacman(RepositoryBase): @@ -90,6 +92,7 @@ def runtime_config(self) -> Dict: """ pacman runtime configuration and environment """ + ToEnv(self.root_dir, defaults.PACKAGE_MANAGER_ENV_VARS) return { 'pacman_args': self.pacman_args, 'command_env': os.environ diff --git a/kiwi/repository/zypper.py b/kiwi/repository/zypper.py index 4fc4a117f5e..2bedc3de647 100644 --- a/kiwi/repository/zypper.py +++ b/kiwi/repository/zypper.py @@ -21,6 +21,7 @@ from typing import List, Dict # project +import kiwi.defaults as defaults from kiwi.utils.temporary import ( Temporary, TmpT ) @@ -30,6 +31,7 @@ from kiwi.system.uri import Uri from kiwi.path import Path from kiwi.utils.rpm_database import RpmDataBase +from kiwi.utils.toenv import ToEnv class RepositoryZypper(RepositoryBase): @@ -427,6 +429,7 @@ def cleanup_unused_repos(self) -> None: def _create_zypper_runtime_environment(self) -> Dict: for zypper_dir in list(self.shared_zypper_dir.values()): Path.create(zypper_dir) + ToEnv(self.root_dir, defaults.PACKAGE_MANAGER_ENV_VARS) return dict( os.environ, LANG='C', diff --git a/kiwi/utils/toenv.py b/kiwi/utils/toenv.py new file mode 100644 index 00000000000..af8c0c4de36 --- /dev/null +++ b/kiwi/utils/toenv.py @@ -0,0 +1,59 @@ +# Copyright (c) 2024 SUSE Software Solutions Germany GmbH. All rights reserved. +# +# This file is part of kiwi. +# +# kiwi is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# kiwi is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with kiwi. If not, see +# +import csv +import os +from io import TextIOWrapper +from typing import Iterable + +# project +from kiwi.exceptions import KiwiEnvImportError + + +class ToEnv: + """ + **Read env file and merge with os.environ** + """ + def __init__(self, root_dir: str, envfile: str): + self.data = {} + env_file = f'{root_dir}/{envfile}' + if os.path.isfile(env_file): + try: + with open(env_file) as osdata: + reader = csv.reader(ToEnv._rip(osdata), delimiter='=') + self.data = dict(reader) + except Exception as issue: + raise KiwiEnvImportError( + f'Import of {envfile} failed with {issue}' + ) + if self.data: + os.environ.update(self.data) + + @staticmethod + def _is_comment(line: str) -> bool: + return line.startswith('#') + + @staticmethod + def _is_whitespace(line: str) -> bool: + return line.isspace() + + @staticmethod + def _rip(csvfile: TextIOWrapper) -> Iterable[str]: + for row in csvfile: + if not ToEnv._is_comment(row) \ + and not ToEnv._is_whitespace(row): + yield row diff --git a/test/data/some.env b/test/data/some.env new file mode 100644 index 00000000000..87e24d4ecef --- /dev/null +++ b/test/data/some.env @@ -0,0 +1 @@ +ZYPP_MODALIAS_SYSFS="" diff --git a/test/data/some_broken.env b/test/data/some_broken.env new file mode 100644 index 00000000000..63fc8131d56 --- /dev/null +++ b/test/data/some_broken.env @@ -0,0 +1 @@ +xxxx diff --git a/test/unit/utils/toenv_test.py b/test/unit/utils/toenv_test.py new file mode 100644 index 00000000000..db6dbf1a57e --- /dev/null +++ b/test/unit/utils/toenv_test.py @@ -0,0 +1,18 @@ +from pytest import raises +import os + +from kiwi.utils.toenv import ToEnv +from kiwi.exceptions import KiwiEnvImportError + + +class TestToEnv(object): + def setup(self): + ToEnv('../data', 'some.env') + assert os.environ['ZYPP_MODALIAS_SYSFS'] == '' + + def setup_method(self, cls): + self.setup() + + def test_setup_raises(self): + with raises(KiwiEnvImportError): + ToEnv('../data', 'some_broken.env')