Pārlūkot izejas kodu

When using the "bsd_flags" option, tailor the flags passed to Borg depending on the Borg version (#394).

Dan Helfman 3 gadi atpakaļ
vecāks
revīzija
6bf6ac310b

+ 2 - 1
NEWS

@@ -2,7 +2,8 @@
  * #394: Compact repository segments and free space with new "borgmatic compact" action. Borg 1.2+
  * #394: Compact repository segments and free space with new "borgmatic compact" action. Borg 1.2+
    only. Also run "compact" by default when no actions are specified, as "prune" in Borg 1.2 no
    only. Also run "compact" by default when no actions are specified, as "prune" in Borg 1.2 no
    longer frees up space unless "compact" is run.
    longer frees up space unless "compact" is run.
- * #394: When the "atime" option is used, tailor the flags passed to Borg depending on version.
+ * #394: When using the "atime" or "bsd_flags" options, tailor the flags passed to Borg depending on
+   the Borg version.
  * #480, #482: Fix traceback when a YAML validation error occurs.
  * #480, #482: Fix traceback when a YAML validation error occurs.
 
 
 1.5.22
 1.5.22

+ 7 - 3
borgmatic/borg/create.py

@@ -227,12 +227,16 @@ def create_archive(
     archive_name_format = storage_config.get('archive_name_format', DEFAULT_ARCHIVE_NAME_FORMAT)
     archive_name_format = storage_config.get('archive_name_format', DEFAULT_ARCHIVE_NAME_FORMAT)
     extra_borg_options = storage_config.get('extra_borg_options', {}).get('create', '')
     extra_borg_options = storage_config.get('extra_borg_options', {}).get('create', '')
 
 
-    atime_feature_available = feature.available(feature.Feature.ATIME, local_borg_version)
-    if atime_feature_available:
+    if feature.available(feature.Feature.ATIME, local_borg_version):
         atime_flags = ('--atime',) if location_config.get('atime') is True else ()
         atime_flags = ('--atime',) if location_config.get('atime') is True else ()
     else:
     else:
         atime_flags = ('--noatime',) if location_config.get('atime') is False else ()
         atime_flags = ('--noatime',) if location_config.get('atime') is False else ()
 
 
+    if feature.available(feature.Feature.NOFLAGS, local_borg_version):
+        noflags_flags = ('--noflags',) if location_config.get('bsd_flags') is False else ()
+    else:
+        noflags_flags = ('--nobsdflags',) if location_config.get('bsd_flags') is False else ()
+
     full_command = (
     full_command = (
         tuple(local_path.split(' '))
         tuple(local_path.split(' '))
         + ('create',)
         + ('create',)
@@ -252,7 +256,7 @@ def create_archive(
         + (('--noctime',) if location_config.get('ctime') is False else ())
         + (('--noctime',) if location_config.get('ctime') is False else ())
         + (('--nobirthtime',) if location_config.get('birthtime') is False else ())
         + (('--nobirthtime',) if location_config.get('birthtime') is False else ())
         + (('--read-special',) if (location_config.get('read_special') or stream_processes) else ())
         + (('--read-special',) if (location_config.get('read_special') or stream_processes) else ())
-        + (('--nobsdflags',) if location_config.get('bsd_flags') is False else ())
+        + noflags_flags
         + (('--files-cache', files_cache) if files_cache else ())
         + (('--files-cache', files_cache) if files_cache else ())
         + (('--remote-path', remote_path) if remote_path else ())
         + (('--remote-path', remote_path) if remote_path else ())
         + (('--umask', str(umask)) if umask else ())
         + (('--umask', str(umask)) if umask else ())

+ 4 - 2
borgmatic/borg/feature.py

@@ -6,11 +6,13 @@ from pkg_resources import parse_version
 class Feature(Enum):
 class Feature(Enum):
     COMPACT = 1
     COMPACT = 1
     ATIME = 2
     ATIME = 2
+    NOFLAGS = 3
 
 
 
 
 FEATURE_TO_MINIMUM_BORG_VERSION = {
 FEATURE_TO_MINIMUM_BORG_VERSION = {
-    Feature.COMPACT: parse_version('1.2.0a2'),
-    Feature.ATIME: parse_version('1.2.0a7'),
+    Feature.COMPACT: parse_version('1.2.0a2'),  # borg compact
+    Feature.ATIME: parse_version('1.2.0a7'),    # borg create --atime
+    Feature.NOFLAGS: parse_version('1.2.0a8'),  # borg create --noflags
 }
 }
 
 
 
 

+ 4 - 4
borgmatic/config/schema.yaml

@@ -111,10 +111,10 @@ properties:
                     type: string
                     type: string
                 description: |
                 description: |
                     Any paths matching these patterns are included/excluded from
                     Any paths matching these patterns are included/excluded from
-                    backups. Globs are expanded. (Tildes are not.) Note that
-                    Borg considers this option experimental. See the output of
-                    "borg help patterns" for more details. Quote any value if it
-                    contains leading punctuation, so it parses correctly.
+                    backups. Globs are expanded. (Tildes are not.) See the
+                    output of "borg help patterns" for more details. Quote any
+                    value if it contains leading punctuation, so it parses
+                    correctly.
                 example:
                 example:
                     - 'R /'
                     - 'R /'
                     - '- /home/*/.cache'
                     - '- /home/*/.cache'

+ 1 - 1
scripts/find-unsupported-borg-options

@@ -38,7 +38,7 @@ for sub_command in prune create check list info; do
             | grep -v '^--json$' \
             | grep -v '^--json$' \
             | grep -v '^--keep-last$' \
             | grep -v '^--keep-last$' \
             | grep -v '^--list$' \
             | grep -v '^--list$' \
-            | grep -v '^--nobsdflags$' \
+            | grep -v '^--bsdflags$' \
             | grep -v '^--pattern$' \
             | grep -v '^--pattern$' \
             | grep -v '^--progress$' \
             | grep -v '^--progress$' \
             | grep -v '^--stats$' \
             | grep -v '^--stats$' \

+ 47 - 5
tests/unit/borg/test_create.py

@@ -768,11 +768,9 @@ def test_create_archive_with_read_special_calls_borg_with_read_special_parameter
         ('ctime', False),
         ('ctime', False),
         ('birthtime', True),
         ('birthtime', True),
         ('birthtime', False),
         ('birthtime', False),
-        ('bsd_flags', True),
-        ('bsd_flags', False),
     ),
     ),
 )
 )
-def test_create_archive_with_option_calls_borg_without_corresponding_parameter(
+def test_create_archive_with_basic_option_calls_borg_with_corresponding_parameter(
     option_name, option_value
     option_name, option_value
 ):
 ):
     option_flag = '--no' + option_name.replace('_', '') if option_value is False else None
     option_flag = '--no' + option_name.replace('_', '') if option_value is False else None
@@ -815,7 +813,7 @@ def test_create_archive_with_option_calls_borg_without_corresponding_parameter(
         (False, False, '--noatime'),
         (False, False, '--noatime'),
     ),
     ),
 )
 )
