From 7e7c3c273e555e304e8763ac4a0ea2053f8362e2 Mon Sep 17 00:00:00 2001 From: Dan Helfman Date: Fri, 22 May 2026 15:25:42 -0700 Subject: [PATCH] Update the Apprise monitoring hook's "url" option to support loading credentials with the "{credential ...}" syntax (#1308). --- NEWS | 3 +++ borgmatic/config/schema.yaml | 4 +++- borgmatic/hooks/monitoring/apprise.py | 8 +++++++- .../configuration/monitoring/apprise.md | 6 +++--- tests/unit/hooks/monitoring/test_apprise.py | 20 ++++++++++++++----- 5 files changed, 31 insertions(+), 10 deletions(-) diff --git a/NEWS b/NEWS index 99a3be21..8afb3c41 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,9 @@ * #1303: For the MariaDB hook, include only a subset of system data when dumping the "mysql" system database (or "all" databases), so the dump is actually restorable. See the documentation for more information: https://torsion.org/borgmatic/reference/configuration/data-sources/mariadb/ + * #1308: Update the Apprise monitoring hook's "url" option to support loading credentials with the + "{credential ...}" syntax. See the documentation for more information: + https://torsion.org/borgmatic/reference/configuration/credentials/ * #1309: Add a "--quick-stats" flag and corresponding "quick_statistics" option for showing only abbreviated statistics for the "create" action. Borg 1.4.5+ only. * Update the KeePassXC credential hook to support KeePassXC's secret service integration. See the diff --git a/borgmatic/config/schema.yaml b/borgmatic/config/schema.yaml index 3d18c0a5..16f43d5c 100644 --- a/borgmatic/config/schema.yaml +++ b/borgmatic/config/schema.yaml @@ -2834,7 +2834,9 @@ properties: properties: url: type: string - description: URL of this Apprise service. + description: | + URL of this Apprise service. Supports the + "{credential ...}" syntax. example: "gotify://hostname/token" label: type: string diff --git a/borgmatic/hooks/monitoring/apprise.py b/borgmatic/hooks/monitoring/apprise.py index 0280758a..a4e649d2 100644 --- a/borgmatic/hooks/monitoring/apprise.py +++ b/borgmatic/hooks/monitoring/apprise.py @@ -1,6 +1,7 @@ import logging import operator +import borgmatic.hooks.credential.parse import borgmatic.hooks.monitoring.logs import borgmatic.hooks.monitoring.monitor @@ -76,7 +77,12 @@ def ping_monitor(hook_config, config, config_filename, state, monitoring_log_lev logger.info(f'Pinging Apprise services: {labels_string}{dry_run_string}') apprise_object = apprise.Apprise() - apprise_object.add(list(map(operator.itemgetter('url'), hook_config.get('services')))) + apprise_object.add( + [ + borgmatic.hooks.credential.parse.resolve_credential(service['url'], config) + for service in hook_config.get('services') + ] + ) if dry_run: return diff --git a/docs/reference/configuration/monitoring/apprise.md b/docs/reference/configuration/monitoring/apprise.md index 27b0a7d6..17cd4e84 100644 --- a/docs/reference/configuration/monitoring/apprise.md +++ b/docs/reference/configuration/monitoring/apprise.md @@ -5,9 +5,9 @@ eleventyNavigation: parent: 🚨 Monitoring --- New in version 1.8.4 -[Apprise](https://github.com/caronc/apprise/wiki) is a local notification library +[Apprise](https://appriseit.com/) is a local notification library that "allows you to send a notification to almost all of the most popular -[notification services](https://github.com/caronc/apprise/wiki) available to +[notification services](https://appriseit.com/services/) available to us today such as: Telegram, Discord, Slack, Amazon SNS, Gotify, etc." Depending on how you installed borgmatic, it may not have come with Apprise. @@ -22,7 +22,7 @@ sudo uv tool install borgmatic[Apprise] Omit `sudo` if borgmatic is installed as a non-root user. Once Apprise is installed, configure borgmatic to notify one or more [Apprise -services](https://github.com/caronc/apprise/wiki). For example: +services](https://appriseit.com/services/). For example: ```yaml apprise: diff --git a/tests/unit/hooks/monitoring/test_apprise.py b/tests/unit/hooks/monitoring/test_apprise.py index 4521427a..457d4abe 100644 --- a/tests/unit/hooks/monitoring/test_apprise.py +++ b/tests/unit/hooks/monitoring/test_apprise.py @@ -66,7 +66,9 @@ def test_initialize_monitor_without_send_logs_does_not_add_handler(): def test_ping_monitor_respects_dry_run(): - flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler') + flexmock(module.borgmatic.hooks.credential.parse).should_receive( + 'resolve_credential' + ).replace_with(lambda url, config: url) flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( 'format_buffered_logs_for_payload', ).and_return('loggy log') @@ -83,7 +85,9 @@ def test_ping_monitor_respects_dry_run(): def test_ping_monitor_with_no_states_does_not_notify(): - flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler').never() + flexmock(module.borgmatic.hooks.credential.parse).should_receive( + 'resolve_credential' + ).replace_with(lambda url, config: url) flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( 'format_buffered_logs_for_payload', ).never() @@ -100,7 +104,9 @@ def test_ping_monitor_with_no_states_does_not_notify(): def test_ping_monitor_notifies_fail_by_default(): - flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler') + flexmock(module.borgmatic.hooks.credential.parse).should_receive( + 'resolve_credential' + ).replace_with(lambda url, config: url) flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( 'format_buffered_logs_for_payload', ).and_return('') @@ -123,7 +129,9 @@ def test_ping_monitor_notifies_fail_by_default(): def test_ping_monitor_with_logs_appends_logs_to_body(): - flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler') + flexmock(module.borgmatic.hooks.credential.parse).should_receive( + 'resolve_credential' + ).replace_with(lambda url, config: url) flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( 'format_buffered_logs_for_payload', ).and_return('loggy log') @@ -146,7 +154,9 @@ def test_ping_monitor_with_logs_appends_logs_to_body(): def test_ping_monitor_with_finish_default_config_notifies(): - flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler') + flexmock(module.borgmatic.hooks.credential.parse).should_receive( + 'resolve_credential' + ).replace_with(lambda url, config: url) flexmock(module.borgmatic.hooks.monitoring.logs).should_receive( 'format_buffered_logs_for_payload', ).and_return('')