diff --git a/borgmatic/actions/config/bootstrap.py b/borgmatic/actions/config/bootstrap.py index 7ece946f..2f5acbd4 100644 --- a/borgmatic/actions/config/bootstrap.py +++ b/borgmatic/actions/config/bootstrap.py @@ -81,26 +81,25 @@ def run_bootstrap(bootstrap_arguments, global_arguments, local_borg_version): bootstrap_arguments, global_arguments, local_borg_version ) - for config_path in manifest_config_paths: - logger.info(f'Bootstrapping config path {config_path}') + logger.info(f"Bootstrapping config paths: {', '.join(manifest_config_paths)}") - borgmatic.borg.extract.extract_archive( - global_arguments.dry_run, + borgmatic.borg.extract.extract_archive( + global_arguments.dry_run, + bootstrap_arguments.repository, + borgmatic.borg.rlist.resolve_archive_name( bootstrap_arguments.repository, - borgmatic.borg.rlist.resolve_archive_name( - bootstrap_arguments.repository, - bootstrap_arguments.archive, - {}, - local_borg_version, - global_arguments, - ), - [config_path], - {}, + bootstrap_arguments.archive, {}, local_borg_version, global_arguments, - extract_to_stdout=False, - destination_path=bootstrap_arguments.destination, - strip_components=bootstrap_arguments.strip_components, - progress=bootstrap_arguments.progress, - ) + ), + [config_path.lstrip(os.path.sep) for config_path in manifest_config_paths], + {}, + {}, + local_borg_version, + global_arguments, + extract_to_stdout=False, + destination_path=bootstrap_arguments.destination, + strip_components=bootstrap_arguments.strip_components, + progress=bootstrap_arguments.progress, + ) diff --git a/borgmatic/commands/borgmatic.py b/borgmatic/commands/borgmatic.py index 79aae78b..c670291f 100644 --- a/borgmatic/commands/borgmatic.py +++ b/borgmatic/commands/borgmatic.py @@ -612,26 +612,8 @@ def collect_configuration_run_summary_logs(configs, arguments): As a side effect of running through these configuration files, output their JSON results, if any, to stdout. ''' - # Run cross-file validation checks. - repository = None - - for action_name, action_arguments in arguments.items(): - if hasattr(action_arguments, 'repository'): - repository = getattr(action_arguments, 'repository') - break - - try: - if 'extract' in arguments or 'mount' in arguments: - validate.guard_single_repository_selected(repository, configs) - - if 'bootstrap' not in arguments: - validate.guard_configuration_contains_repository(repository, configs) - except ValueError as error: - yield from log_error_records(str(error)) - return - if 'bootstrap' in arguments: - # no configuration file is needed for bootstrap + # No configuration file is needed for bootstrap. local_borg_version = borg_version.local_borg_version({}, 'borg') try: borgmatic.actions.config.bootstrap.run_bootstrap( @@ -653,6 +635,23 @@ def collect_configuration_run_summary_logs(configs, arguments): return + # Run cross-file validation checks. + repository = None + + for action_name, action_arguments in arguments.items(): + if hasattr(action_arguments, 'repository'): + repository = getattr(action_arguments, 'repository') + break + + try: + if 'extract' in arguments or 'mount' in arguments: + validate.guard_single_repository_selected(repository, configs) + + validate.guard_configuration_contains_repository(repository, configs) + except ValueError as error: + yield from log_error_records(str(error)) + return + if not configs: yield from log_error_records( f"{' '.join(arguments['global'].config_paths)}: No valid configuration files found", diff --git a/borgmatic/config/collect.py b/borgmatic/config/collect.py index bd38fee2..80c6c5c0 100644 --- a/borgmatic/config/collect.py +++ b/borgmatic/config/collect.py @@ -24,9 +24,9 @@ def get_default_config_paths(expand_home=True): def collect_config_filenames(config_paths): ''' Given a sequence of config paths, both filenames and directories, resolve that to an iterable - of files. Accomplish this by listing any given directories looking for contained config files - (ending with the ".yaml" or ".yml" extension). This is non-recursive, so any directories within the given - directories are ignored. + of absolute files. Accomplish this by listing any given directories looking for contained config + files (ending with the ".yaml" or ".yml" extension). This is non-recursive, so any directories + within the given directories are ignored. Return paths even if they don't exist on disk, so the user can find out about missing configuration paths. However, skip a default config path if it's missing, so the user doesn't @@ -41,7 +41,7 @@ def collect_config_filenames(config_paths): continue if not os.path.isdir(path) or not exists: - yield path + yield os.path.abspath(path) continue if not os.access(path, os.R_OK): @@ -51,4 +51,4 @@ def collect_config_filenames(config_paths): full_filename = os.path.join(path, filename) matching_filetype = full_filename.endswith('.yaml') or full_filename.endswith('.yml') if matching_filetype and not os.path.isdir(full_filename): - yield full_filename + yield os.path.abspath(full_filename) diff --git a/tests/unit/config/test_collect.py b/tests/unit/config/test_collect.py index 5bab1ae0..d20c006f 100644 --- a/tests/unit/config/test_collect.py +++ b/tests/unit/config/test_collect.py @@ -29,15 +29,6 @@ def test_get_default_config_paths_does_not_expand_home_when_false(): assert '$HOME/.config/borgmatic/config.yaml' in config_paths -def test_collect_config_filenames_collects_given_files(): - config_paths = ('config.yaml', 'other.yaml') - flexmock(module.os.path).should_receive('isdir').and_return(False) - - config_filenames = tuple(module.collect_config_filenames(config_paths)) - - assert config_filenames == config_paths - - def test_collect_config_filenames_collects_yml_file_endings(): config_paths = ('config.yaml', '/etc/borgmatic.d') mock_path = flexmock(module.os.path) @@ -45,13 +36,14 @@ def test_collect_config_filenames_collects_yml_file_endings(): mock_path.should_receive('isdir').with_args('config.yaml').and_return(False) mock_path.should_receive('isdir').with_args('/etc/borgmatic.d').and_return(True) mock_path.should_receive('isdir').with_args('/etc/borgmatic.d/foo.yml').and_return(False) + mock_path.should_receive('abspath').replace_with(lambda path: module.os.path.join('/', path)) flexmock(module.os).should_receive('access').and_return(True) flexmock(module.os).should_receive('listdir') flexmock(sys.modules['builtins']).should_receive('sorted').and_return(['foo.yml']) config_filenames = tuple(module.collect_config_filenames(config_paths)) - assert config_filenames == ('config.yaml', '/etc/borgmatic.d/foo.yml') + assert config_filenames == ('/config.yaml', '/etc/borgmatic.d/foo.yml') def test_collect_config_filenames_collects_files_from_given_directories_and_ignores_sub_directories(): @@ -63,6 +55,7 @@ def test_collect_config_filenames_collects_files_from_given_directories_and_igno mock_path.should_receive('isdir').with_args('/etc/borgmatic.d/foo.yaml').and_return(False) mock_path.should_receive('isdir').with_args('/etc/borgmatic.d/bar').and_return(True) mock_path.should_receive('isdir').with_args('/etc/borgmatic.d/baz.yaml').and_return(False) + mock_path.should_receive('abspath').replace_with(lambda path: module.os.path.join('/', path)) flexmock(module.os).should_receive('access').and_return(True) flexmock(module.os).should_receive('listdir') flexmock(sys.modules['builtins']).should_receive('sorted').and_return( @@ -72,7 +65,7 @@ def test_collect_config_filenames_collects_files_from_given_directories_and_igno config_filenames = tuple(module.collect_config_filenames(config_paths)) assert config_filenames == ( - 'config.yaml', + '/config.yaml', '/etc/borgmatic.d/foo.yaml', '/etc/borgmatic.d/baz.yaml', ) @@ -86,6 +79,7 @@ def test_collect_config_filenames_collects_files_from_given_directories_and_igno mock_path.should_receive('isdir').with_args('/etc/borgmatic.d/foo.yaml').and_return(False) mock_path.should_receive('isdir').with_args('/etc/borgmatic.d/bar.yaml~').and_return(False) mock_path.should_receive('isdir').with_args('/etc/borgmatic.d/baz.txt').and_return(False) + mock_path.should_receive('abspath').replace_with(lambda path: module.os.path.join('/', path)) flexmock(module.os).should_receive('access').and_return(True) flexmock(module.os).should_receive('listdir') flexmock(sys.modules['builtins']).should_receive('sorted').and_return( @@ -103,13 +97,14 @@ def test_collect_config_filenames_skips_permission_denied_directories(): mock_path.should_receive('exists').and_return(True) mock_path.should_receive('isdir').with_args('config.yaml').and_return(False) mock_path.should_receive('isdir').with_args('/etc/borgmatic.d').and_return(True) + mock_path.should_receive('abspath').replace_with(lambda path: module.os.path.join('/', path)) flexmock(module.os).should_receive('access').and_return(False) flexmock(module.os).should_receive('listdir') flexmock(sys.modules['builtins']).should_receive('sorted').and_return(['config.yaml']) config_filenames = tuple(module.collect_config_filenames(config_paths)) - assert config_filenames == ('config.yaml',) + assert config_filenames == ('/config.yaml',) def test_collect_config_filenames_skips_etc_borgmatic_config_dot_yaml_if_it_does_not_exist(): @@ -119,10 +114,11 @@ def test_collect_config_filenames_skips_etc_borgmatic_config_dot_yaml_if_it_does mock_path.should_receive('exists').with_args('/etc/borgmatic/config.yaml').and_return(False) mock_path.should_receive('isdir').with_args('config.yaml').and_return(False) mock_path.should_receive('isdir').with_args('/etc/borgmatic/config.yaml').and_return(True) + mock_path.should_receive('abspath').replace_with(lambda path: module.os.path.join('/', path)) config_filenames = tuple(module.collect_config_filenames(config_paths)) - assert config_filenames == ('config.yaml',) + assert config_filenames == ('/config.yaml',) def test_collect_config_filenames_skips_etc_borgmatic_dot_d_if_it_does_not_exist(): @@ -132,10 +128,11 @@ def test_collect_config_filenames_skips_etc_borgmatic_dot_d_if_it_does_not_exist mock_path.should_receive('exists').with_args('/etc/borgmatic.d').and_return(False) mock_path.should_receive('isdir').with_args('config.yaml').and_return(False) mock_path.should_receive('isdir').with_args('/etc/borgmatic.d').and_return(True) + mock_path.should_receive('abspath').replace_with(lambda path: module.os.path.join('/', path)) config_filenames = tuple(module.collect_config_filenames(config_paths)) - assert config_filenames == ('config.yaml',) + assert config_filenames == ('/config.yaml',) def test_collect_config_filenames_skips_non_canonical_etc_borgmatic_dot_d_if_it_does_not_exist(): @@ -145,10 +142,11 @@ def test_collect_config_filenames_skips_non_canonical_etc_borgmatic_dot_d_if_it_ mock_path.should_receive('exists').with_args('/etc/../etc/borgmatic.d').and_return(False) mock_path.should_receive('isdir').with_args('config.yaml').and_return(False) mock_path.should_receive('isdir').with_args('/etc/../etc/borgmatic.d').and_return(True) + mock_path.should_receive('abspath').replace_with(lambda path: module.os.path.join('/', path)) config_filenames = tuple(module.collect_config_filenames(config_paths)) - assert config_filenames == ('config.yaml',) + assert config_filenames == ('/config.yaml',) def test_collect_config_filenames_includes_other_directory_if_it_does_not_exist(): @@ -158,7 +156,8 @@ def test_collect_config_filenames_includes_other_directory_if_it_does_not_exist( mock_path.should_receive('exists').with_args('/my/directory').and_return(False) mock_path.should_receive('isdir').with_args('config.yaml').and_return(False) mock_path.should_receive('isdir').with_args('/my/directory').and_return(True) + mock_path.should_receive('abspath').replace_with(lambda path: module.os.path.join('/', path)) config_filenames = tuple(module.collect_config_filenames(config_paths)) - assert config_filenames == config_paths + assert config_filenames == ('/config.yaml', '/my/directory')