2
0
Эх сурвалжийг харах

Send the "encryption_passphrase" option to Borg via an anonymous pipe (#998).

Reviewed-on: https://projects.torsion.org/borgmatic-collective/borgmatic/pulls/998
Dan Helfman 3 сар өмнө
parent
commit
3cf19dd1b0
56 өөрчлөгдсөн 594 нэмэгдсэн , 592 устгасан
  1. 2 0
      NEWS
  2. 1 1
      borgmatic/actions/check.py
  3. 1 1
      borgmatic/borg/borg.py
  4. 1 1
      borgmatic/borg/break_lock.py
  5. 1 1
      borgmatic/borg/change_passphrase.py
  6. 1 1
      borgmatic/borg/check.py
  7. 1 1
      borgmatic/borg/compact.py
  8. 4 4
      borgmatic/borg/create.py
  9. 1 1
      borgmatic/borg/delete.py
  10. 32 12
      borgmatic/borg/environment.py
  11. 1 1
      borgmatic/borg/export_key.py
  12. 1 1
      borgmatic/borg/export_tar.py
  13. 4 4
      borgmatic/borg/extract.py
  14. 2 2
      borgmatic/borg/info.py
  15. 3 3
      borgmatic/borg/list.py
  16. 2 2
      borgmatic/borg/mount.py
  17. 3 13
      borgmatic/borg/passcommand.py
  18. 1 1
      borgmatic/borg/prune.py
  19. 1 1
      borgmatic/borg/repo_create.py
  20. 1 1
      borgmatic/borg/repo_delete.py
  21. 2 2
      borgmatic/borg/repo_info.py
  22. 3 3
      borgmatic/borg/repo_list.py
  23. 1 1
      borgmatic/borg/transfer.py
  24. 1 1
      borgmatic/borg/version.py
  25. 39 35
      borgmatic/execute.py
  26. 8 6
      borgmatic/hooks/command.py
  27. 20 19
      borgmatic/hooks/data_source/mariadb.py
  28. 20 19
      borgmatic/hooks/data_source/mysql.py
  29. 21 25
      borgmatic/hooks/data_source/postgresql.py
  30. 15 15
      tests/unit/borg/test_borg.py
  31. 1 1
      tests/unit/borg/test_break_lock.py
  32. 1 1
      tests/unit/borg/test_change_passphrase.py
  33. 7 7
      tests/unit/borg/test_check.py
  34. 1 1
      tests/unit/borg/test_compact.py
  35. 19 19
      tests/unit/borg/test_create.py
  36. 3 3
      tests/unit/borg/test_delete.py
  37. 72 46
      tests/unit/borg/test_environment.py
  38. 1 1
      tests/unit/borg/test_export_key.py
  39. 1 1
      tests/unit/borg/test_export_tar.py
  40. 4 4
      tests/unit/borg/test_extract.py
  41. 2 2
      tests/unit/borg/test_info.py
  42. 12 12
      tests/unit/borg/test_list.py
  43. 3 3
      tests/unit/borg/test_mount.py
  44. 11 58
      tests/unit/borg/test_passcommand.py
  45. 2 2
      tests/unit/borg/test_prune.py
  46. 1 1
      tests/unit/borg/test_repo_create.py
  47. 4 4
      tests/unit/borg/test_repo_delete.py
  48. 25 25
      tests/unit/borg/test_repo_info.py
  49. 13 13
      tests/unit/borg/test_repo_list.py
  50. 18 18
      tests/unit/borg/test_transfer.py
  51. 1 1
      tests/unit/borg/test_version.py
  52. 48 34
      tests/unit/hooks/data_source/test_mariadb.py
  53. 48 34
      tests/unit/hooks/data_source/test_mysql.py
  54. 72 71
      tests/unit/hooks/data_source/test_postgresql.py
  55. 6 6
      tests/unit/hooks/test_command.py
  56. 24 46
      tests/unit/test_execute.py

+ 2 - 0
NEWS

@@ -4,6 +4,8 @@
    https://torsion.org/borgmatic/docs/how-to/provide-your-passwords/
  * #996: Fix the "create" action to omit the repository label prefix from Borg's output when
    databases are enabled.
+ * #998: Send the "encryption_passphrase" option to Borg via an anonymous pipe, which is more secure
+   than using an environment variable.
  * #1001: For the ZFS, Btrfs, and LVM hooks, only make snapshots for root patterns that come from
    a borgmatic configuration option (e.g. "source_directories")—not from other hooks within
    borgmatic.

+ 1 - 1
borgmatic/actions/check.py

@@ -391,7 +391,7 @@ def collect_spot_check_source_paths(
     paths_output = borgmatic.execute.execute_command_and_capture_output(
         create_flags + create_positional_arguments,
         capture_stderr=True,
-        extra_environment=borgmatic.borg.environment.make_environment(config),
+        environment=borgmatic.borg.environment.make_environment(config),
         working_directory=working_directory,
         borg_local_path=local_path,
         borg_exit_codes=config.get('borg_exit_codes'),

+ 1 - 1
borgmatic/borg/borg.py

@@ -61,7 +61,7 @@ def run_arbitrary_borg(
         tuple(shlex.quote(part) for part in full_command),
         output_file=DO_NOT_CAPTURE,
         shell=True,
-        extra_environment=dict(
+        environment=dict(
             (environment.make_environment(config) or {}),
             **{
                 'BORG_REPO': repository_path,

+ 1 - 1
borgmatic/borg/break_lock.py

@@ -36,7 +36,7 @@ def break_lock(
 
     execute_command(
         full_command,
-        extra_environment=environment.make_environment(config),
+        environment=environment.make_environment(config),
         working_directory=borgmatic.config.paths.get_working_directory(config),
         borg_local_path=local_path,
         borg_exit_codes=config.get('borg_exit_codes'),

+ 1 - 1
borgmatic/borg/change_passphrase.py

@@ -56,7 +56,7 @@ def change_passphrase(
         full_command,
         output_file=borgmatic.execute.DO_NOT_CAPTURE,
         output_log_level=logging.ANSWER,
-        extra_environment=environment.make_environment(config_without_passphrase),
+        environment=environment.make_environment(config_without_passphrase),
         working_directory=borgmatic.config.paths.get_working_directory(config),
         borg_local_path=local_path,
         borg_exit_codes=config.get('borg_exit_codes'),

+ 1 - 1
borgmatic/borg/check.py

@@ -182,7 +182,7 @@ def check_archives(
             output_file=(
                 DO_NOT_CAPTURE if check_arguments.repair or check_arguments.progress else None
             ),
-            extra_environment=environment.make_environment(config),
+            environment=environment.make_environment(config),
             working_directory=working_directory,
             borg_local_path=local_path,
             borg_exit_codes=borg_exit_codes,

+ 1 - 1
borgmatic/borg/compact.py

@@ -49,7 +49,7 @@ def compact_segments(
     execute_command(
         full_command,
         output_log_level=logging.INFO,
-        extra_environment=environment.make_environment(config),
+        environment=environment.make_environment(config),
         working_directory=borgmatic.config.paths.get_working_directory(config),
         borg_local_path=local_path,
         borg_exit_codes=config.get('borg_exit_codes'),

+ 4 - 4
borgmatic/borg/create.py

@@ -146,7 +146,7 @@ def collect_special_file_paths(
         + ('--dry-run', '--list'),
         capture_stderr=True,
         working_directory=working_directory,
-        extra_environment=environment.make_environment(config),
+        environment=environment.make_environment(config),
         borg_local_path=local_path,
         borg_exit_codes=config.get('borg_exit_codes'),
     )
@@ -421,7 +421,7 @@ def create_archive(
             output_log_level,
             output_file,
             working_directory=working_directory,
-            extra_environment=environment.make_environment(config),
+            environment=environment.make_environment(config),
             borg_local_path=local_path,
             borg_exit_codes=borg_exit_codes,
         )
@@ -429,7 +429,7 @@ def create_archive(
         return execute_command_and_capture_output(
             create_flags + create_positional_arguments,
             working_directory=working_directory,
-            extra_environment=environment.make_environment(config),
+            environment=environment.make_environment(config),
             borg_local_path=local_path,
             borg_exit_codes=borg_exit_codes,
         )
@@ -439,7 +439,7 @@ def create_archive(
             output_log_level,
             output_file,
             working_directory=working_directory,
-            extra_environment=environment.make_environment(config),
+            environment=environment.make_environment(config),
             borg_local_path=local_path,
             borg_exit_codes=borg_exit_codes,
         )

+ 1 - 1
borgmatic/borg/delete.py

@@ -128,7 +128,7 @@ def delete_archives(
     borgmatic.execute.execute_command(
         command,
         output_log_level=logging.ANSWER,
-        extra_environment=borgmatic.borg.environment.make_environment(config),
+        environment=borgmatic.borg.environment.make_environment(config),
         working_directory=borgmatic.config.paths.get_working_directory(config),
         borg_local_path=local_path,
         borg_exit_codes=config.get('borg_exit_codes'),

+ 32 - 12
borgmatic/borg/environment.py

@@ -10,13 +10,10 @@ OPTION_TO_ENVIRONMENT_VARIABLE = {
     'borg_files_cache_ttl': 'BORG_FILES_CACHE_TTL',
     'borg_security_directory': 'BORG_SECURITY_DIR',
     'borg_keys_directory': 'BORG_KEYS_DIR',
-    'encryption_passphrase': 'BORG_PASSPHRASE',
     'ssh_command': 'BORG_RSH',
     'temporary_directory': 'TMPDIR',
 }
 
-CREDENTIAL_OPTIONS = {'encryption_passphrase'}
-
 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',
@@ -29,27 +26,50 @@ DEFAULT_BOOL_OPTION_TO_UPPERCASE_ENVIRONMENT_VARIABLE = {
 
 def make_environment(config):
     '''
-    Given a borgmatic configuration dict, return its options converted to a Borg environment
-    variable dict.
+    Given a borgmatic configuration dict, convert it to a Borg environment variable dict, merge it
+    with a copy of the current environment variables, and return the result.
 
     Do not reuse this environment across multiple Borg invocations, because it can include
     references to resources like anonymous pipes for passphrases—which can only be consumed once.
+
+    Here's how native Borg precedence works for a few of the environment variables:
+
+      1. BORG_PASSPHRASE, if set, is used first.
+      2. BORG_PASSCOMMAND is used only if BORG_PASSPHRASE isn't set.
+      3. BORG_PASSPHRASE_FD is used only if neither of the above are set.
+
+    In borgmatic, we want to simulate this precedence order, but there are some additional
+    complications. First, values can come from either configuration or from environment variables
+    set outside borgmatic; configured options should take precedence. Second, when borgmatic gets a
+    passphrase—directly from configuration or indirectly via a credential hook or a passcommand—we
+    want to pass that passphrase to Borg via an anonymous pipe (+ BORG_PASSPHRASE_FD), since that's
+    more secure than using an environment variable (BORG_PASSPHRASE).
     '''
-    environment = {}
+    environment = dict(os.environ)
 
     for option_name, environment_variable_name in OPTION_TO_ENVIRONMENT_VARIABLE.items():
         value = config.get(option_name)
 
-        if option_name in CREDENTIAL_OPTIONS and value is not None:
-            value = borgmatic.hooks.credential.parse.resolve_credential(value, config)
-
         if value is not None:
             environment[environment_variable_name] = str(value)
 
-    passphrase = borgmatic.borg.passcommand.get_passphrase_from_passcommand(config)
+    if 'encryption_passphrase' in config:
+        environment.pop('BORG_PASSPHRASE', None)
+        environment.pop('BORG_PASSCOMMAND', None)
+
+    if 'encryption_passcommand' in config:
+        environment.pop('BORG_PASSCOMMAND', None)
+
+    passphrase = borgmatic.hooks.credential.parse.resolve_credential(
+        config.get('encryption_passphrase'), config
+    )
+
+    if passphrase is None:
+        passphrase = borgmatic.borg.passcommand.get_passphrase_from_passcommand(config)
 
-    # If the passcommand produced a passphrase, send it to Borg via an anonymous pipe.
-    if passphrase:
+    # If there's a passphrase (from configuration, from a configured credential, or from a
+    # configured passcommand), send it to Borg via an anonymous pipe.
+    if passphrase is not None:
         read_file_descriptor, write_file_descriptor = os.pipe()
         os.write(write_file_descriptor, passphrase.encode('utf-8'))
         os.close(write_file_descriptor)

+ 1 - 1
borgmatic/borg/export_key.py

@@ -67,7 +67,7 @@ def export_key(
         full_command,
         output_file=output_file,
         output_log_level=logging.ANSWER,
-        extra_environment=environment.make_environment(config),
+        environment=environment.make_environment(config),
         working_directory=working_directory,
         borg_local_path=local_path,
         borg_exit_codes=config.get('borg_exit_codes'),

+ 1 - 1
borgmatic/borg/export_tar.py

@@ -70,7 +70,7 @@ def export_tar_archive(
         full_command,
         output_file=DO_NOT_CAPTURE if destination_path == '-' else None,
         output_log_level=output_log_level,
-        extra_environment=environment.make_environment(config),
+        environment=environment.make_environment(config),
         working_directory=borgmatic.config.paths.get_working_directory(config),
         borg_local_path=local_path,
         borg_exit_codes=config.get('borg_exit_codes'),

+ 4 - 4
borgmatic/borg/extract.py

@@ -58,7 +58,7 @@ def extract_last_archive_dry_run(
 
     execute_command(
         full_extract_command,
-        extra_environment=environment.make_environment(config),
+        environment=environment.make_environment(config),
         working_directory=borgmatic.config.paths.get_working_directory(config),
         borg_local_path=local_path,
         borg_exit_codes=config.get('borg_exit_codes'),
@@ -154,7 +154,7 @@ def extract_archive(
         return execute_command(
             full_command,
             output_file=DO_NOT_CAPTURE,
-            extra_environment=environment.make_environment(config),
+            environment=environment.make_environment(config),
             working_directory=full_destination_path,
             borg_local_path=local_path,
             borg_exit_codes=borg_exit_codes,
@@ -166,7 +166,7 @@ def extract_archive(
             full_command,
             output_file=subprocess.PIPE,
             run_to_completion=False,
-            extra_environment=environment.make_environment(config),
+            environment=environment.make_environment(config),
             working_directory=full_destination_path,
             borg_local_path=local_path,
             borg_exit_codes=borg_exit_codes,
@@ -176,7 +176,7 @@ def extract_archive(
     # if the restore paths don't exist in the archive.
     execute_command(
         full_command,
-        extra_environment=environment.make_environment(config),
+        environment=environment.make_environment(config),
         working_directory=full_destination_path,
         borg_local_path=local_path,
         borg_exit_codes=borg_exit_codes,

+ 2 - 2
borgmatic/borg/info.py

@@ -102,7 +102,7 @@ def display_archives_info(
 
     json_info = execute_command_and_capture_output(
         json_command,
-        extra_environment=environment.make_environment(config),
+        environment=environment.make_environment(config),
         working_directory=working_directory,
         borg_local_path=local_path,
         borg_exit_codes=borg_exit_codes,
@@ -116,7 +116,7 @@ def display_archives_info(
     execute_command(
         main_command,
         output_log_level=logging.ANSWER,
-        extra_environment=environment.make_environment(config),
+        environment=environment.make_environment(config),
         working_directory=working_directory,
         borg_local_path=local_path,
         borg_exit_codes=borg_exit_codes,

+ 3 - 3
borgmatic/borg/list.py

@@ -124,7 +124,7 @@ def capture_archive_listing(
                 local_path,
                 remote_path,
             ),
-            extra_environment=environment.make_environment(config),
+            environment=environment.make_environment(config),
             working_directory=borgmatic.config.paths.get_working_directory(config),
             borg_local_path=local_path,
             borg_exit_codes=config.get('borg_exit_codes'),
@@ -221,7 +221,7 @@ def list_archive(
                     local_path,
                     remote_path,
                 ),
-                extra_environment=environment.make_environment(config),
+                environment=environment.make_environment(config),
                 working_directory=borgmatic.config.paths.get_working_directory(config),
                 borg_local_path=local_path,
                 borg_exit_codes=borg_exit_codes,
@@ -257,7 +257,7 @@ def list_archive(
         execute_command(
             main_command,
             output_log_level=logging.ANSWER,
-            extra_environment=environment.make_environment(config),
+            environment=environment.make_environment(config),
             working_directory=borgmatic.config.paths.get_working_directory(config),
             borg_local_path=local_path,
             borg_exit_codes=borg_exit_codes,

+ 2 - 2
borgmatic/borg/mount.py

@@ -66,7 +66,7 @@ def mount_archive(
         execute_command(
             full_command,
             output_file=DO_NOT_CAPTURE,
-            extra_environment=environment.make_environment(config),
+            environment=environment.make_environment(config),
             working_directory=working_directory,
             borg_local_path=local_path,
             borg_exit_codes=config.get('borg_exit_codes'),
@@ -75,7 +75,7 @@ def mount_archive(
 
     execute_command(
         full_command,
-        extra_environment=environment.make_environment(config),
+        environment=environment.make_environment(config),
         working_directory=working_directory,
         borg_local_path=local_path,
         borg_exit_codes=config.get('borg_exit_codes'),

+ 3 - 13
borgmatic/borg/passcommand.py

@@ -9,21 +9,14 @@ logger = logging.getLogger(__name__)
 
 
 @functools.cache
-def run_passcommand(passcommand, passphrase_configured, working_directory):
+def run_passcommand(passcommand, working_directory):
     '''
     Run the given passcommand using the given working directory and return the passphrase produced
-    by the command. But bail first if a passphrase is already configured; this mimics Borg's
-    behavior.
+    by the command.
 
     Cache the results so that the passcommand only needs to run—and potentially prompt the user—once
     per borgmatic invocation.
     '''
-    if passcommand and passphrase_configured:
-        logger.warning(
-            'Ignoring the "encryption_passcommand" option because "encryption_passphrase" is set'
-        )
-        return None
-
     return borgmatic.execute.execute_command_and_capture_output(
         shlex.split(passcommand),
         working_directory=working_directory,
@@ -44,7 +37,4 @@ def get_passphrase_from_passcommand(config):
     if not passcommand:
         return None
 
-    passphrase = config.get('encryption_passphrase')
-    working_directory = borgmatic.config.paths.get_working_directory(config)
-
-    return run_passcommand(passcommand, bool(passphrase is not None), working_directory)
+    return run_passcommand(passcommand, borgmatic.config.paths.get_working_directory(config))

+ 1 - 1
borgmatic/borg/prune.py

@@ -96,7 +96,7 @@ def prune_archives(
     execute_command(
         full_command,
         output_log_level=output_log_level,
-        extra_environment=environment.make_environment(config),
+        environment=environment.make_environment(config),
         working_directory=borgmatic.config.paths.get_working_directory(config),
         borg_local_path=local_path,
         borg_exit_codes=config.get('borg_exit_codes'),

+ 1 - 1
borgmatic/borg/repo_create.py

@@ -98,7 +98,7 @@ def create_repository(
     execute_command(
         repo_create_command,
         output_file=DO_NOT_CAPTURE,
-        extra_environment=environment.make_environment(config),
+        environment=environment.make_environment(config),
         working_directory=borgmatic.config.paths.get_working_directory(config),
         borg_local_path=local_path,
         borg_exit_codes=config.get('borg_exit_codes'),

+ 1 - 1
borgmatic/borg/repo_delete.py

@@ -88,7 +88,7 @@ def delete_repository(
             if repo_delete_arguments.force or repo_delete_arguments.cache_only
             else borgmatic.execute.DO_NOT_CAPTURE
         ),
-        extra_environment=borgmatic.borg.environment.make_environment(config),
+        environment=borgmatic.borg.environment.make_environment(config),
         working_directory=borgmatic.config.paths.get_working_directory(config),
         borg_local_path=local_path,
         borg_exit_codes=config.get('borg_exit_codes'),

+ 2 - 2
borgmatic/borg/repo_info.py

@@ -56,7 +56,7 @@ def display_repository_info(
     if repo_info_arguments.json:
         return execute_command_and_capture_output(
             full_command,
-            extra_environment=environment.make_environment(config),
+            environment=environment.make_environment(config),
             working_directory=working_directory,
             borg_local_path=local_path,
             borg_exit_codes=borg_exit_codes,
@@ -65,7 +65,7 @@ def display_repository_info(
         execute_command(
             full_command,
             output_log_level=logging.ANSWER,
-            extra_environment=environment.make_environment(config),
+            environment=environment.make_environment(config),
             working_directory=working_directory,
             borg_local_path=local_path,
             borg_exit_codes=borg_exit_codes,

+ 3 - 3
borgmatic/borg/repo_list.py

@@ -49,7 +49,7 @@ def resolve_archive_name(
 
     output = execute_command_and_capture_output(
         full_command,
-        extra_environment=environment.make_environment(config),
+        environment=environment.make_environment(config),
         working_directory=borgmatic.config.paths.get_working_directory(config),
         borg_local_path=local_path,
         borg_exit_codes=config.get('borg_exit_codes'),
@@ -164,7 +164,7 @@ def list_repository(
 
     json_listing = execute_command_and_capture_output(
         json_command,
-        extra_environment=environment.make_environment(config),
+        environment=environment.make_environment(config),
         working_directory=working_directory,
         borg_local_path=local_path,
         borg_exit_codes=borg_exit_codes,
@@ -178,7 +178,7 @@ def list_repository(
     execute_command(
         main_command,
         output_log_level=logging.ANSWER,
-        extra_environment=environment.make_environment(config),
+        environment=environment.make_environment(config),
         working_directory=working_directory,
         borg_local_path=local_path,
         borg_exit_codes=borg_exit_codes,

+ 1 - 1
borgmatic/borg/transfer.py

@@ -57,7 +57,7 @@ def transfer_archives(
         full_command,
         output_log_level=logging.ANSWER,
         output_file=DO_NOT_CAPTURE if transfer_arguments.progress else None,
-        extra_environment=environment.make_environment(config),
+        environment=environment.make_environment(config),
         working_directory=borgmatic.config.paths.get_working_directory(config),
         borg_local_path=local_path,
         borg_exit_codes=config.get('borg_exit_codes'),

+ 1 - 1
borgmatic/borg/version.py

@@ -21,7 +21,7 @@ def local_borg_version(config, local_path='borg'):
     )
     output = execute_command_and_capture_output(
         full_command,
-        extra_environment=environment.make_environment(config),
+        environment=environment.make_environment(config),
         working_directory=borgmatic.config.paths.get_working_directory(config),
         borg_local_path=local_path,
         borg_exit_codes=config.get('borg_exit_codes'),

+ 39 - 35
borgmatic/execute.py

@@ -1,7 +1,6 @@
 import collections
 import enum
 import logging
-import os
 import select
 import subprocess
 import textwrap
@@ -243,6 +242,9 @@ def mask_command_secrets(full_command):
 MAX_LOGGED_COMMAND_LENGTH = 1000
 
 
+PREFIXES_OF_ENVIRONMENT_VARIABLES_TO_LOG = ('BORG_', 'PG', 'MARIADB_', 'MYSQL_')
+
+
 def log_command(full_command, input_file=None, output_file=None, environment=None):
     '''
     Log the given command (a sequence of command/argument strings), along with its input/output file
@@ -251,7 +253,14 @@ def log_command(full_command, input_file=None, output_file=None, environment=Non
     logger.debug(
         textwrap.shorten(
             ' '.join(
-                tuple(f'{key}=***' for key in (environment or {}).keys())
+                tuple(
+                    f'{key}=***'
+                    for key in (environment or {}).keys()
+                    if any(
+                        key.startswith(prefix)
+                        for prefix in PREFIXES_OF_ENVIRONMENT_VARIABLES_TO_LOG
+                    )
+                )
                 + mask_command_secrets(full_command)
             ),
             width=MAX_LOGGED_COMMAND_LENGTH,
@@ -274,7 +283,7 @@ def execute_command(
     output_file=None,
     input_file=None,
     shell=False,
-    extra_environment=None,
+    environment=None,
     working_directory=None,
     borg_local_path=None,
     borg_exit_codes=None,
@@ -284,18 +293,17 @@ def execute_command(
     Execute the given command (a sequence of command/argument strings) and log its output at the
     given log level. If an open output file object is given, then write stdout to the file and only
     log stderr. If an open input file object is given, then read stdin from the file. If shell is
-    True, execute the command within a shell. If an extra environment dict is given, then use it to
-    augment the current environment, and pass the result into the command. If a working directory is
-    given, use that as the present working directory when running the command. If a Borg local path
-    is given, and the command matches it (regardless of arguments), treat exit code 1 as a warning
-    instead of an error. But if Borg exit codes are given as a sequence of exit code configuration
-    dicts, then use that configuration to decide what's an error and what's a warning. If run to
-    completion is False, then return the process for the command without executing it to completion.
+    True, execute the command within a shell. If an environment variables dict is given, then pass
+    it into the command. If a working directory is given, use that as the present working directory
+    when running the command. If a Borg local path is given, and the command matches it (regardless
+    of arguments), treat exit code 1 as a warning instead of an error. But if Borg exit codes are
+    given as a sequence of exit code configuration dicts, then use that configuration to decide
+    what's an error and what's a warning. If run to completion is False, then return the process for
+    the command without executing it to completion.
 
     Raise subprocesses.CalledProcessError if an error occurs while running the command.
     '''
-    log_command(full_command, input_file, output_file, extra_environment)
-    environment = {**os.environ, **extra_environment} if extra_environment else None
+    log_command(full_command, input_file, output_file, environment)
     do_not_capture = bool(output_file is DO_NOT_CAPTURE)
     command = ' '.join(full_command) if shell else full_command
 
@@ -308,7 +316,7 @@ def execute_command(
         env=environment,
         cwd=working_directory,
         # Necessary for the passcommand credential hook to work.
-        close_fds=not bool((extra_environment or {}).get('BORG_PASSPHRASE_FD')),
+        close_fds=not bool((environment or {}).get('BORG_PASSPHRASE_FD')),
     )
     if not run_to_completion:
         return process
@@ -327,7 +335,7 @@ def execute_command_and_capture_output(
     full_command,
     capture_stderr=False,
     shell=False,
-    extra_environment=None,
+    environment=None,
     working_directory=None,
     borg_local_path=None,
     borg_exit_codes=None,
@@ -335,18 +343,16 @@ def execute_command_and_capture_output(
     '''
     Execute the given command (a sequence of command/argument strings), capturing and returning its
     output (stdout). If capture stderr is True, then capture and return stderr in addition to
-    stdout. If shell is True, execute the command within a shell. If an extra environment dict is
-    given, then use it to augment the current environment, and pass the result into the command. If
-    a working directory is given, use that as the present working directory when running the
-    command. If a Borg local path is given, and the command matches it (regardless of arguments),
-    treat exit code 1 as a warning instead of an error. But if Borg exit codes are given as a
-    sequence of exit code configuration dicts, then use that configuration to decide what's an error
-    and what's a warning.
+    stdout. If shell is True, execute the command within a shell. If an environment variables dict
+    is given, then pass it into the command. If a working directory is given, use that as the
+    present working directory when running the command. If a Borg local path is given, and the
+    command matches it (regardless of arguments), treat exit code 1 as a warning instead of an
+    error. But if Borg exit codes are given as a sequence of exit code configuration dicts, then use
+    that configuration to decide what's an error and what's a warning.
 
     Raise subprocesses.CalledProcessError if an error occurs while running the command.
     '''
-    log_command(full_command, environment=extra_environment)
-    environment = {**os.environ, **extra_environment} if extra_environment else None
+    log_command(full_command, environment=environment)
     command = ' '.join(full_command) if shell else full_command
 
     try:
@@ -357,7 +363,7 @@ def execute_command_and_capture_output(
             env=environment,
             cwd=working_directory,
             # Necessary for the passcommand credential hook to work.
-            close_fds=not bool((extra_environment or {}).get('BORG_PASSPHRASE_FD')),
+            close_fds=not bool((environment or {}).get('BORG_PASSPHRASE_FD')),
         )
     except subprocess.CalledProcessError as error:
         if (
@@ -377,7 +383,7 @@ def execute_command_with_processes(
     output_file=None,
     input_file=None,
     shell=False,
-    extra_environment=None,
+    environment=None,
     working_directory=None,
     borg_local_path=None,
     borg_exit_codes=None,
@@ -391,19 +397,17 @@ def execute_command_with_processes(
     If an open output file object is given, then write stdout to the file and only log stderr. But
     if output log level is None, instead suppress logging and return the captured output for (only)
     the given command. If an open input file object is given, then read stdin from the file. If
-    shell is True, execute the command within a shell. If an extra environment dict is given, then
-    use it to augment the current environment, and pass the result into the command. If a working
-    directory is given, use that as the present working directory when running the command. If a
-    Borg local path is given, then for any matching command or process (regardless of arguments),
-    treat exit code 1 as a warning instead of an error. But if Borg exit codes are given as a
-    sequence of exit code configuration dicts, then use that configuration to decide what's an error
-    and what's a warning.
+    shell is True, execute the command within a shell. If an environment variables dict is given,
+    then pass it into the command. If a working directory is given, use that as the present working
+    directory when running the command. If a Borg local path is given, then for any matching command
+    or process (regardless of arguments), treat exit code 1 as a warning instead of an error. But if
+    Borg exit codes are given as a sequence of exit code configuration dicts, then use that
+    configuration to decide what's an error and what's a warning.
 
     Raise subprocesses.CalledProcessError if an error occurs while running the command or in the
     upstream process.
     '''
-    log_command(full_command, input_file, output_file, extra_environment)
-    environment = {**os.environ, **extra_environment} if extra_environment else None
+    log_command(full_command, input_file, output_file, environment)
     do_not_capture = bool(output_file is DO_NOT_CAPTURE)
     command = ' '.join(full_command) if shell else full_command
 
@@ -419,7 +423,7 @@ def execute_command_with_processes(
             env=environment,
             cwd=working_directory,
             # Necessary for the passcommand credential hook to work.
-            close_fds=not bool((extra_environment or {}).get('BORG_PASSPHRASE_FD')),
+            close_fds=not bool((environment or {}).get('BORG_PASSPHRASE_FD')),
         )
     except (subprocess.CalledProcessError, OSError):
         # Something has gone wrong. So vent each process' output buffer to prevent it from hanging.

+ 8 - 6
borgmatic/hooks/command.py

@@ -30,16 +30,18 @@ def interpolate_context(hook_description, command, context):
 
 def make_environment(current_environment, sys_module=sys):
     '''
-    Given the existing system environment as a map from environment variable name to value, return
-    (in the same form) any extra environment variables that should be used when running command
-    hooks.
+    Given the existing system environment as a map from environment variable name to value, return a
+    copy of it, augmented with any extra environment variables that should be used when running
+    command hooks.
     '''
+    environment = dict(current_environment)
+
     # Detect whether we're running within a PyInstaller bundle. If so, set or clear LD_LIBRARY_PATH
     # based on the value of LD_LIBRARY_PATH_ORIG. This prevents library version information errors.
     if getattr(sys_module, 'frozen', False) and hasattr(sys_module, '_MEIPASS'):
-        return {'LD_LIBRARY_PATH': current_environment.get('LD_LIBRARY_PATH_ORIG', '')}
+        environment['LD_LIBRARY_PATH'] = environment.get('LD_LIBRARY_PATH_ORIG', '')
 
-    return {}
+    return environment
 
 
 def execute_hook(commands, umask, config_filename, description, dry_run, **context):
@@ -85,7 +87,7 @@ def execute_hook(commands, umask, config_filename, description, dry_run, **conte
                 [command],
                 output_log_level=(logging.ERROR if description == 'on-error' else logging.WARNING),
                 shell=True,
-                extra_environment=make_environment(os.environ),
+                environment=make_environment(os.environ),
             )
     finally:
         if original_umask:

+ 20 - 19
borgmatic/hooks/data_source/mariadb.py

@@ -26,7 +26,7 @@ def make_dump_path(base_directory):  # pragma: no cover
 SYSTEM_DATABASE_NAMES = ('information_schema', 'mysql', 'performance_schema', 'sys')
 
 
-def database_names_to_dump(database, config, extra_environment, dry_run):
+def database_names_to_dump(database, config, environment, dry_run):
     '''
     Given a requested database config and a configuration dict, return the corresponding sequence of
     database names to dump. In the case of "all", query for the names of databases on the configured
@@ -58,9 +58,7 @@ def database_names_to_dump(database, config, extra_environment, dry_run):
         + ('--execute', 'show schemas')
     )
     logger.debug('Querying for "all" MariaDB databases to dump')
-    show_output = execute_command_and_capture_output(
-        show_command, extra_environment=extra_environment
-    )
+    show_output = execute_command_and_capture_output(show_command, environment=environment)
 
     return tuple(
         show_name
@@ -70,7 +68,7 @@ def database_names_to_dump(database, config, extra_environment, dry_run):
 
 
 def execute_dump_command(
-    database, config, dump_path, database_names, extra_environment, dry_run, dry_run_label
+    database, config, dump_path, database_names, environment, dry_run, dry_run_label
 ):
     '''
     Kick off a dump for the given MariaDB database (provided as a configuration dict) to a named
@@ -125,7 +123,7 @@ def execute_dump_command(
 
     return execute_command(
         dump_command,
-        extra_environment=extra_environment,
+        environment=environment,
         run_to_completion=False,
     )
 
@@ -167,16 +165,19 @@ def dump_data_sources(
 
     for database in databases:
         dump_path = make_dump_path(borgmatic_runtime_directory)
-        extra_environment = (
-            {
-                'MYSQL_PWD': borgmatic.hooks.credential.parse.resolve_credential(
-                    database['password'], config
-                )
-            }
-            if 'password' in database
-            else None
+        environment = dict(
+            os.environ,
+            **(
+                {
+                    'MYSQL_PWD': borgmatic.hooks.credential.parse.resolve_credential(
+                        database['password'], config
+                    )
+                }
+                if 'password' in database
+                else {}
+            ),
         )
-        dump_database_names = database_names_to_dump(database, config, extra_environment, dry_run)
+        dump_database_names = database_names_to_dump(database, config, environment, dry_run)
 
         if not dump_database_names:
             if dry_run:
@@ -194,7 +195,7 @@ def dump_data_sources(
                         config,
                         dump_path,
                         (dump_name,),
-                        extra_environment,
+                        environment,
                         dry_run,
                         dry_run_label,
                     )
@@ -206,7 +207,7 @@ def dump_data_sources(
                     config,
                     dump_path,
                     dump_database_names,
-                    extra_environment,
+                    environment,
                     dry_run,
                     dry_run_label,
                 )
@@ -308,7 +309,7 @@ def restore_data_source_dump(
         + (('--protocol', 'tcp') if hostname or port else ())
         + (('--user', username) if username else ())
     )
-    extra_environment = {'MYSQL_PWD': password} if password else None
+    environment = dict(os.environ, **({'MYSQL_PWD': password} if password else {}))
 
     logger.debug(f"Restoring MariaDB database {data_source['name']}{dry_run_label}")
     if dry_run:
@@ -321,5 +322,5 @@ def restore_data_source_dump(
         [extract_process],
         output_log_level=logging.DEBUG,
         input_file=extract_process.stdout,
-        extra_environment=extra_environment,
+        environment=environment,
     )

+ 20 - 19
borgmatic/hooks/data_source/mysql.py

@@ -26,7 +26,7 @@ def make_dump_path(base_directory):  # pragma: no cover
 SYSTEM_DATABASE_NAMES = ('information_schema', 'mysql', 'performance_schema', 'sys')
 
 
-def database_names_to_dump(database, config, extra_environment, dry_run):
+def database_names_to_dump(database, config, environment, dry_run):
     '''
     Given a requested database config and a configuration dict, return the corresponding sequence of
     database names to dump. In the case of "all", query for the names of databases on the configured
@@ -58,9 +58,7 @@ def database_names_to_dump(database, config, extra_environment, dry_run):
         + ('--execute', 'show schemas')
     )
     logger.debug('Querying for "all" MySQL databases to dump')
-    show_output = execute_command_and_capture_output(
-        show_command, extra_environment=extra_environment
-    )
+    show_output = execute_command_and_capture_output(show_command, environment=environment)
 
     return tuple(
         show_name
@@ -70,7 +68,7 @@ def database_names_to_dump(database, config, extra_environment, dry_run):
 
 
 def execute_dump_command(
-    database, config, dump_path, database_names, extra_environment, dry_run, dry_run_label
+    database, config, dump_path, database_names, environment, dry_run, dry_run_label
 ):
     '''
     Kick off a dump for the given MySQL/MariaDB database (provided as a configuration dict) to a
@@ -124,7 +122,7 @@ def execute_dump_command(
 
     return execute_command(
         dump_command,
-        extra_environment=extra_environment,
+        environment=environment,
         run_to_completion=False,
     )
 
@@ -166,16 +164,19 @@ def dump_data_sources(
 
     for database in databases:
         dump_path = make_dump_path(borgmatic_runtime_directory)
-        extra_environment = (
-            {
-                'MYSQL_PWD': borgmatic.hooks.credential.parse.resolve_credential(
-                    database['password'], config
-                )
-            }
-            if 'password' in database
-            else None
+        environment = dict(
+            os.environ,
+            **(
+                {
+                    'MYSQL_PWD': borgmatic.hooks.credential.parse.resolve_credential(
+                        database['password'], config
+                    )
+                }
+                if 'password' in database
+                else {}
+            ),
         )
-        dump_database_names = database_names_to_dump(database, config, extra_environment, dry_run)
+        dump_database_names = database_names_to_dump(database, config, environment, dry_run)
 
         if not dump_database_names:
             if dry_run:
@@ -193,7 +194,7 @@ def dump_data_sources(
                         config,
                         dump_path,
                         (dump_name,),
-                        extra_environment,
+                        environment,
                         dry_run,
                         dry_run_label,
                     )
@@ -205,7 +206,7 @@ def dump_data_sources(
                     config,
                     dump_path,
                     dump_database_names,
-                    extra_environment,
+                    environment,
                     dry_run,
                     dry_run_label,
                 )
@@ -307,7 +308,7 @@ def restore_data_source_dump(
         + (('--protocol', 'tcp') if hostname or port else ())
         + (('--user', username) if username else ())
     )
-    extra_environment = {'MYSQL_PWD': password} if password else None
+    environment = dict(os.environ, **({'MYSQL_PWD': password} if password else {}))
 
     logger.debug(f"Restoring MySQL database {data_source['name']}{dry_run_label}")
     if dry_run:
@@ -320,5 +321,5 @@ def restore_data_source_dump(
         [extract_process],
         output_log_level=logging.DEBUG,
         input_file=extract_process.stdout,
-        extra_environment=extra_environment,
+        environment=environment,
     )

+ 21 - 25
borgmatic/hooks/data_source/postgresql.py

@@ -25,16 +25,16 @@ def make_dump_path(base_directory):  # pragma: no cover
     return dump.make_data_source_dump_path(base_directory, 'postgresql_databases')
 
 
-def make_extra_environment(database, config, restore_connection_params=None):
+def make_environment(database, config, restore_connection_params=None):
     '''
-    Make the extra_environment dict from the given database configuration. If restore connection
-    params are given, this is for a restore operation.
+    Make an environment dict from the current environment variables and the given database
+    configuration. If restore connection params are given, this is for a restore operation.
     '''
-    extra = dict()
+    environment = dict(os.environ)
 
     try:
         if restore_connection_params:
-            extra['PGPASSWORD'] = borgmatic.hooks.credential.parse.resolve_credential(
+            environment['PGPASSWORD'] = borgmatic.hooks.credential.parse.resolve_credential(
                 (
                     restore_connection_params.get('password')
                     or database.get('restore_password', database['password'])
@@ -42,30 +42,30 @@ def make_extra_environment(database, config, restore_connection_params=None):
                 config,
             )
         else:
-            extra['PGPASSWORD'] = borgmatic.hooks.credential.parse.resolve_credential(
+            environment['PGPASSWORD'] = borgmatic.hooks.credential.parse.resolve_credential(
                 database['password'], config
             )
     except (AttributeError, KeyError):
         pass
 
     if 'ssl_mode' in database:
-        extra['PGSSLMODE'] = database['ssl_mode']
+        environment['PGSSLMODE'] = database['ssl_mode']
     if 'ssl_cert' in database:
-        extra['PGSSLCERT'] = database['ssl_cert']
+        environment['PGSSLCERT'] = database['ssl_cert']
     if 'ssl_key' in database:
-        extra['PGSSLKEY'] = database['ssl_key']
+        environment['PGSSLKEY'] = database['ssl_key']
     if 'ssl_root_cert' in database:
-        extra['PGSSLROOTCERT'] = database['ssl_root_cert']
+        environment['PGSSLROOTCERT'] = database['ssl_root_cert']
     if 'ssl_crl' in database:
-        extra['PGSSLCRL'] = database['ssl_crl']
+        environment['PGSSLCRL'] = database['ssl_crl']
 
-    return extra
+    return environment
 
 
 EXCLUDED_DATABASE_NAMES = ('template0', 'template1')
 
 
-def database_names_to_dump(database, config, extra_environment, dry_run):
+def database_names_to_dump(database, config, environment, dry_run):
     '''
     Given a requested database config and a configuration dict, return the corresponding sequence of
     database names to dump. In the case of "all" when a database format is given, query for the
@@ -100,9 +100,7 @@ def database_names_to_dump(database, config, extra_environment, dry_run):
         + (tuple(database['list_options'].split(' ')) if 'list_options' in database else ())
     )
     logger.debug('Querying for "all" PostgreSQL databases to dump')
-    list_output = execute_command_and_capture_output(
-        list_command, extra_environment=extra_environment
-    )
+    list_output = execute_command_and_capture_output(list_command, environment=environment)
 
     return tuple(
         row[0]
@@ -149,9 +147,9 @@ def dump_data_sources(
     logger.info(f'Dumping PostgreSQL databases{dry_run_label}')
 
     for database in databases:
-        extra_environment = make_extra_environment(database, config)
+        environment = make_environment(database, config)
         dump_path = make_dump_path(borgmatic_runtime_directory)
-        dump_database_names = database_names_to_dump(database, config, extra_environment, dry_run)
+        dump_database_names = database_names_to_dump(database, config, environment, dry_run)
 
         if not dump_database_names:
             if dry_run:
@@ -225,7 +223,7 @@ def dump_data_sources(
                 execute_command(
                     command,
                     shell=True,
-                    extra_environment=extra_environment,
+                    environment=environment,
                 )
             else:
                 dump.create_named_pipe_for_dump(dump_filename)
@@ -233,7 +231,7 @@ def dump_data_sources(
                     execute_command(
                         command,
                         shell=True,
-                        extra_environment=extra_environment,
+                        environment=environment,
                         run_to_completion=False,
                     )
                 )
@@ -370,9 +368,7 @@ def restore_data_source_dump(
         )
     )
 
-    extra_environment = make_extra_environment(
-        data_source, config, restore_connection_params=connection_params
-    )
+    environment = make_environment(data_source, config, restore_connection_params=connection_params)
 
     logger.debug(f"Restoring PostgreSQL database {data_source['name']}{dry_run_label}")
     if dry_run:
@@ -385,6 +381,6 @@ def restore_data_source_dump(
         [extract_process] if extract_process else [],
         output_log_level=logging.DEBUG,
         input_file=extract_process.stdout if extract_process else None,
-        extra_environment=extra_environment,
+        environment=environment,
     )
-    execute_command(analyze_command, extra_environment=extra_environment)
+    execute_command(analyze_command, environment=environment)

+ 15 - 15
tests/unit/borg/test_borg.py

@@ -17,7 +17,7 @@ def test_run_arbitrary_borg_calls_borg_with_flags():
         ('borg', 'break-lock', '::'),
         output_file=module.borgmatic.execute.DO_NOT_CAPTURE,
         shell=True,
-        extra_environment={'BORG_REPO': 'repo', 'ARCHIVE': ''},
+        environment={'BORG_REPO': 'repo', 'ARCHIVE': ''},
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -41,7 +41,7 @@ def test_run_arbitrary_borg_with_log_info_calls_borg_with_info_flag():
         ('borg', 'break-lock', '--info', '::'),
         output_file=module.borgmatic.execute.DO_NOT_CAPTURE,
         shell=True,
-        extra_environment={'BORG_REPO': 'repo', 'ARCHIVE': ''},
+        environment={'BORG_REPO': 'repo', 'ARCHIVE': ''},
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -66,7 +66,7 @@ def test_run_arbitrary_borg_with_log_debug_calls_borg_with_debug_flag():
         ('borg', 'break-lock', '--debug', '--show-rc', '::'),
         output_file=module.borgmatic.execute.DO_NOT_CAPTURE,
         shell=True,
-        extra_environment={'BORG_REPO': 'repo', 'ARCHIVE': ''},
+        environment={'BORG_REPO': 'repo', 'ARCHIVE': ''},
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -94,7 +94,7 @@ def test_run_arbitrary_borg_with_lock_wait_calls_borg_with_lock_wait_flags():
         ('borg', 'break-lock', '--lock-wait', '5', '::'),
         output_file=module.borgmatic.execute.DO_NOT_CAPTURE,
         shell=True,
-        extra_environment={'BORG_REPO': 'repo', 'ARCHIVE': ''},
+        environment={'BORG_REPO': 'repo', 'ARCHIVE': ''},
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -118,7 +118,7 @@ def test_run_arbitrary_borg_with_archive_calls_borg_with_archive_flag():
         ('borg', 'break-lock', "'::$ARCHIVE'"),
         output_file=module.borgmatic.execute.DO_NOT_CAPTURE,
         shell=True,
-        extra_environment={'BORG_REPO': 'repo', 'ARCHIVE': 'archive'},
+        environment={'BORG_REPO': 'repo', 'ARCHIVE': 'archive'},
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -143,7 +143,7 @@ def test_run_arbitrary_borg_with_local_path_calls_borg_via_local_path():
         ('borg1', 'break-lock', '::'),
         output_file=module.borgmatic.execute.DO_NOT_CAPTURE,
         shell=True,
-        extra_environment={'BORG_REPO': 'repo', 'ARCHIVE': ''},
+        environment={'BORG_REPO': 'repo', 'ARCHIVE': ''},
         working_directory=None,
         borg_local_path='borg1',
         borg_exit_codes=None,
@@ -169,7 +169,7 @@ def test_run_arbitrary_borg_with_exit_codes_calls_borg_using_them():
         ('borg', 'break-lock', '::'),
         output_file=module.borgmatic.execute.DO_NOT_CAPTURE,
         shell=True,
-        extra_environment={'BORG_REPO': 'repo', 'ARCHIVE': ''},
+        environment={'BORG_REPO': 'repo', 'ARCHIVE': ''},
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=borg_exit_codes,
@@ -195,7 +195,7 @@ def test_run_arbitrary_borg_with_remote_path_calls_borg_with_remote_path_flags()
         ('borg', 'break-lock', '--remote-path', 'borg1', '::'),
         output_file=module.borgmatic.execute.DO_NOT_CAPTURE,
         shell=True,
-        extra_environment={'BORG_REPO': 'repo', 'ARCHIVE': ''},
+        environment={'BORG_REPO': 'repo', 'ARCHIVE': ''},
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -222,7 +222,7 @@ def test_run_arbitrary_borg_with_remote_path_injection_attack_gets_escaped():
         ('borg', 'break-lock', '--remote-path', "'borg1; naughty-command'", '::'),
         output_file=module.borgmatic.execute.DO_NOT_CAPTURE,
         shell=True,
-        extra_environment={'BORG_REPO': 'repo', 'ARCHIVE': ''},
+        environment={'BORG_REPO': 'repo', 'ARCHIVE': ''},
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -247,7 +247,7 @@ def test_run_arbitrary_borg_passes_borg_specific_flags_to_borg():
         ('borg', 'list', '--progress', '::'),
         output_file=module.borgmatic.execute.DO_NOT_CAPTURE,
         shell=True,
-        extra_environment={'BORG_REPO': 'repo', 'ARCHIVE': ''},
+        environment={'BORG_REPO': 'repo', 'ARCHIVE': ''},
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -271,7 +271,7 @@ def test_run_arbitrary_borg_omits_dash_dash_in_flags_passed_to_borg():
         ('borg', 'break-lock', '::'),
         output_file=module.borgmatic.execute.DO_NOT_CAPTURE,
         shell=True,
-        extra_environment={'BORG_REPO': 'repo', 'ARCHIVE': ''},
+        environment={'BORG_REPO': 'repo', 'ARCHIVE': ''},
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -295,7 +295,7 @@ def test_run_arbitrary_borg_without_borg_specific_flags_does_not_raise():
         ('borg',),
         output_file=module.borgmatic.execute.DO_NOT_CAPTURE,
         shell=True,
-        extra_environment={'BORG_REPO': 'repo', 'ARCHIVE': ''},
+        environment={'BORG_REPO': 'repo', 'ARCHIVE': ''},
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -319,7 +319,7 @@ def test_run_arbitrary_borg_passes_key_sub_command_to_borg_before_injected_flags
         ('borg', 'key', 'export', '--info', '::'),
         output_file=module.borgmatic.execute.DO_NOT_CAPTURE,
         shell=True,
-        extra_environment={'BORG_REPO': 'repo', 'ARCHIVE': ''},
+        environment={'BORG_REPO': 'repo', 'ARCHIVE': ''},
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -344,7 +344,7 @@ def test_run_arbitrary_borg_passes_debug_sub_command_to_borg_before_injected_fla
         ('borg', 'debug', 'dump-manifest', '--info', '::', 'path'),
         output_file=module.borgmatic.execute.DO_NOT_CAPTURE,
         shell=True,
-        extra_environment={'BORG_REPO': 'repo', 'ARCHIVE': ''},
+        environment={'BORG_REPO': 'repo', 'ARCHIVE': ''},
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -371,7 +371,7 @@ def test_run_arbitrary_borg_calls_borg_with_working_directory():
         ('borg', 'break-lock', '::'),
         output_file=module.borgmatic.execute.DO_NOT_CAPTURE,
         shell=True,
-        extra_environment={'BORG_REPO': 'repo', 'ARCHIVE': ''},
+        environment={'BORG_REPO': 'repo', 'ARCHIVE': ''},
         working_directory='/working/dir',
         borg_local_path='borg',
         borg_exit_codes=None,

+ 1 - 1
tests/unit/borg/test_break_lock.py

@@ -14,7 +14,7 @@ def insert_execute_command_mock(command, working_directory=None, borg_exit_codes
     )
     flexmock(module).should_receive('execute_command').with_args(
         command,
-        extra_environment=None,
+        environment=None,
         working_directory=working_directory,
         borg_local_path=command[0],
         borg_exit_codes=borg_exit_codes,

+ 1 - 1
tests/unit/borg/test_change_passphrase.py

@@ -25,7 +25,7 @@ def insert_execute_command_mock(
         command,
         output_file=output_file,
         output_log_level=module.logging.ANSWER,
-        extra_environment=None,
+        environment=None,
         working_directory=working_directory,
         borg_local_path=command[0],
         borg_exit_codes=borg_exit_codes,

+ 7 - 7
tests/unit/borg/test_check.py

@@ -18,7 +18,7 @@ def insert_execute_command_mock(
     flexmock(module).should_receive('execute_command').with_args(
         command,
         output_file=output_file,
-        extra_environment=None,
+        environment=None,
         working_directory=working_directory,
         borg_local_path=command[0],
         borg_exit_codes=borg_exit_codes,
@@ -342,7 +342,7 @@ def test_check_archives_with_progress_passes_through_to_borg():
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'check', '--progress', 'repo'),
         output_file=module.DO_NOT_CAPTURE,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -377,7 +377,7 @@ def test_check_archives_with_repair_passes_through_to_borg():
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'check', '--repair', 'repo'),
         output_file=module.DO_NOT_CAPTURE,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -412,7 +412,7 @@ def test_check_archives_with_max_duration_flag_passes_through_to_borg():
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'check', '--max-duration', '33', 'repo'),
         output_file=None,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -447,7 +447,7 @@ def test_check_archives_with_max_duration_option_passes_through_to_borg():
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'check', '--max-duration', '33', 'repo'),
         output_file=None,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -610,7 +610,7 @@ def test_check_archives_with_max_duration_flag_overrides_max_duration_option():
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'check', '--max-duration', '44', 'repo'),
         output_file=None,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -963,7 +963,7 @@ def test_check_archives_with_match_archives_passes_through_to_borg():
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'check', '--match-archives', 'foo-*', 'repo'),
         output_file=None,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,

+ 1 - 1
tests/unit/borg/test_compact.py

@@ -17,7 +17,7 @@ def insert_execute_command_mock(
     flexmock(module).should_receive('execute_command').with_args(
         compact_command,
         output_log_level=output_log_level,
-        extra_environment=None,
+        environment=None,
         working_directory=working_directory,
         borg_local_path=compact_command[0],
         borg_exit_codes=borg_exit_codes,

+ 19 - 19
tests/unit/borg/test_create.py

@@ -296,7 +296,7 @@ def test_collect_special_file_paths_omits_exclude_no_dump_flag_from_command():
         ('borg', 'create', '--dry-run', '--list'),
         capture_stderr=True,
         working_directory=None,
-        extra_environment=None,
+        environment=None,
         borg_local_path='borg',
         borg_exit_codes=None,
     ).and_return('Processing files ...\n- /foo\n+ /bar\n- /baz').once()
@@ -971,7 +971,7 @@ def test_create_archive_calls_borg_with_parameters():
         borg_local_path='borg',
         borg_exit_codes=None,
         working_directory=None,
-        extra_environment=None,
+        environment=None,
     )
 
     module.create_archive(
@@ -1005,7 +1005,7 @@ def test_create_archive_calls_borg_with_environment():
         borg_local_path='borg',
         borg_exit_codes=None,
         working_directory=None,
-        extra_environment=environment,
+        environment=environment,
     )
 
     module.create_archive(
@@ -1038,7 +1038,7 @@ def test_create_archive_with_log_info_calls_borg_with_info_parameter():
         borg_local_path='borg',
         borg_exit_codes=None,
         working_directory=None,
-        extra_environment=None,
+        environment=None,
     )
     insert_logging_mock(logging.INFO)
 
@@ -1068,7 +1068,7 @@ def test_create_archive_with_log_info_and_json_suppresses_most_borg_output():
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('borg', 'create', '--json') + REPO_ARCHIVE,
         working_directory=None,
-        extra_environment=None,
+        environment=None,
         borg_local_path='borg',
         borg_exit_codes=None,
     )
@@ -1105,7 +1105,7 @@ def test_create_archive_with_log_debug_calls_borg_with_debug_parameter():
         borg_local_path='borg',
         borg_exit_codes=None,
         working_directory=None,
-        extra_environment=None,
+        environment=None,
     )
     insert_logging_mock(logging.DEBUG)
 
@@ -1135,7 +1135,7 @@ def test_create_archive_with_log_debug_and_json_suppresses_most_borg_output():
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('borg', 'create', '--json') + REPO_ARCHIVE,
         working_directory=None,
-        extra_environment=None,
+        environment=None,
         borg_local_path='borg',
         borg_exit_codes=None,
     )
@@ -1174,7 +1174,7 @@ def test_create_archive_with_stats_and_dry_run_calls_borg_without_stats():
         borg_local_path='borg',
         borg_exit_codes=None,
         working_directory=None,
-        extra_environment=None,
+        environment=None,
     )
     insert_logging_mock(logging.INFO)
 
@@ -1211,7 +1211,7 @@ def test_create_archive_with_working_directory_calls_borg_with_working_directory
         borg_local_path='borg',
         borg_exit_codes=None,
         working_directory='/working/dir',
-        extra_environment=None,
+        environment=None,
     )
 
     module.create_archive(
@@ -1246,7 +1246,7 @@ def test_create_archive_with_exit_codes_calls_borg_using_them():
         borg_local_path='borg',
         borg_exit_codes=borg_exit_codes,
         working_directory=None,
-        extra_environment=None,
+        environment=None,
     )
 
     module.create_archive(
@@ -1280,7 +1280,7 @@ def test_create_archive_with_stats_calls_borg_with_stats_parameter_and_answer_ou
         borg_local_path='borg',
         borg_exit_codes=None,
         working_directory=None,
-        extra_environment=None,
+        environment=None,
     )
 
     module.create_archive(
@@ -1318,7 +1318,7 @@ def test_create_archive_with_files_calls_borg_with_answer_output_log_level():
         borg_local_path='borg',
         borg_exit_codes=None,
         working_directory=None,
-        extra_environment=None,
+        environment=None,
     )
 
     module.create_archive(
@@ -1352,7 +1352,7 @@ def test_create_archive_with_progress_and_log_info_calls_borg_with_progress_para
         borg_local_path='borg',
         borg_exit_codes=None,
         working_directory=None,
-        extra_environment=None,
+        environment=None,
     )
     insert_logging_mock(logging.INFO)
 
@@ -1387,7 +1387,7 @@ def test_create_archive_with_progress_calls_borg_with_progress_parameter():
         borg_local_path='borg',
         borg_exit_codes=None,
         working_directory=None,
-        extra_environment=None,
+        environment=None,
     )
 
     module.create_archive(
@@ -1433,7 +1433,7 @@ def test_create_archive_with_progress_and_stream_processes_calls_borg_with_progr
         borg_local_path='borg',
         borg_exit_codes=None,
         working_directory=None,
-        extra_environment=None,
+        environment=None,
     )
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         create_command,
@@ -1443,7 +1443,7 @@ def test_create_archive_with_progress_and_stream_processes_calls_borg_with_progr
         borg_local_path='borg',
         borg_exit_codes=None,
         working_directory=None,
-        extra_environment=None,
+        environment=None,
     )
 
     module.create_archive(
@@ -1474,7 +1474,7 @@ def test_create_archive_with_json_calls_borg_with_json_flag():
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('borg', 'create', '--json') + REPO_ARCHIVE,
         working_directory=None,
-        extra_environment=None,
+        environment=None,
         borg_local_path='borg',
         borg_exit_codes=None,
     ).and_return('[]')
@@ -1508,7 +1508,7 @@ def test_create_archive_with_stats_and_json_calls_borg_without_stats_flag():
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('borg', 'create', '--json') + REPO_ARCHIVE,
         working_directory=None,
-        extra_environment=None,
+        environment=None,
         borg_local_path='borg',
         borg_exit_codes=None,
     ).and_return('[]')
@@ -1549,7 +1549,7 @@ def test_create_archive_calls_borg_with_working_directory():
         borg_local_path='borg',
         borg_exit_codes=None,
         working_directory='/working/dir',
-        extra_environment=None,
+        environment=None,
     )
 
     module.create_archive(

+ 3 - 3
tests/unit/borg/test_delete.py

@@ -370,9 +370,9 @@ def test_delete_archives_calls_borg_delete_with_working_directory():
     flexmock(module.borgmatic.borg.repo_delete).should_receive('delete_repository').never()
     command = flexmock()
     flexmock(module).should_receive('make_delete_command').and_return(command)
-    extra_environment = flexmock()
+    environment = flexmock()
     flexmock(module.borgmatic.borg.environment).should_receive('make_environment').and_return(
-        extra_environment
+        environment
     )
     flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
         '/working/dir'
@@ -380,7 +380,7 @@ def test_delete_archives_calls_borg_delete_with_working_directory():
     flexmock(module.borgmatic.execute).should_receive('execute_command').with_args(
         command,
         output_log_level=logging.ANSWER,
-        extra_environment=extra_environment,
+        environment=environment,
         working_directory='/working/dir',
         borg_local_path='borg',
         borg_exit_codes=None,

+ 72 - 46
tests/unit/borg/test_environment.py

@@ -4,6 +4,12 @@ from borgmatic.borg import environment as module
 
 
 def test_make_environment_with_passcommand_should_call_it_and_set_passphrase_file_descriptor_in_environment():
+    flexmock(module.os).should_receive('environ').and_return(
+        {'USER': 'root', 'BORG_PASSCOMMAND': 'nope'}
+    )
+    flexmock(module.borgmatic.hooks.credential.parse).should_receive(
+        'resolve_credential'
+    ).and_return(None)
     flexmock(module.borgmatic.borg.passcommand).should_receive(
         'get_passphrase_from_passcommand'
     ).and_return('passphrase')
@@ -14,133 +20,153 @@ def test_make_environment_with_passcommand_should_call_it_and_set_passphrase_fil
 
     environment = module.make_environment({'encryption_passcommand': 'command'})
 
-    assert not environment.get('BORG_PASSCOMMAND')
+    assert environment.get('BORG_PASSPHRASE') is None
+    assert environment.get('BORG_PASSCOMMAND') is None
     assert environment.get('BORG_PASSPHRASE_FD') == '3'
 
 
-def test_make_environment_with_passphrase_should_set_environment():
-    flexmock(module.borgmatic.borg.passcommand).should_receive(
-        'get_passphrase_from_passcommand'
-    ).and_return(None)
-    flexmock(module.os).should_receive('pipe').never()
-    flexmock(module.os.environ).should_receive('get').and_return(None)
+def test_make_environment_with_passphrase_should_set_passphrase_file_descriptor_in_environment():
+    flexmock(module.os).should_receive('environ').and_return(
+        {'USER': 'root', 'BORG_PASSPHRASE': 'nope', 'BORG_PASSCOMMAND': 'nope'}
+    )
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
+    flexmock(module.borgmatic.borg.passcommand).should_receive(
+        'get_passphrase_from_passcommand'
+    ).and_return(None)
+    flexmock(module.os).should_receive('pipe').and_return((3, 4))
+    flexmock(module.os).should_receive('write')
+    flexmock(module.os).should_receive('close')
+    flexmock(module.os).should_receive('set_inheritable')
 
     environment = module.make_environment({'encryption_passphrase': 'pass'})
 
-    assert environment.get('BORG_PASSPHRASE') == 'pass'
+    assert environment.get('BORG_PASSPHRASE') is None
+    assert environment.get('BORG_PASSCOMMAND') is None
+    assert environment.get('BORG_PASSPHRASE_FD') == '3'
 
 
-def test_make_environment_with_credential_tag_passphrase_should_load_it_and_set_environment():
+def test_make_environment_with_credential_tag_passphrase_should_load_it_and_set_passphrase_file_descriptor_in_environment():
+    flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     config = {'encryption_passphrase': '{credential systemd pass}'}
-    flexmock(module.borgmatic.borg.passcommand).should_receive(
-        'get_passphrase_from_passcommand'
-    ).and_return(None)
-    flexmock(module.os).should_receive('pipe').never()
-    flexmock(module.os.environ).should_receive('get').and_return(None)
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential',
     ).with_args('{credential systemd pass}', config).and_return('pass')
+    flexmock(module.borgmatic.borg.passcommand).should_receive(
+        'get_passphrase_from_passcommand'
+    ).never()
+    flexmock(module.os).should_receive('pipe').and_return((3, 4))
+    flexmock(module.os).should_receive('write')
+    flexmock(module.os).should_receive('close')
+    flexmock(module.os).should_receive('set_inheritable')
 
     environment = module.make_environment(config)
 
-    assert environment.get('BORG_PASSPHRASE') == 'pass'
+    assert environment.get('BORG_PASSPHRASE') is None
+    assert environment.get('BORG_PASSPHRASE_FD') == '3'
 
 
 def test_make_environment_with_ssh_command_should_set_environment():
-    flexmock(module.borgmatic.borg.passcommand).should_receive(
-        'get_passphrase_from_passcommand'
+    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()
-    flexmock(module.os.environ).should_receive('get').and_return(None)
     environment = module.make_environment({'ssh_command': 'ssh -C'})
 
     assert environment.get('BORG_RSH') == 'ssh -C'
 
 
 def test_make_environment_without_configuration_sets_certain_environment_variables():
-    flexmock(module.borgmatic.borg.passcommand).should_receive(
-        'get_passphrase_from_passcommand'
+    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()
-    flexmock(module.os.environ).should_receive('get').and_return(None)
     environment = module.make_environment({})
 
     # Default environment variables.
     assert environment == {
+        'USER': 'root',
         'BORG_EXIT_CODES': 'modern',
         'BORG_RELOCATED_REPO_ACCESS_IS_OK': 'no',
         'BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK': 'no',
     }
 
 
-def test_make_environment_without_configuration_does_not_set_certain_environment_variables_if_already_set():
-    flexmock(module.borgmatic.borg.passcommand).should_receive(
-        'get_passphrase_from_passcommand'
+def test_make_environment_without_configuration_passes_through_default_environment_variables_untouched():
+    flexmock(module.os).should_receive('environ').and_return(
+        {
+            'USER': 'root',
+            'BORG_RELOCATED_REPO_ACCESS_IS_OK': 'yup',
+            'BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK': 'nah',
+        }
+    )
+    flexmock(module.borgmatic.hooks.credential.parse).should_receive(
+        'resolve_credential'
     ).and_return(None)
     flexmock(module.os).should_receive('pipe').never()
-    flexmock(module.os.environ).should_receive('get').with_args(
-        'BORG_RELOCATED_REPO_ACCESS_IS_OK'
-    ).and_return('yup')
-    flexmock(module.os.environ).should_receive('get').with_args(
-        'BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK'
-    ).and_return('nah')
     environment = module.make_environment({})
 
-    assert environment == {'BORG_EXIT_CODES': 'modern'}
+    assert environment == {
+        'USER': 'root',
+        'BORG_RELOCATED_REPO_ACCESS_IS_OK': 'yup',
+        'BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK': 'nah',
+        'BORG_EXIT_CODES': 'modern',
+    }
 
 
 def test_make_environment_with_relocated_repo_access_true_should_set_environment_yes():
-    flexmock(module.borgmatic.borg.passcommand).should_receive(
-        'get_passphrase_from_passcommand'
+    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()
-    flexmock(module.os.environ).should_receive('get').and_return(None)
     environment = module.make_environment({'relocated_repo_access_is_ok': True})
 
     assert environment.get('BORG_RELOCATED_REPO_ACCESS_IS_OK') == 'yes'
 
 
 def test_make_environment_with_relocated_repo_access_false_should_set_environment_no():
-    flexmock(module.borgmatic.borg.passcommand).should_receive(
-        'get_passphrase_from_passcommand'
+    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()
-    flexmock(module.os.environ).should_receive('get').and_return(None)
     environment = module.make_environment({'relocated_repo_access_is_ok': False})
 
     assert environment.get('BORG_RELOCATED_REPO_ACCESS_IS_OK') == 'no'
 
 
 def test_make_environment_check_i_know_what_i_am_doing_true_should_set_environment_YES():
-    flexmock(module.borgmatic.borg.passcommand).should_receive(
-        'get_passphrase_from_passcommand'
+    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()
-    flexmock(module.os.environ).should_receive('get').and_return(None)
     environment = module.make_environment({'check_i_know_what_i_am_doing': True})
 
     assert environment.get('BORG_CHECK_I_KNOW_WHAT_I_AM_DOING') == 'YES'
 
 
 def test_make_environment_check_i_know_what_i_am_doing_false_should_set_environment_NO():
-    flexmock(module.borgmatic.borg.passcommand).should_receive(
-        'get_passphrase_from_passcommand'
+    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()
-    flexmock(module.os.environ).should_receive('get').and_return(None)
     environment = module.make_environment({'check_i_know_what_i_am_doing': False})
 
     assert environment.get('BORG_CHECK_I_KNOW_WHAT_I_AM_DOING') == 'NO'
 
 
 def test_make_environment_with_integer_variable_value():
-    flexmock(module.borgmatic.borg.passcommand).should_receive(
-        'get_passphrase_from_passcommand'
+    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()
-    flexmock(module.os.environ).should_receive('get').and_return(None)
     environment = module.make_environment({'borg_files_cache_ttl': 40})
+
     assert environment.get('BORG_FILES_CACHE_TTL') == '40'

+ 1 - 1
tests/unit/borg/test_export_key.py

@@ -22,7 +22,7 @@ def insert_execute_command_mock(
         command,
         output_file=output_file,
         output_log_level=module.logging.ANSWER,
-        extra_environment=None,
+        environment=None,
         working_directory=working_directory,
         borg_local_path=command[0],
         borg_exit_codes=borg_exit_codes,

+ 1 - 1
tests/unit/borg/test_export_tar.py

@@ -23,7 +23,7 @@ def insert_execute_command_mock(
         command,
         output_file=None if capture else module.DO_NOT_CAPTURE,
         output_log_level=output_log_level,
-        extra_environment=None,
+        environment=None,
         working_directory=working_directory,
         borg_local_path=borg_local_path,
         borg_exit_codes=borg_exit_codes,

+ 4 - 4
tests/unit/borg/test_extract.py

@@ -12,7 +12,7 @@ def insert_execute_command_mock(command, destination_path=None, borg_exit_codes=
     flexmock(module.environment).should_receive('make_environment')
     flexmock(module).should_receive('execute_command').with_args(
         command,
-        extra_environment=None,
+        environment=None,
         working_directory=destination_path,
         borg_local_path=command[0],
         borg_exit_codes=borg_exit_codes,
@@ -587,7 +587,7 @@ def test_extract_archive_calls_borg_with_progress_parameter():
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'extract', '--progress', 'repo::archive'),
         output_file=module.DO_NOT_CAPTURE,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -639,7 +639,7 @@ def test_extract_archive_calls_borg_with_stdout_parameter_and_returns_process():
         ('borg', 'extract', '--stdout', 'repo::archive'),
         output_file=module.subprocess.PIPE,
         run_to_completion=False,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -674,7 +674,7 @@ def test_extract_archive_skips_abspath_for_remote_repository():
     flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'extract', 'server:repo::archive'),
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,

+ 2 - 2
tests/unit/borg/test_info.py

@@ -514,7 +514,7 @@ def test_display_archives_info_calls_borg_with_working_directory():
     )
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         full_command=object,
-        extra_environment=object,
+        environment=object,
         working_directory='/working/dir',
         borg_local_path=object,
         borg_exit_codes=object,
@@ -523,7 +523,7 @@ def test_display_archives_info_calls_borg_with_working_directory():
     flexmock(module).should_receive('execute_command').with_args(
         full_command=object,
         output_log_level=object,
-        extra_environment=object,
+        environment=object,
         working_directory='/working/dir',
         borg_local_path=object,
         borg_exit_codes=object,

+ 12 - 12
tests/unit/borg/test_list.py

@@ -353,7 +353,7 @@ def test_list_archive_calls_borg_with_flags():
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'list', 'repo::archive'),
         output_log_level=module.borgmatic.logger.ANSWER,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -419,7 +419,7 @@ def test_list_archive_calls_borg_with_local_path():
     flexmock(module).should_receive('execute_command').with_args(
         ('borg2', 'list', 'repo::archive'),
         output_log_level=module.borgmatic.logger.ANSWER,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg2',
         borg_exit_codes=None,
@@ -469,7 +469,7 @@ def test_list_archive_calls_borg_using_exit_codes():
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'list', 'repo::archive'),
         output_log_level=module.borgmatic.logger.ANSWER,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=borg_exit_codes,
@@ -507,7 +507,7 @@ def test_list_archive_calls_borg_multiple_times_with_find_paths():
     flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('borg', 'list', 'repo'),
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -520,7 +520,7 @@ def test_list_archive_calls_borg_multiple_times_with_find_paths():
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'list', 'repo::archive1') + glob_paths,
         output_log_level=module.borgmatic.logger.ANSWER,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -528,7 +528,7 @@ def test_list_archive_calls_borg_multiple_times_with_find_paths():
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'list', 'repo::archive2') + glob_paths,
         output_log_level=module.borgmatic.logger.ANSWER,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -576,7 +576,7 @@ def test_list_archive_calls_borg_with_archive():
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'list', 'repo::archive'),
         output_log_level=module.borgmatic.logger.ANSWER,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -699,7 +699,7 @@ def test_list_archive_with_archive_ignores_archive_filter_flag(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'list', 'repo::archive'),
         output_log_level=module.borgmatic.logger.ANSWER,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -759,7 +759,7 @@ def test_list_archive_with_find_paths_allows_archive_filter_flag_but_only_passes
     flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('borg', 'repo-list', '--repo', 'repo'),
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -808,7 +808,7 @@ def test_list_archive_with_find_paths_allows_archive_filter_flag_but_only_passes
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'list', '--repo', 'repo', 'archive1') + glob_paths,
         output_log_level=module.borgmatic.logger.ANSWER,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -816,7 +816,7 @@ def test_list_archive_with_find_paths_allows_archive_filter_flag_but_only_passes
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'list', '--repo', 'repo', 'archive2') + glob_paths,
         output_log_level=module.borgmatic.logger.ANSWER,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -875,7 +875,7 @@ def test_list_archive_calls_borg_with_working_directory():
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'list', 'repo::archive'),
         output_log_level=module.borgmatic.logger.ANSWER,
-        extra_environment=None,
+        environment=None,
         working_directory='/working/dir',
         borg_local_path='borg',
         borg_exit_codes=None,

+ 3 - 3
tests/unit/borg/test_mount.py

@@ -14,7 +14,7 @@ def insert_execute_command_mock(command, working_directory=None, borg_exit_codes
     )
     flexmock(module).should_receive('execute_command').with_args(
         command,
-        extra_environment=None,
+        environment=None,
         working_directory=working_directory,
         borg_local_path=command[0],
         borg_exit_codes=borg_exit_codes,
@@ -262,7 +262,7 @@ def test_mount_archive_calls_borg_with_foreground_parameter():
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'mount', '--foreground', 'repo::archive', '/mnt'),
         output_file=module.DO_NOT_CAPTURE,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -337,7 +337,7 @@ def test_mount_archive_with_date_based_matching_calls_borg_with_date_based_flags
             'repo',
             '/mnt',
         ),
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,

+ 11 - 58
tests/unit/borg/test_passcommand.py

@@ -3,26 +3,13 @@ from flexmock import flexmock
 from borgmatic.borg import passcommand as module
 
 
-def test_run_passcommand_with_passphrase_configured_bails():
-    module.run_passcommand.cache_clear()
-    flexmock(module.borgmatic.execute).should_receive('execute_command_and_capture_output').never()
-
-    assert (
-        module.run_passcommand('passcommand', passphrase_configured=True, working_directory=None)
-        is None
-    )
-
-
-def test_run_passcommand_without_passphrase_configured_executes_passcommand():
+def test_run_passcommand_does_not_raise():
     module.run_passcommand.cache_clear()
     flexmock(module.borgmatic.execute).should_receive(
         'execute_command_and_capture_output'
-    ).and_return('passphrase').once()
+    ).and_return('passphrase')
 
-    assert (
-        module.run_passcommand('passcommand', passphrase_configured=False, working_directory=None)
-        == 'passphrase'
-    )
+    assert module.run_passcommand('passcommand', working_directory=None) == 'passphrase'
 
 
 def test_get_passphrase_from_passcommand_with_configured_passcommand_runs_it():
@@ -30,9 +17,9 @@ def test_get_passphrase_from_passcommand_with_configured_passcommand_runs_it():
     flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
         '/working'
     )
-    flexmock(module).should_receive('run_passcommand').with_args(
-        'command', False, '/working'
-    ).and_return('passphrase').once()
+    flexmock(module).should_receive('run_passcommand').with_args('command', '/working').and_return(
+        'passphrase'
+    ).once()
 
     assert (
         module.get_passphrase_from_passcommand(
@@ -42,38 +29,10 @@ def test_get_passphrase_from_passcommand_with_configured_passcommand_runs_it():
     )
 
 
-def test_get_passphrase_from_passcommand_with_configured_passphrase_and_passcommand_detects_passphrase():
-    module.run_passcommand.cache_clear()
-    flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
-        '/working'
-    )
-    flexmock(module).should_receive('run_passcommand').with_args(
-        'command', True, '/working'
-    ).and_return(None).once()
-
-    assert (
-        module.get_passphrase_from_passcommand(
-            {'encryption_passphrase': 'passphrase', 'encryption_passcommand': 'command'},
-        )
-        is None
-    )
-
-
-def test_get_passphrase_from_passcommand_with_configured_blank_passphrase_and_passcommand_detects_passphrase():
-    module.run_passcommand.cache_clear()
-    flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
-        '/working'
-    )
-    flexmock(module).should_receive('run_passcommand').with_args(
-        'command', True, '/working'
-    ).and_return(None).once()
+def test_get_passphrase_from_passcommand_without_configured_passcommand_bails():
+    flexmock(module).should_receive('run_passcommand').never()
 
-    assert (
-        module.get_passphrase_from_passcommand(
-            {'encryption_passphrase': '', 'encryption_passcommand': 'command'},
-        )
-        is None
-    )
+    assert module.get_passphrase_from_passcommand({}) is None
 
 
 def test_run_passcommand_caches_passcommand_after_first_call():
@@ -82,11 +41,5 @@ def test_run_passcommand_caches_passcommand_after_first_call():
         'execute_command_and_capture_output'
     ).and_return('passphrase').once()
 
-    assert (
-        module.run_passcommand('passcommand', passphrase_configured=False, working_directory=None)
-        == 'passphrase'
-    )
-    assert (
-        module.run_passcommand('passcommand', passphrase_configured=False, working_directory=None)
-        == 'passphrase'
-    )
+    assert module.run_passcommand('passcommand', working_directory=None) == 'passphrase'
+    assert module.run_passcommand('passcommand', working_directory=None) == 'passphrase'

+ 2 - 2
tests/unit/borg/test_prune.py

@@ -17,7 +17,7 @@ def insert_execute_command_mock(
     flexmock(module).should_receive('execute_command').with_args(
         prune_command,
         output_log_level=output_log_level,
-        extra_environment=None,
+        environment=None,
         working_directory=working_directory,
         borg_local_path=prune_command[0],
         borg_exit_codes=borg_exit_codes,
@@ -497,7 +497,7 @@ def test_prune_archives_with_date_based_matching_calls_borg_with_date_based_flag
             'repo',
         ),
         output_log_level=logging.INFO,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,

+ 1 - 1
tests/unit/borg/test_repo_create.py

@@ -36,7 +36,7 @@ def insert_repo_create_command_mock(
     flexmock(module).should_receive('execute_command').with_args(
         repo_create_command,
         output_file=module.DO_NOT_CAPTURE,
-        extra_environment=None,
+        environment=None,
         working_directory=working_directory,
         borg_local_path=repo_create_command[0],
         borg_exit_codes=borg_exit_codes,

+ 4 - 4
tests/unit/borg/test_repo_delete.py

@@ -290,7 +290,7 @@ def test_delete_repository_with_defaults_does_not_capture_output():
         command,
         output_log_level=module.logging.ANSWER,
         output_file=module.borgmatic.execute.DO_NOT_CAPTURE,
-        extra_environment=object,
+        environment=object,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -319,7 +319,7 @@ def test_delete_repository_with_force_captures_output():
         command,
         output_log_level=module.logging.ANSWER,
         output_file=None,
-        extra_environment=object,
+        environment=object,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -348,7 +348,7 @@ def test_delete_repository_with_cache_only_captures_output():
         command,
         output_log_level=module.logging.ANSWER,
         output_file=None,
-        extra_environment=object,
+        environment=object,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -379,7 +379,7 @@ def test_delete_repository_calls_borg_with_working_directory():
         command,
         output_log_level=module.logging.ANSWER,
         output_file=module.borgmatic.execute.DO_NOT_CAPTURE,
-        extra_environment=object,
+        environment=object,
         working_directory='/working/dir',
         borg_local_path='borg',
         borg_exit_codes=None,

+ 25 - 25
tests/unit/borg/test_repo_info.py

@@ -24,7 +24,7 @@ def test_display_repository_info_calls_borg_with_flags():
     flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('borg', 'repo-info', '--json', '--repo', 'repo'),
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -33,7 +33,7 @@ def test_display_repository_info_calls_borg_with_flags():
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'repo-info', '--repo', 'repo'),
         output_log_level=module.borgmatic.logger.ANSWER,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -60,7 +60,7 @@ def test_display_repository_info_without_borg_features_calls_borg_with_info_sub_
     flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('borg', 'repo-info', '--json', 'repo'),
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -69,7 +69,7 @@ def test_display_repository_info_without_borg_features_calls_borg_with_info_sub_
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'info', 'repo'),
         output_log_level=module.borgmatic.logger.ANSWER,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -101,7 +101,7 @@ def test_display_repository_info_with_log_info_calls_borg_with_info_flag():
     flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('borg', 'repo-info', '--info', '--json', '--repo', 'repo'),
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -110,7 +110,7 @@ def test_display_repository_info_with_log_info_calls_borg_with_info_flag():
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'repo-info', '--info', '--repo', 'repo'),
         output_log_level=module.borgmatic.logger.ANSWER,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -142,7 +142,7 @@ def test_display_repository_info_with_log_info_and_json_suppresses_most_borg_out
     flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('borg', 'repo-info', '--json', '--repo', 'repo'),
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -178,7 +178,7 @@ def test_display_repository_info_with_log_debug_calls_borg_with_debug_flag():
     flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('borg', 'repo-info', '--debug', '--show-rc', '--json', '--repo', 'repo'),
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -187,7 +187,7 @@ def test_display_repository_info_with_log_debug_calls_borg_with_debug_flag():
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'repo-info', '--debug', '--show-rc', '--repo', 'repo'),
         output_log_level=module.borgmatic.logger.ANSWER,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -220,7 +220,7 @@ def test_display_repository_info_with_log_debug_and_json_suppresses_most_borg_ou
     flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('borg', 'repo-info', '--json', '--repo', 'repo'),
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -256,7 +256,7 @@ def test_display_repository_info_with_json_calls_borg_with_json_flag():
     flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('borg', 'repo-info', '--json', '--repo', 'repo'),
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -291,7 +291,7 @@ def test_display_repository_info_with_local_path_calls_borg_via_local_path():
     flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('borg1', 'repo-info', '--json', '--repo', 'repo'),
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -300,7 +300,7 @@ def test_display_repository_info_with_local_path_calls_borg_via_local_path():
     flexmock(module).should_receive('execute_command').with_args(
         ('borg1', 'repo-info', '--repo', 'repo'),
         output_log_level=module.borgmatic.logger.ANSWER,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg1',
         borg_exit_codes=None,
@@ -334,7 +334,7 @@ def test_display_repository_info_with_exit_codes_calls_borg_using_them():
     borg_exit_codes = flexmock()
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('borg', 'repo-info', '--json', '--repo', 'repo'),
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=borg_exit_codes,
@@ -343,7 +343,7 @@ def test_display_repository_info_with_exit_codes_calls_borg_using_them():
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'repo-info', '--repo', 'repo'),
         output_log_level=module.borgmatic.logger.ANSWER,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=borg_exit_codes,
@@ -375,7 +375,7 @@ def test_display_repository_info_with_remote_path_calls_borg_with_remote_path_fl
     flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('borg', 'repo-info', '--remote-path', 'borg1', '--json', '--repo', 'repo'),
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -384,7 +384,7 @@ def test_display_repository_info_with_remote_path_calls_borg_with_remote_path_fl
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'repo-info', '--remote-path', 'borg1', '--repo', 'repo'),
         output_log_level=module.borgmatic.logger.ANSWER,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -417,7 +417,7 @@ def test_display_repository_info_with_umask_calls_borg_with_umask_flags():
     flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('borg', 'repo-info', '--umask', '077', '--json', '--repo', 'repo'),
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -426,7 +426,7 @@ def test_display_repository_info_with_umask_calls_borg_with_umask_flags():
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'repo-info', '--umask', '077', '--repo', 'repo'),
         output_log_level=module.borgmatic.logger.ANSWER,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -462,7 +462,7 @@ def test_display_repository_info_with_log_json_calls_borg_with_log_json_flags():
     flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('borg', 'repo-info', '--log-json', '--json', '--repo', 'repo'),
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -471,7 +471,7 @@ def test_display_repository_info_with_log_json_calls_borg_with_log_json_flags():
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'repo-info', '--log-json', '--repo', 'repo'),
         output_log_level=module.borgmatic.logger.ANSWER,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -504,7 +504,7 @@ def test_display_repository_info_with_lock_wait_calls_borg_with_lock_wait_flags(
     flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('borg', 'repo-info', '--lock-wait', '5', '--json', '--repo', 'repo'),
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -513,7 +513,7 @@ def test_display_repository_info_with_lock_wait_calls_borg_with_lock_wait_flags(
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'repo-info', '--lock-wait', '5', '--repo', 'repo'),
         output_log_level=module.borgmatic.logger.ANSWER,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -547,7 +547,7 @@ def test_display_repository_info_calls_borg_with_working_directory():
     )
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('borg', 'repo-info', '--json', '--repo', 'repo'),
-        extra_environment=None,
+        environment=None,
         working_directory='/working/dir',
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -556,7 +556,7 @@ def test_display_repository_info_calls_borg_with_working_directory():
     flexmock(module).should_receive('execute_command').with_args(
         ('borg', 'repo-info', '--repo', 'repo'),
         output_log_level=module.borgmatic.logger.ANSWER,
-        extra_environment=None,
+        environment=None,
         working_directory='/working/dir',
         borg_local_path='borg',
         borg_exit_codes=None,

+ 13 - 13
tests/unit/borg/test_repo_list.py

@@ -39,7 +39,7 @@ def test_resolve_archive_name_calls_borg_with_flags():
         ('borg', 'list') + BORG_LIST_LATEST_ARGUMENTS,
         borg_local_path='borg',
         borg_exit_codes=None,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
     ).and_return(expected_archive + '\n')
 
@@ -61,7 +61,7 @@ def test_resolve_archive_name_with_log_info_calls_borg_without_info_flag():
     flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('borg', 'list') + BORG_LIST_LATEST_ARGUMENTS,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -86,7 +86,7 @@ def test_resolve_archive_name_with_log_debug_calls_borg_without_debug_flag():
     flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('borg', 'list') + BORG_LIST_LATEST_ARGUMENTS,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -111,7 +111,7 @@ def test_resolve_archive_name_with_local_path_calls_borg_via_local_path():
     flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('borg1', 'list') + BORG_LIST_LATEST_ARGUMENTS,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg1',
         borg_exit_codes=None,
@@ -137,7 +137,7 @@ def test_resolve_archive_name_with_exit_codes_calls_borg_using_them():
     borg_exit_codes = flexmock()
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('borg', 'list') + BORG_LIST_LATEST_ARGUMENTS,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=borg_exit_codes,
@@ -161,7 +161,7 @@ def test_resolve_archive_name_with_remote_path_calls_borg_with_remote_path_flags
     flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('borg', 'list', '--remote-path', 'borg1') + BORG_LIST_LATEST_ARGUMENTS,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -186,7 +186,7 @@ def test_resolve_archive_name_with_umask_calls_borg_with_umask_flags():
     flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('borg', 'list', '--umask', '077') + BORG_LIST_LATEST_ARGUMENTS,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -209,7 +209,7 @@ def test_resolve_archive_name_without_archives_raises():
     flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('borg', 'list') + BORG_LIST_LATEST_ARGUMENTS,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -232,7 +232,7 @@ def test_resolve_archive_name_with_log_json_calls_borg_with_log_json_flags():
     flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('borg', 'list', '--log-json') + BORG_LIST_LATEST_ARGUMENTS,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -257,7 +257,7 @@ def test_resolve_archive_name_with_lock_wait_calls_borg_with_lock_wait_flags():
     flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('borg', 'list', '--lock-wait', 'okay') + BORG_LIST_LATEST_ARGUMENTS,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -285,7 +285,7 @@ def test_resolve_archive_name_calls_borg_with_working_directory():
         ('borg', 'list') + BORG_LIST_LATEST_ARGUMENTS,
         borg_local_path='borg',
         borg_exit_codes=None,
-        extra_environment=None,
+        environment=None,
         working_directory='/working/dir',
     ).and_return(expected_archive + '\n')
 
@@ -773,7 +773,7 @@ def test_list_repository_calls_borg_with_working_directory():
     )
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         full_command=object,
-        extra_environment=object,
+        environment=object,
         working_directory='/working/dir',
         borg_local_path=object,
         borg_exit_codes=object,
@@ -782,7 +782,7 @@ def test_list_repository_calls_borg_with_working_directory():
     flexmock(module).should_receive('execute_command').with_args(
         full_command=object,
         output_log_level=object,
-        extra_environment=object,
+        environment=object,
         working_directory='/working/dir',
         borg_local_path=object,
         borg_exit_codes=object,

+ 18 - 18
tests/unit/borg/test_transfer.py

@@ -21,7 +21,7 @@ def test_transfer_archives_calls_borg_with_flags():
         ('borg', 'transfer', '--repo', 'repo'),
         output_log_level=module.borgmatic.logger.ANSWER,
         output_file=None,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -55,7 +55,7 @@ def test_transfer_archives_with_dry_run_calls_borg_with_dry_run_flag():
         ('borg', 'transfer', '--repo', 'repo', '--dry-run'),
         output_log_level=module.borgmatic.logger.ANSWER,
         output_file=None,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -86,7 +86,7 @@ def test_transfer_archives_with_log_info_calls_borg_with_info_flag():
         ('borg', 'transfer', '--info', '--repo', 'repo'),
         output_log_level=module.borgmatic.logger.ANSWER,
         output_file=None,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -117,7 +117,7 @@ def test_transfer_archives_with_log_debug_calls_borg_with_debug_flag():
         ('borg', 'transfer', '--debug', '--show-rc', '--repo', 'repo'),
         output_log_level=module.borgmatic.logger.ANSWER,
         output_file=None,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -151,7 +151,7 @@ def test_transfer_archives_with_archive_calls_borg_with_match_archives_flag():
         ('borg', 'transfer', '--match-archives', 'archive', '--repo', 'repo'),
         output_log_level=module.borgmatic.logger.ANSWER,
         output_file=None,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -184,7 +184,7 @@ def test_transfer_archives_with_match_archives_calls_borg_with_match_archives_fl
         ('borg', 'transfer', '--match-archives', 'sh:foo*', '--repo', 'repo'),
         output_log_level=module.borgmatic.logger.ANSWER,
         output_file=None,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -217,7 +217,7 @@ def test_transfer_archives_with_archive_name_format_calls_borg_with_match_archiv
         ('borg', 'transfer', '--match-archives', 'sh:bar-*', '--repo', 'repo'),
         output_log_level=module.borgmatic.logger.ANSWER,
         output_file=None,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -248,7 +248,7 @@ def test_transfer_archives_with_local_path_calls_borg_via_local_path():
         ('borg2', 'transfer', '--repo', 'repo'),
         output_log_level=module.borgmatic.logger.ANSWER,
         output_file=None,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg2',
         borg_exit_codes=None,
@@ -281,7 +281,7 @@ def test_transfer_archives_with_exit_codes_calls_borg_using_them():
         ('borg', 'transfer', '--repo', 'repo'),
         output_log_level=module.borgmatic.logger.ANSWER,
         output_file=None,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=borg_exit_codes,
@@ -315,7 +315,7 @@ def test_transfer_archives_with_remote_path_calls_borg_with_remote_path_flags():
         ('borg', 'transfer', '--remote-path', 'borg2', '--repo', 'repo'),
         output_log_level=module.borgmatic.logger.ANSWER,
         output_file=None,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -349,7 +349,7 @@ def test_transfer_archives_with_umask_calls_borg_with_umask_flags():
         ('borg', 'transfer', '--umask', '077', '--repo', 'repo'),
         output_log_level=module.borgmatic.logger.ANSWER,
         output_file=None,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -383,7 +383,7 @@ def test_transfer_archives_with_log_json_calls_borg_with_log_json_flags():
         ('borg', 'transfer', '--log-json', '--repo', 'repo'),
         output_log_level=module.borgmatic.logger.ANSWER,
         output_file=None,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -418,7 +418,7 @@ def test_transfer_archives_with_lock_wait_calls_borg_with_lock_wait_flags():
         ('borg', 'transfer', '--lock-wait', '5', '--repo', 'repo'),
         output_log_level=module.borgmatic.logger.ANSWER,
         output_file=None,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -449,7 +449,7 @@ def test_transfer_archives_with_progress_calls_borg_with_progress_flag():
         ('borg', 'transfer', '--progress', '--repo', 'repo'),
         output_log_level=module.borgmatic.logger.ANSWER,
         output_file=module.DO_NOT_CAPTURE,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -484,7 +484,7 @@ def test_transfer_archives_passes_through_arguments_to_borg(argument_name):
         ('borg', 'transfer', flag_name, 'value', '--repo', 'repo'),
         output_log_level=module.borgmatic.logger.ANSWER,
         output_file=None,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -521,7 +521,7 @@ def test_transfer_archives_with_source_repository_calls_borg_with_other_repo_fla
         ('borg', 'transfer', '--repo', 'repo', '--other-repo', 'other'),
         output_log_level=module.borgmatic.logger.ANSWER,
         output_file=None,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -566,7 +566,7 @@ def test_transfer_archives_with_date_based_matching_calls_borg_with_date_based_f
         ),
         output_log_level=module.borgmatic.logger.ANSWER,
         output_file=None,
-        extra_environment=None,
+        environment=None,
         working_directory=None,
         borg_local_path='borg',
         borg_exit_codes=None,
@@ -605,7 +605,7 @@ def test_transfer_archives_calls_borg_with_working_directory():
         ('borg', 'transfer', '--repo', 'repo'),
         output_log_level=module.borgmatic.logger.ANSWER,
         output_file=None,
-        extra_environment=None,
+        environment=None,
         working_directory='/working/dir',
         borg_local_path='borg',
         borg_exit_codes=None,

+ 1 - 1
tests/unit/borg/test_version.py

@@ -23,7 +23,7 @@ def insert_execute_command_and_capture_output_mock(
     )
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         command,
-        extra_environment=None,
+        environment=None,
         working_directory=working_directory,
         borg_local_path=borg_local_path,
         borg_exit_codes=borg_exit_codes,

+ 48 - 34
tests/unit/hooks/data_source/test_mariadb.py

@@ -7,33 +7,33 @@ from borgmatic.hooks.data_source import mariadb as module
 
 
 def test_database_names_to_dump_passes_through_name():
-    extra_environment = flexmock()
+    environment = flexmock()
 
-    names = module.database_names_to_dump({'name': 'foo'}, {}, extra_environment, dry_run=False)
+    names = module.database_names_to_dump({'name': 'foo'}, {}, environment, dry_run=False)
 
     assert names == ('foo',)
 
 
 def test_database_names_to_dump_bails_for_dry_run():
-    extra_environment = flexmock()
+    environment = flexmock()
     flexmock(module).should_receive('execute_command_and_capture_output').never()
 
-    names = module.database_names_to_dump({'name': 'all'}, {}, extra_environment, dry_run=True)
+    names = module.database_names_to_dump({'name': 'all'}, {}, environment, dry_run=True)
 
     assert names == ()
 
 
 def test_database_names_to_dump_queries_mariadb_for_database_names():
-    extra_environment = flexmock()
+    environment = flexmock()
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('mariadb', '--skip-column-names', '--batch', '--execute', 'show schemas'),
-        extra_environment=extra_environment,
+        environment=environment,
     ).and_return('foo\nbar\nmysql\n').once()
 
-    names = module.database_names_to_dump({'name': 'all'}, {}, extra_environment, dry_run=False)
+    names = module.database_names_to_dump({'name': 'all'}, {}, environment, dry_run=False)
 
     assert names == ('foo', 'bar')
 
@@ -53,6 +53,7 @@ def test_dump_data_sources_dumps_each_database():
     databases = [{'name': 'foo'}, {'name': 'bar'}]
     processes = [flexmock(), flexmock()]
     flexmock(module).should_receive('make_dump_path').and_return('')
+    flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
@@ -66,7 +67,7 @@ def test_dump_data_sources_dumps_each_database():
             config={},
             dump_path=object,
             database_names=(name,),
-            extra_environment=object,
+            environment={'USER': 'root'},
             dry_run=object,
             dry_run_label=object,
         ).and_return(process).once()
@@ -88,6 +89,7 @@ def test_dump_data_sources_dumps_with_password():
     database = {'name': 'foo', 'username': 'root', 'password': 'trustsome1'}
     process = flexmock()
     flexmock(module).should_receive('make_dump_path').and_return('')
+    flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
@@ -100,7 +102,7 @@ def test_dump_data_sources_dumps_with_password():
         config={},
         dump_path=object,
         database_names=('foo',),
-        extra_environment={'MYSQL_PWD': 'trustsome1'},
+        environment={'USER': 'root', 'MYSQL_PWD': 'trustsome1'},
         dry_run=object,
         dry_run_label=object,
     ).and_return(process).once()
@@ -119,6 +121,7 @@ def test_dump_data_sources_dumps_all_databases_at_once():
     databases = [{'name': 'all'}]
     process = flexmock()
     flexmock(module).should_receive('make_dump_path').and_return('')
+    flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
@@ -128,7 +131,7 @@ def test_dump_data_sources_dumps_all_databases_at_once():
         config={},
         dump_path=object,
         database_names=('foo', 'bar'),
-        extra_environment=object,
+        environment={'USER': 'root'},
         dry_run=object,
         dry_run_label=object,
     ).and_return(process).once()
@@ -147,6 +150,7 @@ def test_dump_data_sources_dumps_all_databases_separately_when_format_configured
     databases = [{'name': 'all', 'format': 'sql'}]
     processes = [flexmock(), flexmock()]
     flexmock(module).should_receive('make_dump_path').and_return('')
+    flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
@@ -158,7 +162,7 @@ def test_dump_data_sources_dumps_all_databases_separately_when_format_configured
             config={},
             dump_path=object,
             database_names=(name,),
-            extra_environment=object,
+            environment={'USER': 'root'},
             dry_run=object,
             dry_run_label=object,
         ).and_return(process).once()
@@ -187,7 +191,7 @@ def test_database_names_to_dump_runs_mariadb_with_list_options():
             '--execute',
             'show schemas',
         ),
-        extra_environment=None,
+        environment=None,
     ).and_return(('foo\nbar')).once()
 
     assert module.database_names_to_dump(database, {}, None, '') == ('foo', 'bar')
@@ -200,7 +204,7 @@ def test_database_names_to_dump_runs_non_default_mariadb_with_list_options():
         'mariadb_command': 'custom_mariadb',
     }
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
-        extra_environment=None,
+        environment=None,
         full_command=(
             'custom_mariadb',  # Custom MariaDB command
             '--defaults-extra-file=mariadb.cnf',
@@ -232,7 +236,7 @@ def test_execute_dump_command_runs_mariadb_dump():
             '--result-file',
             'dump',
         ),
-        extra_environment=None,
+        environment=None,
         run_to_completion=False,
     ).and_return(process).once()
 
@@ -242,7 +246,7 @@ def test_execute_dump_command_runs_mariadb_dump():
             config={},
             dump_path=flexmock(),
             database_names=('foo',),
-            extra_environment=None,
+            environment=None,
             dry_run=False,
             dry_run_label='',
         )
@@ -267,7 +271,7 @@ def test_execute_dump_command_runs_mariadb_dump_without_add_drop_database():
             '--result-file',
             'dump',
         ),
-        extra_environment=None,
+        environment=None,
         run_to_completion=False,
     ).and_return(process).once()
 
@@ -277,7 +281,7 @@ def test_execute_dump_command_runs_mariadb_dump_without_add_drop_database():
             config={},
             dump_path=flexmock(),
             database_names=('foo',),
-            extra_environment=None,
+            environment=None,
             dry_run=False,
             dry_run_label='',
         )
@@ -309,7 +313,7 @@ def test_execute_dump_command_runs_mariadb_dump_with_hostname_and_port():
             '--result-file',
             'dump',
         ),
-        extra_environment=None,
+        environment=None,
         run_to_completion=False,
     ).and_return(process).once()
 
@@ -319,7 +323,7 @@ def test_execute_dump_command_runs_mariadb_dump_with_hostname_and_port():
             config={},
             dump_path=flexmock(),
             database_names=('foo',),
-            extra_environment=None,
+            environment=None,
             dry_run=False,
             dry_run_label='',
         )
@@ -347,7 +351,7 @@ def test_execute_dump_command_runs_mariadb_dump_with_username_and_password():
             '--result-file',
             'dump',
         ),
-        extra_environment={'MYSQL_PWD': 'trustsome1'},
+        environment={'MYSQL_PWD': 'trustsome1'},
         run_to_completion=False,
     ).and_return(process).once()
 
@@ -357,7 +361,7 @@ def test_execute_dump_command_runs_mariadb_dump_with_username_and_password():
             config={},
             dump_path=flexmock(),
             database_names=('foo',),
-            extra_environment={'MYSQL_PWD': 'trustsome1'},
+            environment={'MYSQL_PWD': 'trustsome1'},
             dry_run=False,
             dry_run_label='',
         )
@@ -384,7 +388,7 @@ def test_execute_dump_command_runs_mariadb_dump_with_options():
             '--result-file',
             'dump',
         ),
-        extra_environment=None,
+        environment=None,
         run_to_completion=False,
     ).and_return(process).once()
 
@@ -394,7 +398,7 @@ def test_execute_dump_command_runs_mariadb_dump_with_options():
             config={},
             dump_path=flexmock(),
             database_names=('foo',),
-            extra_environment=None,
+            environment=None,
             dry_run=False,
             dry_run_label='',
         )
@@ -421,7 +425,7 @@ def test_execute_dump_command_runs_non_default_mariadb_dump_with_options():
             '--result-file',
             'dump',
         ),
-        extra_environment=None,
+        environment=None,
         run_to_completion=False,
     ).and_return(process).once()
 
@@ -435,7 +439,7 @@ def test_execute_dump_command_runs_non_default_mariadb_dump_with_options():
             config={},
             dump_path=flexmock(),
             database_names=('foo',),
-            extra_environment=None,
+            environment=None,
             dry_run=False,
             dry_run_label='',
         )
@@ -455,7 +459,7 @@ def test_execute_dump_command_with_duplicate_dump_skips_mariadb_dump():
             config={},
             dump_path=flexmock(),
             database_names=('foo',),
-            extra_environment=None,
+            environment=None,
             dry_run=True,
             dry_run_label='SO DRY',
         )
@@ -479,7 +483,7 @@ def test_execute_dump_command_with_dry_run_skips_mariadb_dump():
             config={},
             dump_path=flexmock(),
             database_names=('foo',),
-            extra_environment=None,
+            environment=None,
             dry_run=True,
             dry_run_label='SO DRY',
         )
@@ -490,6 +494,7 @@ def test_execute_dump_command_with_dry_run_skips_mariadb_dump():
 def test_dump_data_sources_errors_for_missing_all_databases():
     databases = [{'name': 'all'}]
     flexmock(module).should_receive('make_dump_path').and_return('')
+    flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
@@ -512,6 +517,7 @@ def test_dump_data_sources_errors_for_missing_all_databases():
 def test_dump_data_sources_does_not_error_for_missing_all_databases_with_dry_run():
     databases = [{'name': 'all'}]
     flexmock(module).should_receive('make_dump_path').and_return('')
+    flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
@@ -540,12 +546,13 @@ def test_restore_data_source_dump_runs_mariadb_to_restore():
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
+    flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         ('mariadb', '--batch'),
         processes=[extract_process],
         output_log_level=logging.DEBUG,
         input_file=extract_process.stdout,
-        extra_environment=None,
+        environment={'USER': 'root'},
     ).once()
 
     module.restore_data_source_dump(
@@ -571,12 +578,13 @@ def test_restore_data_source_dump_runs_mariadb_with_options():
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
+    flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         ('mariadb', '--batch', '--harder'),
         processes=[extract_process],
         output_log_level=logging.DEBUG,
         input_file=extract_process.stdout,
-        extra_environment=None,
+        environment={'USER': 'root'},
     ).once()
 
     module.restore_data_source_dump(
@@ -604,12 +612,13 @@ def test_restore_data_source_dump_runs_non_default_mariadb_with_options():
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
+    flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         ('custom_mariadb', '--batch', '--harder'),
         processes=[extract_process],
         output_log_level=logging.DEBUG,
         input_file=extract_process.stdout,
-        extra_environment=None,
+        environment={'USER': 'root'},
     ).once()
 
     module.restore_data_source_dump(
@@ -635,6 +644,7 @@ def test_restore_data_source_dump_runs_mariadb_with_hostname_and_port():
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
+    flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         (
             'mariadb',
@@ -649,7 +659,7 @@ def test_restore_data_source_dump_runs_mariadb_with_hostname_and_port():
         processes=[extract_process],
         output_log_level=logging.DEBUG,
         input_file=extract_process.stdout,
-        extra_environment=None,
+        environment={'USER': 'root'},
     ).once()
 
     module.restore_data_source_dump(
@@ -675,12 +685,13 @@ def test_restore_data_source_dump_runs_mariadb_with_username_and_password():
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
+    flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         ('mariadb', '--batch', '--user', 'root'),
         processes=[extract_process],
         output_log_level=logging.DEBUG,
         input_file=extract_process.stdout,
-        extra_environment={'MYSQL_PWD': 'trustsome1'},
+        environment={'USER': 'root', 'MYSQL_PWD': 'trustsome1'},
     ).once()
 
     module.restore_data_source_dump(
@@ -716,6 +727,7 @@ def test_restore_data_source_dump_with_connection_params_uses_connection_params_
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
+    flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         (
             'mariadb',
@@ -732,7 +744,7 @@ def test_restore_data_source_dump_with_connection_params_uses_connection_params_
         processes=[extract_process],
         output_log_level=logging.DEBUG,
         input_file=extract_process.stdout,
-        extra_environment={'MYSQL_PWD': 'clipassword'},
+        environment={'USER': 'root', 'MYSQL_PWD': 'clipassword'},
     ).once()
 
     module.restore_data_source_dump(
@@ -770,6 +782,7 @@ def test_restore_data_source_dump_without_connection_params_uses_restore_params_
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
+    flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         (
             'mariadb',
@@ -786,7 +799,7 @@ def test_restore_data_source_dump_without_connection_params_uses_restore_params_
         processes=[extract_process],
         output_log_level=logging.DEBUG,
         input_file=extract_process.stdout,
-        extra_environment={'MYSQL_PWD': 'restorepass'},
+        environment={'USER': 'root', 'MYSQL_PWD': 'restorepass'},
     ).once()
 
     module.restore_data_source_dump(
@@ -811,6 +824,7 @@ def test_restore_data_source_dump_with_dry_run_skips_restore():
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
+    flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module).should_receive('execute_command_with_processes').never()
 
     module.restore_data_source_dump(

+ 48 - 34
tests/unit/hooks/data_source/test_mysql.py

@@ -7,36 +7,36 @@ from borgmatic.hooks.data_source import mysql as module
 
 
 def test_database_names_to_dump_passes_through_name():
-    extra_environment = flexmock()
+    environment = flexmock()
 
-    names = module.database_names_to_dump({'name': 'foo'}, {}, extra_environment, dry_run=False)
+    names = module.database_names_to_dump({'name': 'foo'}, {}, environment, dry_run=False)
 
     assert names == ('foo',)
 
 
 def test_database_names_to_dump_bails_for_dry_run():
-    extra_environment = flexmock()
+    environment = flexmock()
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_and_capture_output').never()
 
-    names = module.database_names_to_dump({'name': 'all'}, {}, extra_environment, dry_run=True)
+    names = module.database_names_to_dump({'name': 'all'}, {}, environment, dry_run=True)
 
     assert names == ()
 
 
 def test_database_names_to_dump_queries_mysql_for_database_names():
-    extra_environment = flexmock()
+    environment = flexmock()
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('mysql', '--skip-column-names', '--batch', '--execute', 'show schemas'),
-        extra_environment=extra_environment,
+        environment=environment,
     ).and_return('foo\nbar\nmysql\n').once()
 
-    names = module.database_names_to_dump({'name': 'all'}, {}, extra_environment, dry_run=False)
+    names = module.database_names_to_dump({'name': 'all'}, {}, environment, dry_run=False)
 
     assert names == ('foo', 'bar')
 
@@ -56,6 +56,7 @@ def test_dump_data_sources_dumps_each_database():
     databases = [{'name': 'foo'}, {'name': 'bar'}]
     processes = [flexmock(), flexmock()]
     flexmock(module).should_receive('make_dump_path').and_return('')
+    flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module).should_receive('database_names_to_dump').and_return(('foo',)).and_return(
         ('bar',)
     )
@@ -66,7 +67,7 @@ def test_dump_data_sources_dumps_each_database():
             config={},
             dump_path=object,
             database_names=(name,),
-            extra_environment=object,
+            environment={'USER': 'root'},
             dry_run=object,
             dry_run_label=object,
         ).and_return(process).once()
@@ -88,6 +89,7 @@ def test_dump_data_sources_dumps_with_password():
     database = {'name': 'foo', 'username': 'root', 'password': 'trustsome1'}
     process = flexmock()
     flexmock(module).should_receive('make_dump_path').and_return('')
+    flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
@@ -100,7 +102,7 @@ def test_dump_data_sources_dumps_with_password():
         config={},
         dump_path=object,
         database_names=('foo',),
-        extra_environment={'MYSQL_PWD': 'trustsome1'},
+        environment={'USER': 'root', 'MYSQL_PWD': 'trustsome1'},
         dry_run=object,
         dry_run_label=object,
     ).and_return(process).once()
@@ -119,13 +121,14 @@ def test_dump_data_sources_dumps_all_databases_at_once():
     databases = [{'name': 'all'}]
     process = flexmock()
     flexmock(module).should_receive('make_dump_path').and_return('')
+    flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module).should_receive('database_names_to_dump').and_return(('foo', 'bar'))
     flexmock(module).should_receive('execute_dump_command').with_args(
         database={'name': 'all'},
         config={},
         dump_path=object,
         database_names=('foo', 'bar'),
-        extra_environment=object,
+        environment={'USER': 'root'},
         dry_run=object,
         dry_run_label=object,
     ).and_return(process).once()
@@ -144,6 +147,7 @@ def test_dump_data_sources_dumps_all_databases_separately_when_format_configured
     databases = [{'name': 'all', 'format': 'sql'}]
     processes = [flexmock(), flexmock()]
     flexmock(module).should_receive('make_dump_path').and_return('')
+    flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module).should_receive('database_names_to_dump').and_return(('foo', 'bar'))
 
     for name, process in zip(('foo', 'bar'), processes):
@@ -152,7 +156,7 @@ def test_dump_data_sources_dumps_all_databases_separately_when_format_configured
             config={},
             dump_path=object,
             database_names=(name,),
-            extra_environment=object,
+            environment={'USER': 'root'},
             dry_run=object,
             dry_run_label=object,
         ).and_return(process).once()
@@ -181,7 +185,7 @@ def test_database_names_to_dump_runs_mysql_with_list_options():
             '--execute',
             'show schemas',
         ),
-        extra_environment=None,
+        environment=None,
     ).and_return(('foo\nbar')).once()
 
     assert module.database_names_to_dump(database, {}, None, '') == ('foo', 'bar')
@@ -194,7 +198,7 @@ def test_database_names_to_dump_runs_non_default_mysql_with_list_options():
         'mysql_command': 'custom_mysql',
     }
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
-        extra_environment=None,
+        environment=None,
         full_command=(
             'custom_mysql',  # Custom MySQL command
             '--defaults-extra-file=my.cnf',
@@ -226,7 +230,7 @@ def test_execute_dump_command_runs_mysqldump():
             '--result-file',
             'dump',
         ),
-        extra_environment=None,
+        environment=None,
         run_to_completion=False,
     ).and_return(process).once()
 
@@ -236,7 +240,7 @@ def test_execute_dump_command_runs_mysqldump():
             config={},
             dump_path=flexmock(),
             database_names=('foo',),
-            extra_environment=None,
+            environment=None,
             dry_run=False,
             dry_run_label='',
         )
@@ -261,7 +265,7 @@ def test_execute_dump_command_runs_mysqldump_without_add_drop_database():
             '--result-file',
             'dump',
         ),
-        extra_environment=None,
+        environment=None,
         run_to_completion=False,
     ).and_return(process).once()
 
@@ -271,7 +275,7 @@ def test_execute_dump_command_runs_mysqldump_without_add_drop_database():
             config={},
             dump_path=flexmock(),
             database_names=('foo',),
-            extra_environment=None,
+            environment=None,
             dry_run=False,
             dry_run_label='',
         )
@@ -303,7 +307,7 @@ def test_execute_dump_command_runs_mysqldump_with_hostname_and_port():
             '--result-file',
             'dump',
         ),
