Add Borg 1.4.1 features (#1078).

Reviewed-on: https://projects.torsion.org/borgmatic-collective/borgmatic/pulls/1081
This commit is contained in:
Dan Helfman
2025-04-25 19:02:27 +00:00
7 changed files with 90 additions and 3 deletions
+7 -2
View File
@@ -1,7 +1,7 @@
import logging
import borgmatic.config.paths
from borgmatic.borg import environment, flags
from borgmatic.borg import environment, feature, flags
from borgmatic.execute import execute_command
logger = logging.getLogger(__name__)
@@ -37,11 +37,16 @@ def compact_segments(
+ (('--threshold', str(threshold)) if threshold else ())
+ (('--info',) if logger.getEffectiveLevel() == logging.INFO else ())
+ (('--debug', '--show-rc') if logger.isEnabledFor(logging.DEBUG) else ())
+ (
('--dry-run',)
if dry_run and feature.available(feature.Feature.DRY_RUN_COMPACT, local_borg_version)
else ()
)
+ (tuple(extra_borg_options.split(' ')) if extra_borg_options else ())
+ flags.make_repository_flags(repository_path, local_borg_version)
)
if dry_run:
if dry_run and not feature.available(feature.Feature.DRY_RUN_COMPACT, local_borg_version):
logging.info('Skipping compact (dry run)')
return
+1
View File
@@ -17,6 +17,7 @@ OPTION_TO_ENVIRONMENT_VARIABLE = {
DEFAULT_BOOL_OPTION_TO_DOWNCASE_ENVIRONMENT_VARIABLE = {
'relocated_repo_access_is_ok': 'BORG_RELOCATED_REPO_ACCESS_IS_OK',
'unknown_unencrypted_repo_access_is_ok': 'BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK',
'use_chunks_archive': 'BORG_USE_CHUNKS_ARCHIVE',
}
DEFAULT_BOOL_OPTION_TO_UPPERCASE_ENVIRONMENT_VARIABLE = {
+2
View File
@@ -18,6 +18,7 @@ class Feature(Enum):
EXCLUDED_FILES_MINUS = 12
ARCHIVE_SERIES = 13
NO_PRUNE_STATS = 14
DRY_RUN_COMPACT = 15
FEATURE_TO_MINIMUM_BORG_VERSION = {
@@ -35,6 +36,7 @@ FEATURE_TO_MINIMUM_BORG_VERSION = {
Feature.EXCLUDED_FILES_MINUS: parse('2.0.0b5'), # --list --filter uses "-" for excludes
Feature.ARCHIVE_SERIES: parse('2.0.0b11'), # identically named archives form a series
Feature.NO_PRUNE_STATS: parse('2.0.0b10'), # prune --stats is not available
Feature.DRY_RUN_COMPACT: parse('1.4.1'), # borg compact --dry-run support
}
+15
View File
@@ -389,6 +389,13 @@ properties:
Path for Borg cache files. Defaults to
$borg_base_directory/.cache/borg
example: /path/to/base/cache
use_chunks_archive:
type: boolean
description: |
Enables or disables the use of chunks.archive.d for faster cache
resyncs in Borg. If true, value is set to "yes" (default) else
it's set to "no", reducing disk usage but slowing resyncs.
example: true
borg_files_cache_ttl:
type: integer
description: |
@@ -558,6 +565,14 @@ properties:
type: integer
description: Number of yearly archives to keep.
example: 1
keep_13weekly:
type: integer
description: Number of quarterly archives to keep (13 week strategy).
example: 13
keep_3monthly:
type: integer
description: Number of quarterly archives to keep (3 month strategy).
example: 3
prefix:
type: string
description: |
+26 -1
View File
@@ -68,8 +68,15 @@ def test_compact_segments_with_log_debug_calls_borg_with_debug_flag():
)
def test_compact_segments_with_dry_run_skips_borg_call():
def test_compact_segments_with_dry_run_skips_borg_call_when_feature_unavailable():
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
flexmock(module.feature).should_receive('available').with_args(
module.feature.Feature.DRY_RUN_COMPACT, '1.2.3'
).and_return(False)
flexmock(module.environment).should_receive('make_environment').never()
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').never()
flexmock(module).should_receive('execute_command').never()
flexmock(logging).should_receive('info').with_args('Skipping compact (dry run)').once()
module.compact_segments(
repository_path='repo',
@@ -80,6 +87,24 @@ def test_compact_segments_with_dry_run_skips_borg_call():
)
def test_compact_segments_with_dry_run_executes_borg_call_when_feature_available():
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
flexmock(module.feature).should_receive('available').with_args(
module.feature.Feature.DRY_RUN_COMPACT, '1.4.1'
).and_return(True)
flexmock(module.environment).should_receive('make_environment').once()
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').once()
flexmock(module).should_receive('execute_command').once()
module.compact_segments(
repository_path='repo',
config={},
local_borg_version='1.4.1',
global_arguments=flexmock(),
dry_run=True,
)
def test_compact_segments_with_local_path_calls_borg_via_local_path():
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
insert_execute_command_mock(('borg1',) + COMPACT_COMMAND[1:] + ('repo',), logging.INFO)
+17
View File
@@ -92,6 +92,7 @@ def test_make_environment_without_configuration_sets_certain_environment_variabl
'BORG_EXIT_CODES': 'modern',
'BORG_RELOCATED_REPO_ACCESS_IS_OK': 'no',
'BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK': 'no',
'BORG_USE_CHUNKS_ARCHIVE': 'no',
}
@@ -101,6 +102,7 @@ def test_make_environment_without_configuration_passes_through_default_environme
'USER': 'root',
'BORG_RELOCATED_REPO_ACCESS_IS_OK': 'yup',
'BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK': 'nah',
'BORG_USE_CHUNKS_ARCHIVE': 'yup',
}
)
flexmock(module.borgmatic.hooks.credential.parse).should_receive(
@@ -113,6 +115,7 @@ def test_make_environment_without_configuration_passes_through_default_environme
'USER': 'root',
'BORG_RELOCATED_REPO_ACCESS_IS_OK': 'yup',
'BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK': 'nah',
'BORG_USE_CHUNKS_ARCHIVE': 'yup',
'BORG_EXIT_CODES': 'modern',
}
@@ -170,3 +173,17 @@ def test_make_environment_with_integer_variable_value():
environment = module.make_environment({'borg_files_cache_ttl': 40})
assert environment.get('BORG_FILES_CACHE_TTL') == '40'
def test_make_environment_with_use_chunks_archive_should_set_correct_environment_value():
flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
flexmock(module.borgmatic.hooks.credential.parse).should_receive(
'resolve_credential'
).and_return(None)
flexmock(module.os).should_receive('pipe').never()
environment = module.make_environment({'use_chunks_archive': True})
assert environment.get('BORG_USE_CHUNKS_ARCHIVE') == 'yes'
environment = module.make_environment({'use_chunks_archive': False})
assert environment.get('BORG_USE_CHUNKS_ARCHIVE') == 'no'
+22
View File
@@ -43,6 +43,28 @@ def test_make_prune_flags_returns_flags_from_config():
assert result == BASE_PRUNE_FLAGS
def test_make_prune_flags_with_keep_13weekly_and_keep_3monthly():
config = {
'keep_13weekly': 4,
'keep_3monthly': 5,
}
flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
result = module.make_prune_flags(
config, flexmock(match_archives=None), local_borg_version='1.2.3'
)
expected = (
'--keep-13weekly',
'4',
'--keep-3monthly',
'5',
)
assert result == expected
def test_make_prune_flags_accepts_prefix_with_placeholders():
config = {
'keep_daily': 1,