Przeglądaj źródła

Change "exclude_if_present" option to support multiple filenames, rather than just a single filename (#280).

Dan Helfman 5 lat temu
rodzic
commit
94b9ef56be

+ 2 - 0
NEWS

@@ -1,6 +1,8 @@
 1.4.23.dev0
  * #274: Add ~/.config/borgmatic.d as another configuration directory default.
  * #277: Customize Healthchecks log level via borgmatic "--monitoring-verbosity" flag.
+ * #280: Change "exclude_if_present" option to support multiple filenames that indicate a directory
+   should be excluded from backups, rather than just a single filename.
  * For "create" and "prune" actions, no longer list files or show detailed stats at any verbosities
    by default. You can opt back in with "--files" or "--stats" flags.
  * For "list" and "info" actions, show repository names even at verbosity 0.

+ 6 - 2
borgmatic/borg/create.py

@@ -88,8 +88,12 @@ def _make_exclude_flags(location_config, exclude_filename=None):
         )
     )
     caches_flag = ('--exclude-caches',) if location_config.get('exclude_caches') else ()
-    if_present = location_config.get('exclude_if_present')
-    if_present_flags = ('--exclude-if-present', if_present) if if_present else ()
+    if_present_flags = tuple(
+        itertools.chain.from_iterable(
+            ('--exclude-if-present', if_present)
+            for if_present in location_config.get('exclude_if_present', ())
+        )
+    )
     keep_exclude_tags_flags = (
         ('--keep-exclude-tags',) if location_config.get('keep_exclude_tags') else ()
     )

+ 10 - 0
borgmatic/config/normalize.py

@@ -0,0 +1,10 @@
+def normalize(config):
+    '''
+    Given a configuration dict, apply particular hard-coded rules to normalize its contents to
+    adhere to the configuration schema.
+    '''
+    exclude_if_present = config.get('location', {}).get('exclude_if_present')
+
+    # "Upgrade" exclude_if_present from a string to a list.
+    if isinstance(exclude_if_present, str):
+        config['location']['exclude_if_present'] = [exclude_if_present]

+ 5 - 3
borgmatic/config/schema.yaml

@@ -121,11 +121,13 @@ map:
                     http://www.brynosaurus.com/cachedir/spec.html for details. Defaults to false.
                 example: true
             exclude_if_present:
-                type: str
+                seq:
+                    - type: str
                 desc: |
-                    Exclude directories that contain a file with the given filename. Defaults to not
+                    Exclude directories that contain a file with the given filenames. Defaults to not
                     set.
-                example: .nobackup
+                example:
+                    - .nobackup
             keep_exclude_tags:
                 type: bool
                 desc: |

+ 2 - 1
borgmatic/config/validate.py

@@ -6,7 +6,7 @@ import pykwalify.core
 import pykwalify.errors
 import ruamel.yaml
 
-from borgmatic.config import load, override
+from borgmatic.config import load, normalize, override
 
 
 def schema_filename():
@@ -104,6 +104,7 @@ def parse_configuration(config_filename, schema_filename, overrides=None):
         raise Validation_error(config_filename, (str(error),))
 
     override.apply_overrides(config, overrides)
+    normalize.normalize(config)
 
     validator = pykwalify.core.Core(source_data=config, schema_data=remove_examples(schema))
     parsed_result = validator.validate(raise_exception=False)

+ 25 - 0
tests/integration/config/test_validate.py

@@ -239,3 +239,28 @@ def test_parse_configuration_applies_overrides():
             'local_path': 'borg2',
         }
     }
+
+
+def test_parse_configuration_applies_normalization():
+    mock_config_and_schema(
+        '''
+        location:
+            source_directories:
+                - /home
+
+            repositories:
+                - hostname.borg
+
+            exclude_if_present: .nobackup
+        '''
+    )
+
+    result = module.parse_configuration('config.yaml', 'schema.yaml')
+
+    assert result == {
+        'location': {
+            'source_directories': ['/home'],
+            'repositories': ['hostname.borg'],
+            'exclude_if_present': ['.nobackup'],
+        }
+    }

+ 9 - 2
tests/unit/borg/test_create.py

@@ -145,9 +145,16 @@ def test_make_exclude_flags_does_not_include_exclude_caches_when_false_in_config
 
 
 def test_make_exclude_flags_includes_exclude_if_present_when_in_config():
-    exclude_flags = module._make_exclude_flags(location_config={'exclude_if_present': 'exclude_me'})
+    exclude_flags = module._make_exclude_flags(
+        location_config={'exclude_if_present': ['exclude_me', 'also_me']}
+    )
 
-    assert exclude_flags == ('--exclude-if-present', 'exclude_me')
+    assert exclude_flags == (
+        '--exclude-if-present',
+        'exclude_me',
+        '--exclude-if-present',
+        'also_me',
+    )
 
 
 def test_make_exclude_flags_includes_keep_exclude_tags_when_true_in_config():

+ 27 - 0
tests/unit/config/test_normalize.py

@@ -0,0 +1,27 @@
+import pytest
+
+from borgmatic.config import normalize as module
+
+
+@pytest.mark.parametrize(
+    'config,expected_config',
+    (
+        (
+            {'location': {'exclude_if_present': '.nobackup'}},
+            {'location': {'exclude_if_present': ['.nobackup']}},
+        ),
+        (
+            {'location': {'exclude_if_present': ['.nobackup']}},
+            {'location': {'exclude_if_present': ['.nobackup']}},
+        ),
+        (
+            {'location': {'source_directories': ['foo', 'bar']}},
+            {'location': {'source_directories': ['foo', 'bar']}},
+        ),
+        ({'storage': {'compression': 'yes_please'}}, {'storage': {'compression': 'yes_please'}}),
+    ),
+)
+def test_normalize_applies_hard_coded_normalization_to_config(config, expected_config):
+    module.normalize(config)
+
+    assert config == expected_config