-        extra_environment=None,
+        environment=None,
         run_to_completion=False,
     ).and_return(process).once()
 
@@ -313,7 +317,7 @@ def test_execute_dump_command_runs_mysqldump_with_hostname_and_port():
             config={},
             dump_path=flexmock(),
             database_names=('foo',),
-            extra_environment=None,
+            environment=None,
             dry_run=False,
             dry_run_label='',
         )
@@ -341,7 +345,7 @@ def test_execute_dump_command_runs_mysqldump_with_username_and_password():
             '--result-file',
             'dump',
         ),
-        extra_environment={'MYSQL_PWD': 'trustsome1'},
+        environment={'MYSQL_PWD': 'trustsome1'},
         run_to_completion=False,
     ).and_return(process).once()
 
@@ -351,7 +355,7 @@ def test_execute_dump_command_runs_mysqldump_with_username_and_password():
             config={},
             dump_path=flexmock(),
             database_names=('foo',),
-            extra_environment={'MYSQL_PWD': 'trustsome1'},
+            environment={'MYSQL_PWD': 'trustsome1'},
             dry_run=False,
             dry_run_label='',
         )
@@ -378,7 +382,7 @@ def test_execute_dump_command_runs_mysqldump_with_options():
             '--result-file',
             'dump',
         ),
