diff --git a/borgmatic/borg/extract.py b/borgmatic/borg/extract.py index 5097421c..52d7c35f 100644 --- a/borgmatic/borg/extract.py +++ b/borgmatic/borg/extract.py @@ -134,9 +134,7 @@ def extract_archive( # Make the repository path absolute so the destination directory used below via changing # the working directory doesn't prevent Borg from finding the repo. But also apply the # user's configured working directory (if any) to the repo path. - borgmatic.config.validate.normalize_repository_path( - os.path.join(working_directory or '', repository) - ), + borgmatic.config.validate.normalize_repository_path(repository, working_directory), archive, local_borg_version, ) diff --git a/borgmatic/config/validate.py b/borgmatic/config/validate.py index c80cee4d..0334e301 100644 --- a/borgmatic/config/validate.py +++ b/borgmatic/config/validate.py @@ -138,16 +138,22 @@ def parse_configuration(config_filename, schema_filename, overrides=None, resolv return config, config_paths, logs -def normalize_repository_path(repository): +def normalize_repository_path(repository, base=None): ''' Given a repository path, return the absolute path of it (for local repositories). + Optionally, use a base path for resolving relative paths, e.g. to the configured working directory. ''' # A colon in the repository could mean that it's either a file:// URL or a remote repository. # If it's a remote repository, we don't want to normalize it. If it's a file:// URL, we do. if ':' not in repository: - return os.path.abspath(repository) + return ( + os.path.abspath(os.path.join(base, repository)) if base else os.path.abspath(repository) + ) elif repository.startswith('file://'): - return os.path.abspath(repository.partition('file://')[-1]) + local_path = repository.partition('file://')[-1] + return ( + os.path.abspath(os.path.join(base, local_path)) if base else os.path.abspath(local_path) + ) else: return repository diff --git a/tests/unit/borg/test_extract.py b/tests/unit/borg/test_extract.py index db244e54..de392f24 100644 --- a/tests/unit/borg/test_extract.py +++ b/tests/unit/borg/test_extract.py @@ -710,7 +710,7 @@ def test_extract_archive_uses_configured_working_directory_in_repo_path_and_dest ) flexmock(module.borgmatic.config.validate).should_receive( 'normalize_repository_path' - ).with_args('/working/dir/repo').and_return('/working/dir/repo').once() + ).with_args('repo', '/working/dir').and_return('/working/dir/repo').once() module.extract_archive( dry_run=False, @@ -733,7 +733,7 @@ def test_extract_archive_uses_configured_working_directory_in_repo_path_when_des ) flexmock(module.borgmatic.config.validate).should_receive( 'normalize_repository_path' - ).with_args('/working/dir/repo').and_return('/working/dir/repo').once() + ).with_args('repo', '/working/dir').and_return('/working/dir/repo').once() module.extract_archive( dry_run=False, diff --git a/tests/unit/config/test_validate.py b/tests/unit/config/test_validate.py index 8490bd07..04b5bb40 100644 --- a/tests/unit/config/test_validate.py +++ b/tests/unit/config/test_validate.py @@ -94,13 +94,40 @@ def test_normalize_repository_path_passes_through_remote_repository(): module.normalize_repository_path(repository) == repository +def test_normalize_repository_path_passes_through_remote_repository_with_base_dir(): + repository = 'example.org:test.borg' + + flexmock(module.os.path).should_receive('abspath').never() + module.normalize_repository_path(repository, '/working') == repository + + def test_normalize_repository_path_passes_through_file_repository(): repository = 'file:///foo/bar/test.borg' - flexmock(module.os.path).should_receive('abspath').and_return('/foo/bar/test.borg') + flexmock(module.os.path).should_receive('abspath').with_args('/foo/bar/test.borg').and_return( + '/foo/bar/test.borg' + ) module.normalize_repository_path(repository) == '/foo/bar/test.borg' +def test_normalize_repository_path_passes_through_absolute_file_repository_with_base_dir(): + repository = 'file:///foo/bar/test.borg' + flexmock(module.os.path).should_receive('abspath').with_args('/foo/bar/test.borg').and_return( + '/foo/bar/test.borg' + ) + + module.normalize_repository_path(repository, '/working') == '/foo/bar/test.borg' + + +def test_normalize_repository_path_resolves_relative_file_repository_with_base_dir(): + repository = 'file://foo/bar/test.borg' + flexmock(module.os.path).should_receive('abspath').with_args( + '/working/foo/bar/test.borg' + ).and_return('/working/foo/bar/test.borg') + + module.normalize_repository_path(repository, '/working') == '/working/foo/bar/test.borg' + + def test_normalize_repository_path_passes_through_absolute_repository(): repository = '/foo/bar/test.borg' flexmock(module.os.path).should_receive('abspath').and_return(repository) @@ -108,14 +135,32 @@ def test_normalize_repository_path_passes_through_absolute_repository(): module.normalize_repository_path(repository) == repository +def test_normalize_repository_path_passes_through_absolute_repository_with_base_dir(): + repository = '/foo/bar/test.borg' + flexmock(module.os.path).should_receive('abspath').and_return(repository) + + module.normalize_repository_path(repository, '/working') == repository + + def test_normalize_repository_path_resolves_relative_repository(): repository = 'test.borg' absolute = '/foo/bar/test.borg' - flexmock(module.os.path).should_receive('abspath').and_return(absolute) + flexmock(module.os.path).should_receive('abspath').with_args(repository).and_return(absolute) module.normalize_repository_path(repository) == absolute +def test_normalize_repository_path_resolves_relative_repository_with_base_dir(): + repository = 'test.borg' + base = '/working' + absolute = '/working/test.borg' + flexmock(module.os.path).should_receive('abspath').with_args('/working/test.borg').and_return( + absolute + ) + + module.normalize_repository_path(repository, base) == absolute + + @pytest.mark.parametrize( 'first,second,expected_result', (