From 2bb818402d4f4caeb1ff93e322cc329e07563bbd Mon Sep 17 00:00:00 2001 From: Dan Helfman Date: Thu, 15 Jan 2026 22:16:41 -0800 Subject: [PATCH] Fix the "recreate" action to include borgmatic-specific paths (database dumps, etc.) in recreated archives. --- NEWS | 2 ++ borgmatic/actions/recreate.py | 16 ++++++++++++---- tests/unit/actions/test_recreate.py | 14 ++++++++++++++ 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index 2a3df2c2..afb0c58e 100644 --- a/NEWS +++ b/NEWS @@ -39,6 +39,8 @@ * SECURITY: Prevent shell injection attacks via constant interpolation in command hooks. (This was already implemented for deprecated "before_*"/"after_*" command hooks.) * Fix for an error in the "key import" action when importing a key from stdin. + * Fix the "recreate" action to include borgmatic-specific paths (database dumps, etc.) in recreated + archives. * Update the "list" action to support the "--json" flag when the "--archive" flag is also used. * Promote the ZFS, LVM, and Btrfs hooks from beta features to stable. diff --git a/borgmatic/actions/recreate.py b/borgmatic/actions/recreate.py index 8eabe3e7..c1c54ff4 100644 --- a/borgmatic/actions/recreate.py +++ b/borgmatic/actions/recreate.py @@ -1,10 +1,10 @@ import logging import subprocess -import borgmatic.borg.info +import borgmatic.actions.pattern +import borgmatic.borg.pattern import borgmatic.borg.recreate import borgmatic.borg.repo_list -from borgmatic.actions.pattern import collect_patterns, process_patterns logger = logging.getLogger(__name__) @@ -30,8 +30,16 @@ def run_recreate( logger.answer('Recreating repository') # Collect and process patterns. - processed_patterns = process_patterns( - collect_patterns(config), + processed_patterns = borgmatic.actions.pattern.process_patterns( + ( + *borgmatic.actions.pattern.collect_patterns(config), + # Also add borgmatic-specific paths, so they don't get excluded from the recreated + # archive. Note that this doesn't currently work for archives created with Borg 1.2 or + # below. + borgmatic.borg.pattern.Pattern( + '/borgmatic', source=borgmatic.borg.pattern.Pattern_source.INTERNAL + ), + ), config, borgmatic.config.paths.get_working_directory(config), ) diff --git a/tests/unit/actions/test_recreate.py b/tests/unit/actions/test_recreate.py index 74db695f..9b92530b 100644 --- a/tests/unit/actions/test_recreate.py +++ b/tests/unit/actions/test_recreate.py @@ -9,6 +9,8 @@ def test_run_recreate_does_not_raise(): flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return( flexmock(), ) + flexmock(module.borgmatic.actions.pattern).should_receive('collect_patterns').and_return(()) + flexmock(module.borgmatic.actions.pattern).should_receive('process_patterns').and_return(()) flexmock(module.borgmatic.borg.repo_list).should_receive('resolve_archive_name').and_return( None, ) @@ -30,6 +32,8 @@ def test_run_recreate_with_archive_does_not_raise(): flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return( flexmock(), ) + flexmock(module.borgmatic.actions.pattern).should_receive('collect_patterns').and_return(()) + flexmock(module.borgmatic.actions.pattern).should_receive('process_patterns').and_return(()) flexmock(module.borgmatic.borg.repo_list).should_receive('resolve_archive_name').and_return( 'test-archive', ) @@ -51,6 +55,8 @@ def test_run_recreate_with_leftover_recreate_archive_raises(): flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return( flexmock(), ) + flexmock(module.borgmatic.actions.pattern).should_receive('collect_patterns').and_return(()) + flexmock(module.borgmatic.actions.pattern).should_receive('process_patterns').and_return(()) flexmock(module.borgmatic.borg.repo_list).should_receive('resolve_archive_name').and_return( 'test-archive.recreate', ) @@ -73,6 +79,8 @@ def test_run_recreate_with_latest_archive_resolving_to_leftover_recreate_archive flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return( flexmock(), ) + flexmock(module.borgmatic.actions.pattern).should_receive('collect_patterns').and_return(()) + flexmock(module.borgmatic.actions.pattern).should_receive('process_patterns').and_return(()) flexmock(module.borgmatic.borg.repo_list).should_receive('resolve_archive_name').and_return( 'test-archive.recreate', ) @@ -95,6 +103,8 @@ def test_run_recreate_with_archive_already_exists_error_raises(): flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return( flexmock(), ) + flexmock(module.borgmatic.actions.pattern).should_receive('collect_patterns').and_return(()) + flexmock(module.borgmatic.actions.pattern).should_receive('process_patterns').and_return(()) flexmock(module.borgmatic.borg.repo_list).should_receive('resolve_archive_name').and_return( 'test-archive', ) @@ -122,6 +132,8 @@ def test_run_recreate_with_target_and_archive_already_exists_error_raises(): flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return( flexmock(), ) + flexmock(module.borgmatic.actions.pattern).should_receive('collect_patterns').and_return(()) + flexmock(module.borgmatic.actions.pattern).should_receive('process_patterns').and_return(()) flexmock(module.borgmatic.borg.repo_list).should_receive('resolve_archive_name').and_return( 'test-archive', ) @@ -153,6 +165,8 @@ def test_run_recreate_with_other_called_process_error_passes_it_through(): flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return( flexmock(), ) + flexmock(module.borgmatic.actions.pattern).should_receive('collect_patterns').and_return(()) + flexmock(module.borgmatic.actions.pattern).should_receive('process_patterns').and_return(()) flexmock(module.borgmatic.borg.repo_list).should_receive('resolve_archive_name').and_return( 'test-archive', )