-        extra_environment=None,
+        environment=None,
         run_to_completion=False,
     ).and_return(process).once()
 
@@ -388,7 +392,7 @@ def test_execute_dump_command_runs_mysqldump_with_options():
             config={},
             dump_path=flexmock(),
             database_names=('foo',),
-            extra_environment=None,
+            environment=None,
             dry_run=False,
             dry_run_label='',
         )
@@ -414,7 +418,7 @@ def test_execute_dump_command_runs_non_default_mysqldump():
             '--result-file',
             'dump',
         ),
-        extra_environment=None,
+        environment=None,
         run_to_completion=False,
     ).and_return(process).once()
 
@@ -427,7 +431,7 @@ def test_execute_dump_command_runs_non_default_mysqldump():
             config={},
             dump_path=flexmock(),
             database_names=('foo',),
-            extra_environment=None,
+            environment=None,
             dry_run=False,
             dry_run_label='',
         )
@@ -447,7 +451,7 @@ def test_execute_dump_command_with_duplicate_dump_skips_mysqldump():
             config={},
             dump_path=flexmock(),
             database_names=('foo',),
-            extra_environment=None,
+            environment=None,
             dry_run=True,
             dry_run_label='SO DRY',
         )
@@ -471,7 +475,7 @@ def test_execute_dump_command_with_dry_run_skips_mysqldump():
             config={},
             dump_path=flexmock(),
             database_names=('foo',),
