diff --git a/NEWS b/NEWS index e21c8db7..7928eef8 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,7 @@ 2.0.12.dev0 * Add documentation on repositories, including SSH, Rclone, S3, and B2: https://torsion.org/borgmatic/reference/configuration/repositories/ + * Improve documentation search results for individual configuration options. 2.0.11 * #957: Document borgmatic's limitations around parallelism—both its own and Borg's. See the diff --git a/borgmatic/config/generate.py b/borgmatic/config/generate.py index 2cccdaa5..76d4a167 100644 --- a/borgmatic/config/generate.py +++ b/borgmatic/config/generate.py @@ -1,6 +1,7 @@ import collections import contextlib import io +import json import os import re @@ -316,6 +317,24 @@ def merge_source_configuration_into_destination(destination_config, source_confi return destination_config +def get_configuration_subset(config, option_name): # pragma: no cover + ''' + Given configuration as a ruamel.yaml.CommentedMap and an option name found within it at the top + level, return a new CommentedMap containing a subset of the configuration with only the given + option and no other top-level options. + + This is useful when generating the sample configuration for a single option instead of a whole + configuration file. + ''' + option_config = ruamel.yaml.CommentedMap({option_name: config[option_name]}) + + # Due to a quirk of ruamel.yaml, the comment right before a top-level key is not on that key and + # needs to get copied separately. + option_config.ca.items[option_name] = config.ca.items[option_name] + + return option_config + + def generate_sample_configuration( dry_run, source_filename, @@ -359,16 +378,22 @@ def generate_sample_configuration( os.makedirs(destination_path, exist_ok=True) - for option_name, option_config in destination_config.items(): + for option_name in destination_config: write_configuration( os.path.join(destination_path, f'{option_name}.yaml'), transform_optional_configuration( - render_configuration({option_name: option_config}), + render_configuration(get_configuration_subset(destination_config, option_name)), comment_out=False, - ), + ).strip(), overwrite=overwrite, ) + # Also dump a manifest listing all the options we've written. + json.dump( + {'option_names': list(destination_config.keys())}, + open(os.path.join(destination_path, 'options.json'), 'w', encoding='utf-8'), + ) + return if os.path.exists(destination_path) and not os.path.isfile(destination_path): diff --git a/docs/Dockerfile b/docs/Dockerfile index 28264c2d..5463b98d 100644 --- a/docs/Dockerfile +++ b/docs/Dockerfile @@ -24,6 +24,7 @@ RUN npm install @11ty/eleventy \ markdown-it-anchor \ markdown-it-replace-link COPY --from=borgmatic /etc/borgmatic/* /source/docs/_includes/borgmatic/ +COPY --from=borgmatic /etc/borgmatic/options.json /source/docs/reference/configuration/index.json COPY --from=borgmatic /command-line/* /source/docs/_includes/borgmatic/command-line/ COPY --from=borgmatic /contributors.html /source/docs/_includes/borgmatic/contributors.html COPY . /source diff --git a/docs/reference/configuration/index.md b/docs/reference/configuration/index.md index 905ec15d..4aed5804 100644 --- a/docs/reference/configuration/index.md +++ b/docs/reference/configuration/index.md @@ -5,10 +5,10 @@ eleventyNavigation: parent: Reference guides order: 0 --- -Below is a sample borgmatic configuration file including all available options -for the [most recent version of +Below is a sample borgmatic configuration snippet for every available option in +the [most recent version of borgmatic](https://projects.torsion.org/borgmatic-collective/borgmatic/releases). -This file is also [available for +A full example configuration file is also [available for download](https://torsion.org/borgmatic/reference/config.yaml). If you're using an older version of borgmatic, some of these options may not @@ -16,6 +16,11 @@ work, and you should instead [generate a sample configuration file specific to your borgmatic version](https://torsion.org/borgmatic/how-to/set-up-backups/#configuration). + +{% for option_name in option_names %} +### {{ option_name }} option ```yaml -{% include borgmatic/config.yaml %} +{% include borgmatic/{{ option_name }}.yaml %} ``` +{% endfor %} + diff --git a/tests/integration/config/test_generate.py b/tests/integration/config/test_generate.py index 26e75573..48fbd502 100644 --- a/tests/integration/config/test_generate.py +++ b/tests/integration/config/test_generate.py @@ -512,6 +512,12 @@ def test_generate_sample_configuration_with_dry_run_does_not_write_file(): def test_generate_sample_configuration_with_split_writes_each_option_to_file(): builtins = flexmock(sys.modules['builtins']) builtins.should_receive('open').with_args('schema.yaml', encoding='utf-8').and_return('') + builtins.should_receive('open').with_args( + 'dest/options.json', 'w', encoding='utf-8' + ).and_return(flexmock()) + flexmock(module.json).should_receive('dump').with_args( + {'option_names': ['foo', 'bar']}, object + ).once() flexmock(module.ruamel.yaml).should_receive('YAML').and_return( flexmock(load=lambda filename: {}) ) @@ -520,17 +526,18 @@ def test_generate_sample_configuration_with_split_writes_each_option_to_file(): {'foo': 1, 'bar': 2} ) flexmock(module.os.path).should_receive('exists').and_return(False) + flexmock(module).should_receive('get_configuration_subset') flexmock(module).should_receive('render_configuration') - flexmock(module).should_receive('transform_optional_configuration') + flexmock(module).should_receive('transform_optional_configuration').and_return(' ') flexmock(module.os).should_receive('makedirs') flexmock(module).should_receive('write_configuration').with_args( 'dest/foo.yaml', - None, + '', overwrite=False, ).once() flexmock(module).should_receive('write_configuration').with_args( 'dest/bar.yaml', - None, + '', overwrite=False, ).once() @@ -549,6 +556,7 @@ def test_generate_sample_configuration_with_split_and_file_destination_errors(): ) flexmock(module.os.path).should_receive('exists').and_return(True) flexmock(module.os.path).should_receive('isdir').and_return(False) + flexmock(module).should_receive('get_configuration_subset').never() flexmock(module).should_receive('render_configuration').never() flexmock(module).should_receive('transform_optional_configuration').never() flexmock(module.os).should_receive('makedirs').never()