-def test_create_archive_with_atime_option_calls_borg_without_corresponding_parameter(
+def test_create_archive_with_atime_option_calls_borg_with_corresponding_parameter(
     option_value, feature_available, option_flag
     option_value, feature_available, option_flag
 ):
 ):
     flexmock(module).should_receive('borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('borgmatic_source_directories').and_return([])
@@ -824,7 +822,8 @@ def test_create_archive_with_atime_option_calls_borg_without_corresponding_param
     flexmock(module).should_receive('_expand_directories').and_return(())
     flexmock(module).should_receive('_expand_directories').and_return(())
     flexmock(module).should_receive('_expand_home_directories').and_return(())
     flexmock(module).should_receive('_expand_home_directories').and_return(())
     flexmock(module).should_receive('_write_pattern_file').and_return(None)
     flexmock(module).should_receive('_write_pattern_file').and_return(None)
-    flexmock(module.feature).should_receive('available').and_return(feature_available)
+    flexmock(module.feature).should_receive('available').with_args(module.feature.Feature.ATIME, '1.2.3').and_return(feature_available)
+    flexmock(module.feature).should_receive('available').with_args(module.feature.Feature.NOFLAGS, '1.2.3').and_return(True)
     flexmock(module).should_receive('_make_pattern_flags').and_return(())
     flexmock(module).should_receive('_make_pattern_flags').and_return(())
     flexmock(module).should_receive('_make_exclude_flags').and_return(())
     flexmock(module).should_receive('_make_exclude_flags').and_return(())
     flexmock(module).should_receive('execute_command').with_args(
     flexmock(module).should_receive('execute_command').with_args(
@@ -848,6 +847,49 @@ def test_create_archive_with_atime_option_calls_borg_without_corresponding_param
     )
     )
 
 
 
 
+@pytest.mark.parametrize(
+    'option_value,feature_available,option_flag',
+    (
+        (True, True, None),
+        (True, False, None),
+        (False, True, '--noflags'),
+        (False, False, '--nobsdflags'),
+    ),
+)
+def test_create_archive_with_bsd_flags_option_calls_borg_with_corresponding_parameter(
+    option_value, feature_available, option_flag
+):
+    flexmock(module).should_receive('borgmatic_source_directories').and_return([])
+    flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
+    flexmock(module).should_receive('map_directories_to_devices').and_return({})
+    flexmock(module).should_receive('_expand_directories').and_return(())
+    flexmock(module).should_receive('_expand_home_directories').and_return(())
+    flexmock(module).should_receive('_write_pattern_file').and_return(None)
+    flexmock(module.feature).should_receive('available').with_args(module.feature.Feature.ATIME, '1.2.3').and_return(True)
+    flexmock(module.feature).should_receive('available').with_args(module.feature.Feature.NOFLAGS, '1.2.3').and_return(feature_available)
+    flexmock(module).should_receive('_make_pattern_flags').and_return(())
+    flexmock(module).should_receive('_make_exclude_flags').and_return(())
+    flexmock(module).should_receive('execute_command').with_args(
+        ('borg', 'create') + ((option_flag,) if option_flag else ()) + ARCHIVE_WITH_PATHS,
+        output_log_level=logging.INFO,
+        output_file=None,
+        borg_local_path='borg',
+    )
+
+    module.create_archive(
+        dry_run=False,
+        repository='repo',
+        location_config={
+            'source_directories': ['foo', 'bar'],
+            'repositories': ['repo'],
+            'bsd_flags': option_value,
+            'exclude_patterns': None,
+        },
+        storage_config={},
+        local_borg_version='1.2.3',
+    )
+
+
 def test_create_archive_with_files_cache_calls_borg_with_files_cache_parameters():
 def test_create_archive_with_files_cache_calls_borg_with_files_cache_parameters():
     flexmock(module).should_receive('borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('borgmatic_source_directories').and_return([])
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
     flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))