-            extra_environment=None,
+            environment=None,
             dry_run=True,
             dry_run_label='SO DRY',
         )
@@ -482,6 +486,7 @@ def test_execute_dump_command_with_dry_run_skips_mysqldump():
 def test_dump_data_sources_errors_for_missing_all_databases():
     databases = [{'name': 'all'}]
     flexmock(module).should_receive('make_dump_path').and_return('')
+    flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
@@ -504,6 +509,7 @@ def test_dump_data_sources_errors_for_missing_all_databases():
 def test_dump_data_sources_does_not_error_for_missing_all_databases_with_dry_run():
     databases = [{'name': 'all'}]
     flexmock(module).should_receive('make_dump_path').and_return('')
+    flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
@@ -532,12 +538,13 @@ def test_restore_data_source_dump_runs_mysql_to_restore():
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
+    flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         ('mysql', '--batch'),
         processes=[extract_process],
         output_log_level=logging.DEBUG,
         input_file=extract_process.stdout,
-        extra_environment=None,
+        environment={'USER': 'root'},
     ).once()
 
     module.restore_data_source_dump(
@@ -563,12 +570,13 @@ def test_restore_data_source_dump_runs_mysql_with_options():
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
+    flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         ('mysql', '--batch', '--harder'),
         processes=[extract_process],
         output_log_level=logging.DEBUG,
         input_file=extract_process.stdout,
-        extra_environment=None,
+        environment={'USER': 'root'},
     ).once()
 
     module.restore_data_source_dump(
@@ -594,12 +602,13 @@ def test_restore_data_source_dump_runs_non_default_mysql_with_options():
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
+    flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         ('custom_mysql', '--batch', '--harder'),
         processes=[extract_process],
         output_log_level=logging.DEBUG,
         input_file=extract_process.stdout,
-        extra_environment=None,
+        environment={'USER': 'root'},
     ).once()
 
     module.restore_data_source_dump(
@@ -625,6 +634,7 @@ def test_restore_data_source_dump_runs_mysql_with_hostname_and_port():
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
+    flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         (
             'mysql',
@@ -639,7 +649,7 @@ def test_restore_data_source_dump_runs_mysql_with_hostname_and_port():
         processes=[extract_process],
         output_log_level=logging.DEBUG,
         input_file=extract_process.stdout,
-        extra_environment=None,
+        environment={'USER': 'root'},
     ).once()
 
     module.restore_data_source_dump(
@@ -665,12 +675,13 @@ def test_restore_data_source_dump_runs_mysql_with_username_and_password():
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
+    flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         ('mysql', '--batch', '--user', 'root'),
         processes=[extract_process],
         output_log_level=logging.DEBUG,
         input_file=extract_process.stdout,
-        extra_environment={'MYSQL_PWD': 'trustsome1'},
+        environment={'USER': 'root', 'MYSQL_PWD': 'trustsome1'},
     ).once()
 
     module.restore_data_source_dump(
@@ -706,6 +717,7 @@ def test_restore_data_source_dump_with_connection_params_uses_connection_params_
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
+    flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         (
             'mysql',
@@ -722,7 +734,7 @@ def test_restore_data_source_dump_with_connection_params_uses_connection_params_
         processes=[extract_process],
         output_log_level=logging.DEBUG,
         input_file=extract_process.stdout,
-        extra_environment={'MYSQL_PWD': 'clipassword'},
+        environment={'USER': 'root', 'MYSQL_PWD': 'clipassword'},
     ).once()
 
     module.restore_data_source_dump(
@@ -760,6 +772,7 @@ def test_restore_data_source_dump_without_connection_params_uses_restore_params_
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
+    flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module).should_receive('execute_command_with_processes').with_args(
         (
             'mysql',
@@ -776,7 +789,7 @@ def test_restore_data_source_dump_without_connection_params_uses_restore_params_
         processes=[extract_process],
         output_log_level=logging.DEBUG,
         input_file=extract_process.stdout,
-        extra_environment={'MYSQL_PWD': 'restorepass'},
+        environment={'USER': 'root', 'MYSQL_PWD': 'restorepass'},
     ).once()
 
     module.restore_data_source_dump(
@@ -801,6 +814,7 @@ def test_restore_data_source_dump_with_dry_run_skips_restore():
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
+    flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module).should_receive('execute_command_with_processes').never()
 
     module.restore_data_source_dump(

+ 72 - 71
tests/unit/hooks/data_source/test_postgresql.py

@@ -6,7 +6,7 @@ from flexmock import flexmock
 from borgmatic.hooks.data_source import postgresql as module
 
 
-def test_make_extra_environment_maps_options_to_environment():
+def test_make_environment_maps_options_to_environment():
     database = {
         'name': 'foo',
         'password': 'pass',
@@ -17,6 +17,7 @@ def test_make_extra_environment_maps_options_to_environment():
         'ssl_crl': 'crl.crl',
     }
     expected = {
+        'USER': 'root',
         'PGPASSWORD': 'pass',
         'PGSSLMODE': 'require',
         'PGSSLCERT': 'cert.crt',
@@ -24,44 +25,44 @@ def test_make_extra_environment_maps_options_to_environment():
         'PGSSLROOTCERT': 'root.crt',
         'PGSSLCRL': 'crl.crl',
     }
+    flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
 
-    extra_env = module.make_extra_environment(database, {})
+    assert module.make_environment(database, {}) == expected
 
-    assert extra_env == expected
 
-
-def test_make_extra_environment_with_cli_password_sets_correct_password():
+def test_make_environment_with_cli_password_sets_correct_password():
     database = {'name': 'foo', 'restore_password': 'trustsome1', 'password': 'anotherpassword'}
+    flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
 
-    extra = module.make_extra_environment(
+    environment = module.make_environment(
         database, {}, restore_connection_params={'password': 'clipassword'}
     )
 
-    assert extra['PGPASSWORD'] == 'clipassword'
+    assert environment['PGPASSWORD'] == 'clipassword'
 
 
-def test_make_extra_environment_without_cli_password_or_configured_password_does_not_set_password():
+def test_make_environment_without_cli_password_or_configured_password_does_not_set_password():
     database = {'name': 'foo'}
 
-    extra = module.make_extra_environment(
+    environment = module.make_environment(
         database, {}, restore_connection_params={'username': 'someone'}
     )
 
-    assert 'PGPASSWORD' not in extra
+    assert 'PGPASSWORD' not in environment
 
 
-def test_make_extra_environment_without_ssl_mode_does_not_set_ssl_mode():
+def test_make_environment_without_ssl_mode_does_not_set_ssl_mode():
     database = {'name': 'foo'}
 
-    extra = module.make_extra_environment(database, {})
+    environment = module.make_environment(database, {})
 
-    assert 'PGSSLMODE' not in extra
+    assert 'PGSSLMODE' not in environment
 
 
 def test_database_names_to_dump_passes_through_individual_database_name():
@@ -125,7 +126,7 @@ def test_database_names_to_dump_with_all_and_format_lists_databases_with_hostnam
             '--port',
             '1234',
         ),
-        extra_environment=object,
+        environment=object,
     ).and_return('foo,test,\nbar,test,"stuff and such"')
 
     assert module.database_names_to_dump(database, {}, flexmock(), dry_run=False) == (
@@ -150,7 +151,7 @@ def test_database_names_to_dump_with_all_and_format_lists_databases_with_usernam
             '--username',
             'postgres',
         ),
-        extra_environment=object,
+        environment=object,
     ).and_return('foo,test,\nbar,test,"stuff and such"')
 
     assert module.database_names_to_dump(database, {}, flexmock(), dry_run=False) == (
@@ -166,7 +167,7 @@ def test_database_names_to_dump_with_all_and_format_lists_databases_with_options
     ).replace_with(lambda value, config: value)
     flexmock(module).should_receive('execute_command_and_capture_output').with_args(
         ('psql', '--list', '--no-password', '--no-psqlrc', '--csv', '--tuples-only', '--harder'),
-        extra_environment=object,
+        environment=object,
     ).and_return('foo,test,\nbar,test,"stuff and such"')
 
     assert module.database_names_to_dump(database, {}, flexmock(), dry_run=False) == (
@@ -210,7 +211,7 @@ def test_database_names_to_dump_with_all_and_psql_command_uses_custom_command():
             '--csv',
             '--tuples-only',
         ),
-        extra_environment=object,
+        environment=object,
     ).and_return('foo,text').once()
 
     assert module.database_names_to_dump(database, {}, flexmock(), dry_run=False) == ('foo',)
@@ -237,7 +238,7 @@ def test_use_streaming_false_for_no_databases():
 def test_dump_data_sources_runs_pg_dump_for_each_database():
     databases = [{'name': 'foo'}, {'name': 'bar'}]
     processes = [flexmock(), flexmock()]
-    flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
+    flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('database_names_to_dump').and_return(('foo',)).and_return(
         ('bar',)
@@ -265,7 +266,7 @@ def test_dump_data_sources_runs_pg_dump_for_each_database():
                 f'databases/localhost/{name}',
             ),
             shell=True,
-            extra_environment={'PGSSLMODE': 'disable'},
+            environment={'PGSSLMODE': 'disable'},
             run_to_completion=False,
         ).and_return(process).once()
 
@@ -284,7 +285,7 @@ def test_dump_data_sources_runs_pg_dump_for_each_database():
 
 def test_dump_data_sources_raises_when_no_database_names_to_dump():
     databases = [{'name': 'foo'}, {'name': 'bar'}]
-    flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
+    flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('database_names_to_dump').and_return(())
 
@@ -301,7 +302,7 @@ def test_dump_data_sources_raises_when_no_database_names_to_dump():
 
 def test_dump_data_sources_does_not_raise_when_no_database_names_to_dump():
     databases = [{'name': 'foo'}, {'name': 'bar'}]
-    flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
+    flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('database_names_to_dump').and_return(())
 
@@ -317,7 +318,7 @@ def test_dump_data_sources_does_not_raise_when_no_database_names_to_dump():
 
 def test_dump_data_sources_with_duplicate_dump_skips_pg_dump():
     databases = [{'name': 'foo'}, {'name': 'bar'}]
-    flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
+    flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('database_names_to_dump').and_return(('foo',)).and_return(
         ('bar',)
@@ -344,7 +345,7 @@ def test_dump_data_sources_with_duplicate_dump_skips_pg_dump():
 
 def test_dump_data_sources_with_dry_run_skips_pg_dump():
     databases = [{'name': 'foo'}, {'name': 'bar'}]
-    flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
+    flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('database_names_to_dump').and_return(('foo',)).and_return(
         ('bar',)
@@ -375,7 +376,7 @@ def test_dump_data_sources_with_dry_run_skips_pg_dump():
 def test_dump_data_sources_runs_pg_dump_with_hostname_and_port():
     databases = [{'name': 'foo', 'hostname': 'database.example.org', 'port': 5433}]
     process = flexmock()
-    flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
+    flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('database_names_to_dump').and_return(('foo',))
     flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return(
@@ -404,7 +405,7 @@ def test_dump_data_sources_runs_pg_dump_with_hostname_and_port():
             'databases/database.example.org/foo',
         ),
         shell=True,
-        extra_environment={'PGSSLMODE': 'disable'},
+        environment={'PGSSLMODE': 'disable'},
         run_to_completion=False,
     ).and_return(process).once()
 
@@ -421,7 +422,7 @@ def test_dump_data_sources_runs_pg_dump_with_hostname_and_port():
 def test_dump_data_sources_runs_pg_dump_with_username_and_password():
     databases = [{'name': 'foo', 'username': 'postgres', 'password': 'trustsome1'}]
     process = flexmock()
-    flexmock(module).should_receive('make_extra_environment').and_return(
+    flexmock(module).should_receive('make_environment').and_return(
         {'PGPASSWORD': 'trustsome1', 'PGSSLMODE': 'disable'}
     )
     flexmock(module).should_receive('make_dump_path').and_return('')
@@ -450,7 +451,7 @@ def test_dump_data_sources_runs_pg_dump_with_username_and_password():
             'databases/localhost/foo',
         ),
         shell=True,
-        extra_environment={'PGPASSWORD': 'trustsome1', 'PGSSLMODE': 'disable'},
+        environment={'PGPASSWORD': 'trustsome1', 'PGSSLMODE': 'disable'},
         run_to_completion=False,
     ).and_return(process).once()
 
@@ -467,7 +468,7 @@ def test_dump_data_sources_runs_pg_dump_with_username_and_password():
 def test_dump_data_sources_with_username_injection_attack_gets_escaped():
     databases = [{'name': 'foo', 'username': 'postgres; naughty-command', 'password': 'trustsome1'}]
     process = flexmock()
-    flexmock(module).should_receive('make_extra_environment').and_return(
+    flexmock(module).should_receive('make_environment').and_return(
         {'PGPASSWORD': 'trustsome1', 'PGSSLMODE': 'disable'}
     )
     flexmock(module).should_receive('make_dump_path').and_return('')
@@ -496,7 +497,7 @@ def test_dump_data_sources_with_username_injection_attack_gets_escaped():
             'databases/localhost/foo',
         ),
         shell=True,
-        extra_environment={'PGPASSWORD': 'trustsome1', 'PGSSLMODE': 'disable'},
+        environment={'PGPASSWORD': 'trustsome1', 'PGSSLMODE': 'disable'},
         run_to_completion=False,
     ).and_return(process).once()
 
