Add "actions" runtime variable (#657)

This commit adds the "actions" variable to the list of variables
borgmatic can interpolate at runtime.

This variable will contains the list (as a string of comma-separated
elements) of all the actions borgmatic will run in this instance.

This will allow running "intelligent" before_actions and after_actions
hooks capable of behavior dependent of the actual action borgmatic will
perform/has performed.

Original need for this variable was to automatically mount and unmount
an NFS remote folder.

Issue: #657
This commit is contained in:
C-Duv 2023-05-11 01:42:55 +02:00
parent 403ae0f698
commit f17fde399e
4 changed files with 93 additions and 1 deletions

View File

@ -272,6 +272,7 @@ def run_actions(
global_arguments = arguments['global']
dry_run_label = ' (dry run; not making any changes)' if global_arguments.dry_run else ''
hook_context = {
'actions': ','.join(sorted(action for action in arguments.keys() if action != 'global')),
'repository': repository_path,
# Deprecated: For backwards compatibility with borgmatic < 1.6.0.
'repositories': ','.join([repo['path'] for repo in location['repositories']]),

View File

@ -64,6 +64,9 @@ values into the hook command: the borgmatic configuration filename and the
paths of the current Borg repository. Here's the full set of supported
variables you can use here:
* `actions`
<span class="minilink minilink-addedin">New in version 1.7.13</span>:
comma-separated list of all the action(s) borgmatic is running
* `configuration_filename`: borgmatic configuration filename in which the
hook was defined
* `log_file`

View File

@ -108,6 +108,8 @@ In this example, when the error occurs, borgmatic interpolates runtime values
into the hook command: the borgmatic configuration filename, and the path of
the repository. Here's the full set of supported variables you can use here:
* `actions`:
comma-separated list of all the action(s) borgmatic will run
* `configuration_filename`: borgmatic configuration filename in which the
error occurred
* `repository`: path of the repository in which the error occurred (may be

View File

@ -434,7 +434,7 @@ def test_run_actions_adds_log_file_to_hook_context():
location={'repositories': []},
storage=object,
hooks={},
hook_context={'repository': 'repo', 'repositories': '', 'log_file': 'foo'},
hook_context={'repository': 'repo', 'repositories': '', 'log_file': 'foo', 'actions': 'create'},
local_borg_version=object,
create_arguments=object,
global_arguments=object,
@ -461,6 +461,92 @@ def test_run_actions_adds_log_file_to_hook_context():
assert result == (expected,)
def test_run_actions_adds_action_name_to_hook_context():
flexmock(module).should_receive('add_custom_log_levels')
flexmock(module.command).should_receive('execute_hook')
expected = flexmock()
flexmock(borgmatic.actions.create).should_receive('run_create').with_args(
config_filename=object,
repository={'path': 'repo'},
location={'repositories': []},
storage=object,
hooks={},
hook_context={'repository': 'repo', 'repositories': '', 'actions': 'create'},
local_borg_version=object,
create_arguments=object,
global_arguments=object,
dry_run_label='',
local_path=object,
remote_path=object,
).once().and_return(expected)
result = tuple(
module.run_actions(
arguments={'global': flexmock(dry_run=False), 'create': flexmock()},
config_filename=flexmock(),
location={'repositories': []},
storage=flexmock(),
retention=flexmock(),
consistency=flexmock(),
hooks={},
local_path=flexmock(),
remote_path=flexmock(),
local_borg_version=flexmock(),
repository={'path': 'repo'},
)
)
assert result == (expected,)
def test_run_actions_adds_all_sorted_action_names_to_hook_context():
flexmock(module).should_receive('add_custom_log_levels')
flexmock(module.command).should_receive('execute_hook')
expected = flexmock()
flexmock(borgmatic.actions.check).should_receive('run_check').with_args(
config_filename=object,
repository={'path': 'repo'},
location={'repositories': []},
storage=object,
consistency=object,
hooks={},
hook_context={'repository': 'repo', 'repositories': '', 'actions': 'check,prune'},
local_borg_version=object,
check_arguments=object,
global_arguments=object,
local_path=object,
remote_path=object,
).once()
flexmock(borgmatic.actions.prune).should_receive('run_prune').with_args(
config_filename=object,
repository={'path': 'repo'},
storage=object,
retention=object,
hooks={},
hook_context={'repository': 'repo', 'repositories': '', 'actions': 'check,prune'},
local_borg_version=object,
prune_arguments=object,
global_arguments=object,
dry_run_label='',
local_path=object,
remote_path=object,
).once()
module.run_actions(
arguments={'global': flexmock(dry_run=False), 'prune': flexmock(), 'check': flexmock()},
config_filename=flexmock(),
location={'repositories': []},
storage=flexmock(),
retention=flexmock(),
consistency=flexmock(),
hooks={},
local_path=flexmock(),
remote_path=flexmock(),
local_borg_version=flexmock(),
repository={'path': 'repo'},
)
def test_run_actions_runs_transfer():
flexmock(module).should_receive('add_custom_log_levels')
flexmock(module.command).should_receive('execute_hook')