diff --git a/src/tufup/repo/__init__.py b/src/tufup/repo/__init__.py index ad7b919..f75a5f8 100644 --- a/src/tufup/repo/__init__.py +++ b/src/tufup/repo/__init__.py @@ -953,6 +953,7 @@ def threshold_sign( def _load_keys_and_roles(self, create_keys: bool = False): # todo: make public, rename load_keys_and_metadata + # todo: check import success (now says "metadata imported" even if files not found) if self.keys is None: logger.info('Importing public keys...') self.keys = Keys( diff --git a/src/tufup/utils/platform_specific.py b/src/tufup/utils/platform_specific.py index 2cd1c02..fbb6bb6 100644 --- a/src/tufup/utils/platform_specific.py +++ b/src/tufup/utils/platform_specific.py @@ -239,8 +239,14 @@ def _install_update_mac( dst_dir: Union[pathlib.Path, str], purge_dst_dir: bool, exclude_from_purge: List[Union[pathlib.Path, str]], + symlinks: bool = False, **kwargs, ): + """ + The symlinks arg is passed on to shutil.copytree() + + [1]: https://docs.python.org/3/library/shutil.html#shutil.copytree + """ # todo: implement as_admin and debug kwargs for mac logger.debug(f'Kwargs not used: {kwargs}') if purge_dst_dir: @@ -256,7 +262,7 @@ def _install_update_mac( if path not in exclude_from_purge: remove_path(path=path) logger.debug(f'Moving content of {src_dir} to {dst_dir}.') - shutil.copytree(src_dir, dst_dir, dirs_exist_ok=True) + shutil.copytree(src_dir, dst_dir, dirs_exist_ok=True, symlinks=symlinks) # Note: the src_dir is typically a temporary directory, but we'll clear # it anyway just to be consistent with the windows implementation for path in pathlib.Path(src_dir).iterdir(): diff --git a/tests/test_utils_platform_specific.py b/tests/test_utils_platform_specific.py index 8adf033..ff188b7 100644 --- a/tests/test_utils_platform_specific.py +++ b/tests/test_utils_platform_specific.py @@ -7,9 +7,12 @@ import textwrap from time import sleep import unittest +from unittest.mock import patch from tests import BASE_DIR, TempDirTestCase +import tufup.utils.platform_specific as ps from tufup.utils.platform_specific import ( + ON_MAC, ON_WINDOWS, PLATFORM_SUPPORTED, run_bat_as_admin, @@ -91,6 +94,14 @@ def test_run_bat_as_admin(self): self.assertTrue(len(output)) self.assertNotIn(current_user, output) + @unittest.skipIf(condition=not ON_MAC, reason='macOS only') + def test_install_update_macos_symlinks(self): + with patch.object(ps, '_install_update_mac') as mock_install_update_mac: + ps.install_update(src_dir='', dst_dir='') + self.assertNotIn('symlinks', mock_install_update_mac.call_args.kwargs) + ps.install_update(src_dir='', dst_dir='', symlinks=True) + self.assertTrue(mock_install_update_mac.call_args.kwargs['symlinks']) + @unittest.skipIf( condition=not PLATFORM_SUPPORTED, reason=_reason_platform_not_supported )