@@ -512,7 +513,7 @@ def test_dump_data_sources_with_username_injection_attack_gets_escaped():
 
 def test_dump_data_sources_runs_pg_dump_with_directory_format():
     databases = [{'name': 'foo', 'format': 'directory'}]
-    flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
+    flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('database_names_to_dump').and_return(('foo',))
     flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return(
@@ -538,7 +539,7 @@ def test_dump_data_sources_runs_pg_dump_with_directory_format():
             'foo',
         ),
         shell=True,
-        extra_environment={'PGSSLMODE': 'disable'},
+        environment={'PGSSLMODE': 'disable'},
     ).and_return(flexmock()).once()
 
     assert (
@@ -557,7 +558,7 @@ def test_dump_data_sources_runs_pg_dump_with_directory_format():
 def test_dump_data_sources_runs_pg_dump_with_options():
     databases = [{'name': 'foo', 'options': '--stuff=such'}]
     process = flexmock()
-    flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
+    flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('database_names_to_dump').and_return(('foo',))
     flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return(
@@ -583,7 +584,7 @@ def test_dump_data_sources_runs_pg_dump_with_options():
             'databases/localhost/foo',
         ),
         shell=True,
-        extra_environment={'PGSSLMODE': 'disable'},
+        environment={'PGSSLMODE': 'disable'},
         run_to_completion=False,
     ).and_return(process).once()
 
@@ -600,7 +601,7 @@ def test_dump_data_sources_runs_pg_dump_with_options():
 def test_dump_data_sources_runs_pg_dumpall_for_all_databases():
     databases = [{'name': 'all'}]
     process = flexmock()
-    flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
+    flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('database_names_to_dump').and_return(('all',))
     flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return(
@@ -615,7 +616,7 @@ def test_dump_data_sources_runs_pg_dumpall_for_all_databases():
     flexmock(module).should_receive('execute_command').with_args(
         ('pg_dumpall', '--no-password', '--clean', '--if-exists', '>', 'databases/localhost/all'),
         shell=True,
-        extra_environment={'PGSSLMODE': 'disable'},
+        environment={'PGSSLMODE': 'disable'},
         run_to_completion=False,
     ).and_return(process).once()
 
@@ -632,7 +633,7 @@ def test_dump_data_sources_runs_pg_dumpall_for_all_databases():
 def test_dump_data_sources_runs_non_default_pg_dump():
     databases = [{'name': 'foo', 'pg_dump_command': 'special_pg_dump --compress *'}]
     process = flexmock()
-    flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
+    flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_dump_path').and_return('')
     flexmock(module).should_receive('database_names_to_dump').and_return(('foo',))
     flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return(
@@ -659,7 +660,7 @@ def test_dump_data_sources_runs_non_default_pg_dump():
             'databases/localhost/foo',
         ),
         shell=True,
-        extra_environment={'PGSSLMODE': 'disable'},
+        environment={'PGSSLMODE': 'disable'},
         run_to_completion=False,
     ).and_return(process).once()
 
@@ -680,7 +681,7 @@ def test_restore_data_source_dump_runs_pg_restore():
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
-    flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
+    flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_dump_path')
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
     flexmock(module).should_receive('execute_command_with_processes').with_args(
@@ -696,7 +697,7 @@ def test_restore_data_source_dump_runs_pg_restore():
         processes=[extract_process],
         output_log_level=logging.DEBUG,
         input_file=extract_process.stdout,
-        extra_environment={'PGSSLMODE': 'disable'},
+        environment={'PGSSLMODE': 'disable'},
     ).once()
     flexmock(module).should_receive('execute_command').with_args(
         (
@@ -709,7 +710,7 @@ def test_restore_data_source_dump_runs_pg_restore():
             '--command',
             'ANALYZE',
         ),
-        extra_environment={'PGSSLMODE': 'disable'},
+        environment={'PGSSLMODE': 'disable'},
     ).once()
 
     module.restore_data_source_dump(
@@ -737,7 +738,7 @@ def test_restore_data_source_dump_runs_pg_restore_with_hostname_and_port():
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
-    flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
+    flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_dump_path')
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
     flexmock(module).should_receive('execute_command_with_processes').with_args(
@@ -757,7 +758,7 @@ def test_restore_data_source_dump_runs_pg_restore_with_hostname_and_port():
         processes=[extract_process],
         output_log_level=logging.DEBUG,
         input_file=extract_process.stdout,
-        extra_environment={'PGSSLMODE': 'disable'},
+        environment={'PGSSLMODE': 'disable'},
     ).once()
     flexmock(module).should_receive('execute_command').with_args(
         (
@@ -774,7 +775,7 @@ def test_restore_data_source_dump_runs_pg_restore_with_hostname_and_port():
             '--command',
             'ANALYZE',
         ),
-        extra_environment={'PGSSLMODE': 'disable'},
+        environment={'PGSSLMODE': 'disable'},
     ).once()
 
     module.restore_data_source_dump(
@@ -802,7 +803,7 @@ def test_restore_data_source_dump_runs_pg_restore_with_username_and_password():
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
-    flexmock(module).should_receive('make_extra_environment').and_return(
+    flexmock(module).should_receive('make_environment').and_return(
         {'PGPASSWORD': 'trustsome1', 'PGSSLMODE': 'disable'}
     )
     flexmock(module).should_receive('make_dump_path')
@@ -822,7 +823,7 @@ def test_restore_data_source_dump_runs_pg_restore_with_username_and_password():
         processes=[extract_process],
         output_log_level=logging.DEBUG,
         input_file=extract_process.stdout,
-        extra_environment={'PGPASSWORD': 'trustsome1', 'PGSSLMODE': 'disable'},
+        environment={'PGPASSWORD': 'trustsome1', 'PGSSLMODE': 'disable'},
     ).once()
     flexmock(module).should_receive('execute_command').with_args(
         (
@@ -837,7 +838,7 @@ def test_restore_data_source_dump_runs_pg_restore_with_username_and_password():
             '--command',
             'ANALYZE',
         ),
-        extra_environment={'PGPASSWORD': 'trustsome1', 'PGSSLMODE': 'disable'},
+        environment={'PGPASSWORD': 'trustsome1', 'PGSSLMODE': 'disable'},
     ).once()
 
     module.restore_data_source_dump(
@@ -876,7 +877,7 @@ def test_restore_data_source_dump_with_connection_params_uses_connection_params_
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
-    flexmock(module).should_receive('make_extra_environment').and_return(
+    flexmock(module).should_receive('make_environment').and_return(
         {'PGPASSWORD': 'clipassword', 'PGSSLMODE': 'disable'}
     )
     flexmock(module).should_receive('make_dump_path')
@@ -900,7 +901,7 @@ def test_restore_data_source_dump_with_connection_params_uses_connection_params_
         processes=[extract_process],
         output_log_level=logging.DEBUG,
         input_file=extract_process.stdout,
-        extra_environment={'PGPASSWORD': 'clipassword', 'PGSSLMODE': 'disable'},
+        environment={'PGPASSWORD': 'clipassword', 'PGSSLMODE': 'disable'},
     ).once()
     flexmock(module).should_receive('execute_command').with_args(
         (
@@ -919,7 +920,7 @@ def test_restore_data_source_dump_with_connection_params_uses_connection_params_
             '--command',
             'ANALYZE',
         ),
-        extra_environment={'PGPASSWORD': 'clipassword', 'PGSSLMODE': 'disable'},
+        environment={'PGPASSWORD': 'clipassword', 'PGSSLMODE': 'disable'},
     ).once()
 
     module.restore_data_source_dump(
@@ -958,7 +959,7 @@ def test_restore_data_source_dump_without_connection_params_uses_restore_params_
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
-    flexmock(module).should_receive('make_extra_environment').and_return(
+    flexmock(module).should_receive('make_environment').and_return(
         {'PGPASSWORD': 'restorepassword', 'PGSSLMODE': 'disable'}
     )
     flexmock(module).should_receive('make_dump_path')
@@ -982,7 +983,7 @@ def test_restore_data_source_dump_without_connection_params_uses_restore_params_
         processes=[extract_process],
         output_log_level=logging.DEBUG,
         input_file=extract_process.stdout,
-        extra_environment={'PGPASSWORD': 'restorepassword', 'PGSSLMODE': 'disable'},
+        environment={'PGPASSWORD': 'restorepassword', 'PGSSLMODE': 'disable'},
     ).once()
     flexmock(module).should_receive('execute_command').with_args(
         (
@@ -1001,7 +1002,7 @@ def test_restore_data_source_dump_without_connection_params_uses_restore_params_
             '--command',
             'ANALYZE',
         ),
-        extra_environment={'PGPASSWORD': 'restorepassword', 'PGSSLMODE': 'disable'},
+        environment={'PGPASSWORD': 'restorepassword', 'PGSSLMODE': 'disable'},
     ).once()
 
     module.restore_data_source_dump(
@@ -1034,7 +1035,7 @@ def test_restore_data_source_dump_runs_pg_restore_with_options():
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
-    flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
+    flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_dump_path')
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
     flexmock(module).should_receive('execute_command_with_processes').with_args(
@@ -1051,7 +1052,7 @@ def test_restore_data_source_dump_runs_pg_restore_with_options():
         processes=[extract_process],
         output_log_level=logging.DEBUG,
         input_file=extract_process.stdout,
-        extra_environment={'PGSSLMODE': 'disable'},
+        environment={'PGSSLMODE': 'disable'},
     ).once()
     flexmock(module).should_receive('execute_command').with_args(
         (
@@ -1065,7 +1066,7 @@ def test_restore_data_source_dump_runs_pg_restore_with_options():
             '--command',
             'ANALYZE',
         ),
-        extra_environment={'PGSSLMODE': 'disable'},
+        environment={'PGSSLMODE': 'disable'},
     ).once()
 
     module.restore_data_source_dump(
@@ -1091,7 +1092,7 @@ def test_restore_data_source_dump_runs_psql_for_all_database_dump():
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
-    flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
+    flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_dump_path')
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
     flexmock(module).should_receive('execute_command_with_processes').with_args(
@@ -1103,11 +1104,11 @@ def test_restore_data_source_dump_runs_psql_for_all_database_dump():
         processes=[extract_process],
         output_log_level=logging.DEBUG,
         input_file=extract_process.stdout,
-        extra_environment={'PGSSLMODE': 'disable'},
+        environment={'PGSSLMODE': 'disable'},
     ).once()
     flexmock(module).should_receive('execute_command').with_args(
         ('psql', '--no-password', '--no-psqlrc', '--quiet', '--command', 'ANALYZE'),
-        extra_environment={'PGSSLMODE': 'disable'},
+        environment={'PGSSLMODE': 'disable'},
     ).once()
 
     module.restore_data_source_dump(
@@ -1133,7 +1134,7 @@ def test_restore_data_source_dump_runs_psql_for_plain_database_dump():
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
-    flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
+    flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_dump_path')
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
     flexmock(module).should_receive('execute_command_with_processes').with_args(
@@ -1141,7 +1142,7 @@ def test_restore_data_source_dump_runs_psql_for_plain_database_dump():
         processes=[extract_process],
         output_log_level=logging.DEBUG,
         input_file=extract_process.stdout,
-        extra_environment={'PGSSLMODE': 'disable'},
+        environment={'PGSSLMODE': 'disable'},
     ).once()
     flexmock(module).should_receive('execute_command').with_args(
         (
@@ -1154,7 +1155,7 @@ def test_restore_data_source_dump_runs_psql_for_plain_database_dump():
             '--command',
             'ANALYZE',
         ),
-        extra_environment={'PGSSLMODE': 'disable'},
+        environment={'PGSSLMODE': 'disable'},
     ).once()
 
     module.restore_data_source_dump(
@@ -1187,7 +1188,7 @@ def test_restore_data_source_dump_runs_non_default_pg_restore_and_psql():
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
-    flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
+    flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_dump_path')
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
     flexmock(module).should_receive('execute_command_with_processes').with_args(
@@ -1208,7 +1209,7 @@ def test_restore_data_source_dump_runs_non_default_pg_restore_and_psql():
         processes=[extract_process],
         output_log_level=logging.DEBUG,
         input_file=extract_process.stdout,
-        extra_environment={'PGSSLMODE': 'disable'},
+        environment={'PGSSLMODE': 'disable'},
     ).once()
     flexmock(module).should_receive('execute_command').with_args(
         (
@@ -1226,7 +1227,7 @@ def test_restore_data_source_dump_runs_non_default_pg_restore_and_psql():
             '--command',
             'ANALYZE',
         ),
-        extra_environment={'PGSSLMODE': 'disable'},
+        environment={'PGSSLMODE': 'disable'},
     ).once()
 
     module.restore_data_source_dump(
@@ -1251,7 +1252,7 @@ def test_restore_data_source_dump_with_dry_run_skips_restore():
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
-    flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
+    flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_dump_path')
     flexmock(module.dump).should_receive('make_data_source_dump_filename')
     flexmock(module).should_receive('execute_command_with_processes').never()
@@ -1278,7 +1279,7 @@ def test_restore_data_source_dump_without_extract_process_restores_from_disk():
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
-    flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
+    flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_dump_path')
     flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return('/dump/path')
     flexmock(module).should_receive('execute_command_with_processes').with_args(
@@ -1295,7 +1296,7 @@ def test_restore_data_source_dump_without_extract_process_restores_from_disk():
         processes=[],
         output_log_level=logging.DEBUG,
         input_file=None,
-        extra_environment={'PGSSLMODE': 'disable'},
+        environment={'PGSSLMODE': 'disable'},
     ).once()
     flexmock(module).should_receive('execute_command').with_args(
         (
@@ -1308,7 +1309,7 @@ def test_restore_data_source_dump_without_extract_process_restores_from_disk():
             '--command',
             'ANALYZE',
         ),
-        extra_environment={'PGSSLMODE': 'disable'},
+        environment={'PGSSLMODE': 'disable'},
     ).once()
 
     module.restore_data_source_dump(
@@ -1333,7 +1334,7 @@ def test_restore_data_source_dump_with_schemas_restores_schemas():
     flexmock(module.borgmatic.hooks.credential.parse).should_receive(
         'resolve_credential'
     ).replace_with(lambda value, config: value)
-    flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
+    flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
     flexmock(module).should_receive('make_dump_path')
     flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return('/dump/path')
     flexmock(module).should_receive('execute_command_with_processes').with_args(
@@ -1354,7 +1355,7 @@ def test_restore_data_source_dump_with_schemas_restores_schemas():
         processes=[],
         output_log_level=logging.DEBUG,
         input_file=None,
-        extra_environment={'PGSSLMODE': 'disable'},
+        environment={'PGSSLMODE': 'disable'},
     ).once()
     flexmock(module).should_receive('execute_command').with_args(
         (
@@ -1367,7 +1368,7 @@ def test_restore_data_source_dump_with_schemas_restores_schemas():
             '--command',
             'ANALYZE',
         ),
-        extra_environment={'PGSSLMODE': 'disable'},
+        environment={'PGSSLMODE': 'disable'},
     ).once()
 
     module.restore_data_source_dump(

+ 6 - 6
tests/unit/hooks/test_command.py

@@ -45,7 +45,7 @@ def test_make_environment_with_pyinstaller_clears_LD_LIBRARY_PATH():
 def test_make_environment_with_pyinstaller_and_LD_LIBRARY_PATH_ORIG_copies_it_into_LD_LIBRARY_PATH():
     assert module.make_environment(
         {'LD_LIBRARY_PATH_ORIG': '/lib/lib/lib'}, sys_module=flexmock(frozen=True, _MEIPASS='yup')
-    ) == {'LD_LIBRARY_PATH': '/lib/lib/lib'}
+    ) == {'LD_LIBRARY_PATH_ORIG': '/lib/lib/lib', 'LD_LIBRARY_PATH': '/lib/lib/lib'}
 
 
 def test_execute_hook_invokes_each_command():
@@ -57,7 +57,7 @@ def test_execute_hook_invokes_each_command():
         [':'],
         output_log_level=logging.WARNING,
         shell=True,
-        extra_environment={},
+        environment={},
     ).once()
 
     module.execute_hook([':'], None, 'config.yaml', 'pre-backup', dry_run=False)
@@ -72,13 +72,13 @@ def test_execute_hook_with_multiple_commands_invokes_each_command():
         [':'],
         output_log_level=logging.WARNING,
         shell=True,
-        extra_environment={},
+        environment={},
     ).once()
     flexmock(module.borgmatic.execute).should_receive('execute_command').with_args(
         ['true'],
         output_log_level=logging.WARNING,
         shell=True,
-        extra_environment={},
+        environment={},
     ).once()
 
     module.execute_hook([':', 'true'], None, 'config.yaml', 'pre-backup', dry_run=False)
@@ -95,7 +95,7 @@ def test_execute_hook_with_umask_sets_that_umask():
         [':'],
         output_log_level=logging.WARNING,
         shell=True,
-        extra_environment={},
+        environment={},
     )
 
     module.execute_hook([':'], 77, 'config.yaml', 'pre-backup', dry_run=False)
@@ -124,7 +124,7 @@ def test_execute_hook_on_error_logs_as_error():
         [':'],
         output_log_level=logging.ERROR,
         shell=True,
-        extra_environment={},
+        environment={},
     ).once()
 
     module.execute_hook([':'], None, 'config.yaml', 'on-error', dry_run=False)

+ 24 - 46
tests/unit/test_execute.py

@@ -159,8 +159,15 @@ def test_mask_command_secrets_passes_through_other_commands():
             ('foo', 'bar'),
             None,
             None,
-            {'DBPASS': 'secret', 'OTHER': 'thing'},
-            'DBPASS=*** OTHER=*** foo bar',
+            {'UNKNOWN': 'secret', 'OTHER': 'thing'},
+            'foo bar',
+        ),
+        (
+            ('foo', 'bar'),
+            None,
+            None,
+            {'PGTHING': 'secret', 'BORG_OTHER': 'thing'},
+            'PGTHING=*** BORG_OTHER=*** foo bar',
         ),
     ),
 )
@@ -176,7 +183,6 @@ def test_log_command_logs_command_constructed_from_arguments(
 def test_execute_command_calls_full_command():
     full_command = ['foo', 'bar']
     flexmock(module).should_receive('log_command')
-    flexmock(module.os, environ={'a': 'b'})
     flexmock(module.subprocess).should_receive('Popen').with_args(
         full_command,
         stdin=None,
@@ -199,7 +205,6 @@ def test_execute_command_calls_full_command_with_output_file():
     full_command = ['foo', 'bar']
     output_file = flexmock(name='test')
     flexmock(module).should_receive('log_command')
-    flexmock(module.os, environ={'a': 'b'})
     flexmock(module.subprocess).should_receive('Popen').with_args(
         full_command,
         stdin=None,
@@ -221,7 +226,6 @@ def test_execute_command_calls_full_command_with_output_file():
 def test_execute_command_calls_full_command_without_capturing_output():
     full_command = ['foo', 'bar']
     flexmock(module).should_receive('log_command')
-    flexmock(module.os, environ={'a': 'b'})
     flexmock(module.subprocess).should_receive('Popen').with_args(
         full_command,
         stdin=None,
@@ -245,7 +249,6 @@ def test_execute_command_calls_full_command_with_input_file():
     full_command = ['foo', 'bar']
     input_file = flexmock(name='test')
     flexmock(module).should_receive('log_command')
-    flexmock(module.os, environ={'a': 'b'})
     flexmock(module.subprocess).should_receive('Popen').with_args(
         full_command,
         stdin=input_file,
@@ -267,7 +270,6 @@ def test_execute_command_calls_full_command_with_input_file():
 def test_execute_command_calls_full_command_with_shell():
     full_command = ['foo', 'bar']
     flexmock(module).should_receive('log_command')
-    flexmock(module.os, environ={'a': 'b'})
     flexmock(module.subprocess).should_receive('Popen').with_args(
         ' '.join(full_command),
         stdin=None,
@@ -286,24 +288,23 @@ def test_execute_command_calls_full_command_with_shell():
     assert output is None
 
 
-def test_execute_command_calls_full_command_with_extra_environment():
+def test_execute_command_calls_full_command_with_environment():
     full_command = ['foo', 'bar']
     flexmock(module).should_receive('log_command')
-    flexmock(module.os, environ={'a': 'b'})
     flexmock(module.subprocess).should_receive('Popen').with_args(
         full_command,
         stdin=None,
         stdout=module.subprocess.PIPE,
         stderr=module.subprocess.STDOUT,
         shell=False,
-        env={'a': 'b', 'c': 'd'},
+        env={'a': 'b'},
         cwd=None,
         close_fds=True,
     ).and_return(flexmock(stdout=None)).once()
     flexmock(module.borgmatic.logger).should_receive('Log_prefix').and_return(flexmock())
     flexmock(module).should_receive('log_outputs')
 
-    output = module.execute_command(full_command, extra_environment={'c': 'd'})
+    output = module.execute_command(full_command, environment={'a': 'b'})
 
     assert output is None
 
@@ -311,7 +312,6 @@ def test_execute_command_calls_full_command_with_extra_environment():
 def test_execute_command_calls_full_command_with_working_directory():
     full_command = ['foo', 'bar']
     flexmock(module).should_receive('log_command')
-    flexmock(module.os, environ={'a': 'b'})
     flexmock(module.subprocess).should_receive('Popen').with_args(
         full_command,
         stdin=None,
@@ -333,21 +333,20 @@ def test_execute_command_calls_full_command_with_working_directory():
 def test_execute_command_with_BORG_PASSPHRASE_FD_leaves_file_descriptors_open():
     full_command = ['foo', 'bar']
     flexmock(module).should_receive('log_command')
-    flexmock(module.os, environ={'a': 'b'})
     flexmock(module.subprocess).should_receive('Popen').with_args(
         full_command,
         stdin=None,
         stdout=module.subprocess.PIPE,
         stderr=module.subprocess.STDOUT,
         shell=False,
-        env={'a': 'b', 'BORG_PASSPHRASE_FD': '4'},
+        env={'BORG_PASSPHRASE_FD': '4'},
         cwd=None,
         close_fds=False,
     ).and_return(flexmock(stdout=None)).once()
     flexmock(module.borgmatic.logger).should_receive('Log_prefix').and_return(flexmock())
     flexmock(module).should_receive('log_outputs')
 
-    output = module.execute_command(full_command, extra_environment={'BORG_PASSPHRASE_FD': '4'})
+    output = module.execute_command(full_command, environment={'BORG_PASSPHRASE_FD': '4'})
 
     assert output is None
 
@@ -356,7 +355,6 @@ def test_execute_command_without_run_to_completion_returns_process():
     full_command = ['foo', 'bar']
     process = flexmock()
     flexmock(module).should_receive('log_command')
-    flexmock(module.os, environ={'a': 'b'})
     flexmock(module.subprocess).should_receive('Popen').with_args(
         full_command,
         stdin=None,
@@ -377,7 +375,6 @@ def test_execute_command_and_capture_output_returns_stdout():
     full_command = ['foo', 'bar']
     expected_output = '[]'
     flexmock(module).should_receive('log_command')
-    flexmock(module.os, environ={'a': 'b'})
     flexmock(module.subprocess).should_receive('check_output').with_args(
         full_command,
         stderr=None,
@@ -396,7 +393,6 @@ def test_execute_command_and_capture_output_with_capture_stderr_returns_stderr()
     full_command = ['foo', 'bar']
     expected_output = '[]'
     flexmock(module).should_receive('log_command')
-    flexmock(module.os, environ={'a': 'b'})
     flexmock(module.subprocess).should_receive('check_output').with_args(
         full_command,
         stderr=module.subprocess.STDOUT,
@@ -416,7 +412,6 @@ def test_execute_command_and_capture_output_returns_output_when_process_error_is
     expected_output = '[]'
     err_output = b'[]'
     flexmock(module).should_receive('log_command')
-    flexmock(module.os, environ={'a': 'b'})
     flexmock(module.subprocess).should_receive('check_output').with_args(
         full_command,
         stderr=None,
@@ -438,7 +433,6 @@ def test_execute_command_and_capture_output_raises_when_command_errors():
     full_command = ['foo', 'bar']
     expected_output = '[]'
     flexmock(module).should_receive('log_command')
-    flexmock(module.os, environ={'a': 'b'})
     flexmock(module.subprocess).should_receive('check_output').with_args(
         full_command,
         stderr=None,
@@ -459,7 +453,6 @@ def test_execute_command_and_capture_output_returns_output_with_shell():
     full_command = ['foo', 'bar']
     expected_output = '[]'
     flexmock(module).should_receive('log_command')
-    flexmock(module.os, environ={'a': 'b'})
     flexmock(module.subprocess).should_receive('check_output').with_args(
         'foo bar',
         stderr=None,
@@ -474,22 +467,21 @@ def test_execute_command_and_capture_output_returns_output_with_shell():
     assert output == expected_output
 
 
-def test_execute_command_and_capture_output_returns_output_with_extra_environment():
+def test_execute_command_and_capture_output_returns_output_with_environment():
     full_command = ['foo', 'bar']
     expected_output = '[]'
     flexmock(module).should_receive('log_command')
-    flexmock(module.os, environ={'a': 'b'})
     flexmock(module.subprocess).should_receive('check_output').with_args(
         full_command,
         stderr=None,
         shell=False,
-        env={'a': 'b', 'c': 'd'},
+        env={'a': 'b'},
         cwd=None,
         close_fds=True,
     ).and_return(flexmock(decode=lambda: expected_output)).once()
 
     output = module.execute_command_and_capture_output(
-        full_command, shell=False, extra_environment={'c': 'd'}
+        full_command, shell=False, environment={'a': 'b'}
     )
 
     assert output == expected_output
@@ -499,7 +491,6 @@ def test_execute_command_and_capture_output_returns_output_with_working_director
     full_command = ['foo', 'bar']
     expected_output = '[]'
     flexmock(module).should_receive('log_command')
-    flexmock(module.os, environ={'a': 'b'})
     flexmock(module.subprocess).should_receive('check_output').with_args(
         full_command,
         stderr=None,
@@ -520,12 +511,11 @@ def test_execute_command_and_capture_output_with_BORG_PASSPHRASE_FD_leaves_file_
     full_command = ['foo', 'bar']
     expected_output = '[]'
     flexmock(module).should_receive('log_command')
-    flexmock(module.os, environ={'a': 'b'})
     flexmock(module.subprocess).should_receive('check_output').with_args(
         full_command,
         stderr=None,
         shell=False,
-        env={'a': 'b', 'BORG_PASSPHRASE_FD': '4'},
+        env={'BORG_PASSPHRASE_FD': '4'},
         cwd=None,
         close_fds=False,
     ).and_return(flexmock(decode=lambda: expected_output)).once()
@@ -533,7 +523,7 @@ def test_execute_command_and_capture_output_with_BORG_PASSPHRASE_FD_leaves_file_
     output = module.execute_command_and_capture_output(
         full_command,
         shell=False,
-        extra_environment={'BORG_PASSPHRASE_FD': '4'},
+        environment={'BORG_PASSPHRASE_FD': '4'},
     )
 
     assert output == expected_output
@@ -543,7 +533,6 @@ def test_execute_command_with_processes_calls_full_command():
     full_command = ['foo', 'bar']
     processes = (flexmock(),)
     flexmock(module).should_receive('log_command')
-    flexmock(module.os, environ={'a': 'b'})
     flexmock(module.subprocess).should_receive('Popen').with_args(
         full_command,
         stdin=None,
@@ -566,7 +555,6 @@ def test_execute_command_with_processes_returns_output_with_output_log_level_non
     full_command = ['foo', 'bar']
     processes = (flexmock(),)
     flexmock(module).should_receive('log_command')
-    flexmock(module.os, environ={'a': 'b'})
     process = flexmock(stdout=None)
     flexmock(module.subprocess).should_receive('Popen').with_args(
         full_command,
@@ -591,7 +579,6 @@ def test_execute_command_with_processes_calls_full_command_with_output_file():
     processes = (flexmock(),)
     output_file = flexmock(name='test')
     flexmock(module).should_receive('log_command')
-    flexmock(module.os, environ={'a': 'b'})
     flexmock(module.subprocess).should_receive('Popen').with_args(
         full_command,
         stdin=None,
@@ -614,7 +601,6 @@ def test_execute_command_with_processes_calls_full_command_without_capturing_out
     full_command = ['foo', 'bar']
     processes = (flexmock(),)
     flexmock(module).should_receive('log_command')
-    flexmock(module.os, environ={'a': 'b'})
     flexmock(module.subprocess).should_receive('Popen').with_args(
         full_command,
         stdin=None,
@@ -641,7 +627,6 @@ def test_execute_command_with_processes_calls_full_command_with_input_file():
     processes = (flexmock(),)
     input_file = flexmock(name='test')
     flexmock(module).should_receive('log_command')
-    flexmock(module.os, environ={'a': 'b'})
     flexmock(module.subprocess).should_receive('Popen').with_args(
         full_command,
         stdin=input_file,
@@ -664,7 +649,6 @@ def test_execute_command_with_processes_calls_full_command_with_shell():
     full_command = ['foo', 'bar']
     processes = (flexmock(),)
     flexmock(module).should_receive('log_command')
-    flexmock(module.os, environ={'a': 'b'})
     flexmock(module.subprocess).should_receive('Popen').with_args(
         ' '.join(full_command),
         stdin=None,
@@ -683,27 +667,24 @@ def test_execute_command_with_processes_calls_full_command_with_shell():
     assert output is None
 
 
-def test_execute_command_with_processes_calls_full_command_with_extra_environment():
+def test_execute_command_with_processes_calls_full_command_with_environment():
     full_command = ['foo', 'bar']
     processes = (flexmock(),)
     flexmock(module).should_receive('log_command')
-    flexmock(module.os, environ={'a': 'b'})
     flexmock(module.subprocess).should_receive('Popen').with_args(
         full_command,
         stdin=None,
         stdout=module.subprocess.PIPE,
         stderr=module.subprocess.STDOUT,
         shell=False,
-        env={'a': 'b', 'c': 'd'},
+        env={'a': 'b'},
         cwd=None,
         close_fds=True,
     ).and_return(flexmock(stdout=None)).once()
     flexmock(module.borgmatic.logger).should_receive('Log_prefix').and_return(flexmock())
     flexmock(module).should_receive('log_outputs')
 
-    output = module.execute_command_with_processes(
-        full_command, processes, extra_environment={'c': 'd'}
-    )
+    output = module.execute_command_with_processes(full_command, processes, environment={'a': 'b'})
 
     assert output is None
 
@@ -712,7 +693,6 @@ def test_execute_command_with_processes_calls_full_command_with_working_director
     full_command = ['foo', 'bar']
     processes = (flexmock(),)
     flexmock(module).should_receive('log_command')
-    flexmock(module.os, environ={'a': 'b'})
     flexmock(module.subprocess).should_receive('Popen').with_args(
         full_command,
         stdin=None,
@@ -737,14 +717,13 @@ def test_execute_command_with_processes_with_BORG_PASSPHRASE_FD_leaves_file_desc
     full_command = ['foo', 'bar']
     processes = (flexmock(),)
     flexmock(module).should_receive('log_command')
-    flexmock(module.os, environ={'a': 'b'})
     flexmock(module.subprocess).should_receive('Popen').with_args(
         full_command,
         stdin=None,
         stdout=module.subprocess.PIPE,
         stderr=module.subprocess.STDOUT,
         shell=False,
-        env={'a': 'b', 'BORG_PASSPHRASE_FD': '4'},
+        env={'BORG_PASSPHRASE_FD': '4'},
         cwd=None,
         close_fds=False,
     ).and_return(flexmock(stdout=None)).once()
@@ -754,7 +733,7 @@ def test_execute_command_with_processes_with_BORG_PASSPHRASE_FD_leaves_file_desc
     output = module.execute_command_with_processes(
         full_command,
         processes,
-        extra_environment={'BORG_PASSPHRASE_FD': '4'},
+        environment={'BORG_PASSPHRASE_FD': '4'},
     )
 
     assert output is None
@@ -767,7 +746,6 @@ def test_execute_command_with_processes_kills_processes_on_error():
     process.should_receive('poll')
     process.should_receive('kill').once()
     processes = (process,)
-    flexmock(module.os, environ={'a': 'b'})
     flexmock(module.subprocess).should_receive('Popen').with_args(
         full_command,
         stdin=None,