filebackup 0.5.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,99 @@
1
+ Metadata-Version: 2.4
2
+ Name: filebackup
3
+ Version: 0.5.3
4
+ Summary: Tool for creating and restoring file system backups.
5
+ Keywords: backup,mirror,offsite,restore
6
+ Author: David Brownell
7
+ Author-email: David Brownell <github@DavidBrownell.com>
8
+ License-Expression: MIT
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: End Users/Desktop
13
+ Classifier: Intended Audience :: System Administrators
14
+ Classifier: Natural Language :: English
15
+ Classifier: Operating System :: MacOS
16
+ Classifier: Operating System :: Microsoft :: Windows
17
+ Classifier: Operating System :: POSIX :: Linux
18
+ Classifier: Programming Language :: Python
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: System :: Archiving :: Backup
24
+ Classifier: Topic :: System :: Archiving :: Mirroring
25
+ Classifier: Topic :: System :: Systems Administration
26
+ Classifier: Topic :: Utilities
27
+ Requires-Dist: dbrownell-common>=0.15.0
28
+ Requires-Dist: paramiko>=3.5.1
29
+ Requires-Dist: typer>=0.15.3
30
+ Requires-Python: >=3.10
31
+ Project-URL: Documentation, https://github.com/davidbrownell/FileBackup
32
+ Project-URL: Homepage, https://github.com/davidbrownell/FileBackup
33
+ Project-URL: Repository, https://github.com/davidbrownell/FileBackup
34
+ Description-Content-Type: text/markdown
35
+
36
+ **Project:**
37
+ [![License](https://img.shields.io/github/license/davidbrownell/FileBackup?color=dark-green)](https://github.com/davidbrownell/FileBackup/blob/master/LICENSE)
38
+
39
+ **Package:**
40
+ [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/FileBackup?color=dark-green)](https://pypi.org/project/FileBackup/)
41
+ [![PyPI - Version](https://img.shields.io/pypi/v/FileBackup?color=dark-green)](https://pypi.org/project/FileBackup/)
42
+ [![PyPI - Downloads](https://img.shields.io/pypi/dm/FileBackup)](https://pypistats.org/packages/filebackup)
43
+
44
+ **Development:**
45
+ [![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv)
46
+ [![CI](https://github.com/davidbrownell/FileBackup/actions/workflows/CICD.yml/badge.svg)](https://github.com/davidbrownell/FileBackup/actions/workflows/CICD.yml)
47
+ [![Code Coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/davidbrownell/f15146b1b8fdc0a5d45ac0eb786a84f7/raw/FileBackup_code_coverage.json)](https://github.com/davidbrownell/FileBackup/actions)
48
+ [![GitHub commit activity](https://img.shields.io/github/commit-activity/y/davidbrownell/FileBackup?color=dark-green)](https://github.com/davidbrownell/FileBackup/commits/main/)
49
+
50
+ <!-- Content above this delimiter will be copied to the generated README.md file. DO NOT REMOVE THIS COMMENT, as it will cause regeneration to fail. -->
51
+
52
+ ## Contents
53
+ - [Overview](#overview)
54
+ - [Installation](#installation)
55
+ - [Development](#development)
56
+ - [Additional Information](#additional-information)
57
+ - [License](#license)
58
+
59
+ ## Overview
60
+ TODO: Complete this section
61
+
62
+ ### How to use `FileBackup`
63
+ TODO: Complete this section
64
+
65
+ <!-- Content below this delimiter will be copied to the generated README.md file. DO NOT REMOVE THIS COMMENT, as it will cause regeneration to fail. -->
66
+
67
+ ## Installation
68
+
69
+ | Installation Method | Command |
70
+ | --- | --- |
71
+ | Via [uv](https://github.com/astral-sh/uv) | `uv add FileBackup` |
72
+ | Via [pip](https://pip.pypa.io/en/stable/) | `pip install FileBackup` |
73
+
74
+ ### Verifying Signed Artifacts
75
+ Artifacts are signed and verified using [py-minisign](https://github.com/x13a/py-minisign) and the public key in the file `./minisign_key.pub`.
76
+
77
+ To verify that an artifact is valid, visit [the latest release](https://github.com/davidbrownell/FileBackup/releases/latest) and download the `.minisign` signature file that corresponds to the artifact, then run the following command, replacing `<filename>` with the name of the artifact to be verified:
78
+
79
+ ```shell
80
+ uv run --with py-minisign python -c "import minisign; minisign.PublicKey.from_file('minisign_key.pub').verify_file('<filename>'); print('The file has been verified.')"
81
+ ```
82
+
83
+ ## Development
84
+ Please visit [Contributing](https://github.com/davidbrownell/FileBackup/blob/main/CONTRIBUTING.md) and [Development](https://github.com/davidbrownell/FileBackup/blob/main/DEVELOPMENT.md) for information on contributing to this project.
85
+
86
+ ## Additional Information
87
+ Additional information can be found at these locations.
88
+
89
+ | Title | Document | Description |
90
+ | --- | --- | --- |
91
+ | Code of Conduct | [CODE_OF_CONDUCT.md](https://github.com/davidbrownell/FileBackup/blob/main/CODE_OF_CONDUCT.md) | Information about the norms, rules, and responsibilities we adhere to when participating in this open source community. |
92
+ | Contributing | [CONTRIBUTING.md](https://github.com/davidbrownell/FileBackup/blob/main/CONTRIBUTING.md) | Information about contributing to this project. |
93
+ | Development | [DEVELOPMENT.md](https://github.com/davidbrownell/FileBackup/blob/main/DEVELOPMENT.md) | Information about development activities involved in making changes to this project. |
94
+ | Governance | [GOVERNANCE.md](https://github.com/davidbrownell/FileBackup/blob/main/GOVERNANCE.md) | Information about how this project is governed. |
95
+ | Maintainers | [MAINTAINERS.md](https://github.com/davidbrownell/FileBackup/blob/main/MAINTAINERS.md) | Information about individuals who maintain this project. |
96
+ | Security | [SECURITY.md](https://github.com/davidbrownell/FileBackup/blob/main/SECURITY.md) | Information about how to privately report security issues associated with this project. |
97
+
98
+ ## License
99
+ `FileBackup` is licensed under the <a href="https://choosealicense.com/licenses/MIT/" target="_blank">MIT</a> license.
@@ -0,0 +1,64 @@
1
+ **Project:**
2
+ [![License](https://img.shields.io/github/license/davidbrownell/FileBackup?color=dark-green)](https://github.com/davidbrownell/FileBackup/blob/master/LICENSE)
3
+
4
+ **Package:**
5
+ [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/FileBackup?color=dark-green)](https://pypi.org/project/FileBackup/)
6
+ [![PyPI - Version](https://img.shields.io/pypi/v/FileBackup?color=dark-green)](https://pypi.org/project/FileBackup/)
7
+ [![PyPI - Downloads](https://img.shields.io/pypi/dm/FileBackup)](https://pypistats.org/packages/filebackup)
8
+
9
+ **Development:**
10
+ [![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv)
11
+ [![CI](https://github.com/davidbrownell/FileBackup/actions/workflows/CICD.yml/badge.svg)](https://github.com/davidbrownell/FileBackup/actions/workflows/CICD.yml)
12
+ [![Code Coverage](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/davidbrownell/f15146b1b8fdc0a5d45ac0eb786a84f7/raw/FileBackup_code_coverage.json)](https://github.com/davidbrownell/FileBackup/actions)
13
+ [![GitHub commit activity](https://img.shields.io/github/commit-activity/y/davidbrownell/FileBackup?color=dark-green)](https://github.com/davidbrownell/FileBackup/commits/main/)
14
+
15
+ <!-- Content above this delimiter will be copied to the generated README.md file. DO NOT REMOVE THIS COMMENT, as it will cause regeneration to fail. -->
16
+
17
+ ## Contents
18
+ - [Overview](#overview)
19
+ - [Installation](#installation)
20
+ - [Development](#development)
21
+ - [Additional Information](#additional-information)
22
+ - [License](#license)
23
+
24
+ ## Overview
25
+ TODO: Complete this section
26
+
27
+ ### How to use `FileBackup`
28
+ TODO: Complete this section
29
+
30
+ <!-- Content below this delimiter will be copied to the generated README.md file. DO NOT REMOVE THIS COMMENT, as it will cause regeneration to fail. -->
31
+
32
+ ## Installation
33
+
34
+ | Installation Method | Command |
35
+ | --- | --- |
36
+ | Via [uv](https://github.com/astral-sh/uv) | `uv add FileBackup` |
37
+ | Via [pip](https://pip.pypa.io/en/stable/) | `pip install FileBackup` |
38
+
39
+ ### Verifying Signed Artifacts
40
+ Artifacts are signed and verified using [py-minisign](https://github.com/x13a/py-minisign) and the public key in the file `./minisign_key.pub`.
41
+
42
+ To verify that an artifact is valid, visit [the latest release](https://github.com/davidbrownell/FileBackup/releases/latest) and download the `.minisign` signature file that corresponds to the artifact, then run the following command, replacing `<filename>` with the name of the artifact to be verified:
43
+
44
+ ```shell
45
+ uv run --with py-minisign python -c "import minisign; minisign.PublicKey.from_file('minisign_key.pub').verify_file('<filename>'); print('The file has been verified.')"
46
+ ```
47
+
48
+ ## Development
49
+ Please visit [Contributing](https://github.com/davidbrownell/FileBackup/blob/main/CONTRIBUTING.md) and [Development](https://github.com/davidbrownell/FileBackup/blob/main/DEVELOPMENT.md) for information on contributing to this project.
50
+
51
+ ## Additional Information
52
+ Additional information can be found at these locations.
53
+
54
+ | Title | Document | Description |
55
+ | --- | --- | --- |
56
+ | Code of Conduct | [CODE_OF_CONDUCT.md](https://github.com/davidbrownell/FileBackup/blob/main/CODE_OF_CONDUCT.md) | Information about the norms, rules, and responsibilities we adhere to when participating in this open source community. |
57
+ | Contributing | [CONTRIBUTING.md](https://github.com/davidbrownell/FileBackup/blob/main/CONTRIBUTING.md) | Information about contributing to this project. |
58
+ | Development | [DEVELOPMENT.md](https://github.com/davidbrownell/FileBackup/blob/main/DEVELOPMENT.md) | Information about development activities involved in making changes to this project. |
59
+ | Governance | [GOVERNANCE.md](https://github.com/davidbrownell/FileBackup/blob/main/GOVERNANCE.md) | Information about how this project is governed. |
60
+ | Maintainers | [MAINTAINERS.md](https://github.com/davidbrownell/FileBackup/blob/main/MAINTAINERS.md) | Information about individuals who maintain this project. |
61
+ | Security | [SECURITY.md](https://github.com/davidbrownell/FileBackup/blob/main/SECURITY.md) | Information about how to privately report security issues associated with this project. |
62
+
63
+ ## License
64
+ `FileBackup` is licensed under the <a href="https://choosealicense.com/licenses/MIT/" target="_blank">MIT</a> license.
@@ -0,0 +1,121 @@
1
+ [project]
2
+ name = "FileBackup"
3
+ version = "0.5.3"
4
+ # ^^^^^
5
+ # Wheel names will be generated according to this value. Do not manually modify this value; instead
6
+ # update it according to committed changes by running this command from the root of the repository:
7
+ #
8
+ # uv run python -m AutoGitSemVer.scripts.UpdatePythonVersion ./pyproject.toml ./src
9
+
10
+ description = "Tool for creating and restoring file system backups."
11
+ readme = "README.md"
12
+ authors = [
13
+ { name = "David Brownell", email = "github@DavidBrownell.com" }
14
+ ]
15
+ requires-python = ">= 3.10"
16
+ dependencies = [
17
+ "dbrownell-common>=0.15.0",
18
+ "paramiko>=3.5.1",
19
+ "typer>=0.15.3",
20
+ ]
21
+
22
+ keywords = [
23
+ "backup",
24
+ "mirror",
25
+ "offsite",
26
+ "restore",
27
+ ]
28
+
29
+ license = "MIT"
30
+
31
+ classifiers = [
32
+ "Development Status :: 4 - Beta",
33
+ "Environment :: Console",
34
+ "Intended Audience :: Developers",
35
+ "Intended Audience :: End Users/Desktop",
36
+ "Intended Audience :: System Administrators",
37
+ "Natural Language :: English",
38
+ "Operating System :: MacOS",
39
+ "Operating System :: Microsoft :: Windows",
40
+ "Operating System :: POSIX :: Linux",
41
+ "Programming Language :: Python",
42
+ "Programming Language :: Python :: 3.10",
43
+ "Programming Language :: Python :: 3.11",
44
+ "Programming Language :: Python :: 3.12",
45
+ "Programming Language :: Python :: 3.13",
46
+ "Topic :: System :: Archiving :: Backup",
47
+ "Topic :: System :: Archiving :: Mirroring",
48
+ "Topic :: System :: Systems Administration",
49
+ "Topic :: Utilities",
50
+ ]
51
+
52
+ [project.urls]
53
+ Homepage = "https://github.com/davidbrownell/FileBackup"
54
+ Documentation = "https://github.com/davidbrownell/FileBackup"
55
+ Repository = "https://github.com/davidbrownell/FileBackup"
56
+
57
+ [project.scripts]
58
+ FileBackup = "FileBackup.CommandLine:EntryPoint.app"
59
+ file_backup = "FileBackup.CommandLine:EntryPoint.app"
60
+
61
+ [build-system]
62
+ requires = ["uv_build>=0.8.15,<0.9.0"]
63
+ build-backend = "uv_build"
64
+
65
+ [dependency-groups]
66
+ dev = [
67
+ "autogitsemver>=0.9.2",
68
+ "cx-freeze>=8.3.0",
69
+ "dbrownell-commitemojis>=0.1.4",
70
+ "pre-commit>=4.2.0",
71
+ "py-minisign>=0.12.0",
72
+ "pytest>=8.4.1",
73
+ "pytest-cov>=6.2.1",
74
+ "ruff>=0.12.3",
75
+ ]
76
+
77
+ [tool.pytest.ini_options]
78
+ addopts = "--verbose -vv --capture=no --cov=FileBackup --cov-report term --cov-report xml:coverage.xml --cov-fail-under=85.0"
79
+ python_files = [
80
+ "**/*Test.py",
81
+ ]
82
+
83
+ [tool.ruff]
84
+ line-length = 110
85
+
86
+ [tool.ruff.lint]
87
+ exclude = ["tests/**"]
88
+
89
+ ignore = [
90
+ "ANN002", # Missing type annotation for `*args`
91
+ "ANN003", # Missing type annotation for `**kwargs`
92
+ "BLE001", # Do not catch blind exception: `Exception`
93
+ "COM812", # Trailing comma missing
94
+ "D105", # Missing docstring in magic method
95
+ "D107", # Missing docstring in `__init__` method
96
+ "D202", # No blank lines allowed after function docstring
97
+ "E501", # Line too long
98
+ "FIX002", # Line contains TODO, consider resolving the issue
99
+ "I001", # Import block is un-sorted or un-formatted
100
+ "N802", # Function name `xxx` should be lowercase
101
+ "N999", # Invalid module name
102
+ "RSE102", # Unnecessary parentheses on raise exception
103
+ "S101", # Use of assert detected
104
+ "TC006", # Add quotes to type expression in `typing.cast()`
105
+ "TD002", # Missing author in TODO
106
+ "TD003", # Missing issue link for this TODO
107
+ "TRY002", # Create your own exception
108
+ "TRY300", # Consider moving this statement to an `else` block
109
+ "UP032", # Use f-string instead of `format` call
110
+ ]
111
+
112
+ [tool.ruff.lint.mccabe]
113
+ max-complexity = 15
114
+
115
+ [tool.ruff.lint.pylint]
116
+ max-args = 10
117
+ max-branches = 20
118
+ max-returns = 20
119
+
120
+ [tool.uv.build-backend]
121
+ module-name = "FileBackup"
@@ -0,0 +1,75 @@
1
+ # ----------------------------------------------------------------------
2
+ # |
3
+ # | CommandLineArguments.py
4
+ # |
5
+ # | David Brownell <db@DavidBrownell.com>
6
+ # | 2024-06-12 13:20:05
7
+ # |
8
+ # ----------------------------------------------------------------------
9
+ # |
10
+ # | Copyright David Brownell 2024
11
+ # | Distributed under the MIT License.
12
+ # |
13
+ # ----------------------------------------------------------------------
14
+
15
+ import re
16
+
17
+ from typing import Pattern
18
+
19
+ import typer
20
+
21
+
22
+ # ----------------------------------------------------------------------
23
+ def ToRegex(
24
+ values: list[str],
25
+ ) -> list[Pattern]:
26
+ expressions: list[Pattern] = []
27
+
28
+ for value in values:
29
+ try:
30
+ expressions.append(re.compile("^{}$".format(value)))
31
+ except re.error as ex:
32
+ raise typer.BadParameter("The regular expression '{}' is not valid ({}).".format(value, ex))
33
+
34
+ return expressions
35
+
36
+
37
+ # ----------------------------------------------------------------------
38
+ input_filename_or_dirs_argument = typer.Argument(
39
+ ..., exists=True, resolve_path=True, help="Input filename or directory."
40
+ )
41
+
42
+ ssd_option = typer.Option(
43
+ "--ssd",
44
+ help="Processes tasks in parallel to leverage the capabilities of solid-state-drives.",
45
+ )
46
+ ssd_option_default = False
47
+
48
+ quiet_option = typer.Option("--quiet", help="Reduce the amount of information displayed.")
49
+ quiet_option_default = False
50
+
51
+ force_option = typer.Option(
52
+ "--force",
53
+ help="Ignore previous backup information and overwrite all data in the destination data store.",
54
+ )
55
+ force_option_default = False
56
+
57
+ verbose_option = typer.Option("--verbose", help="Write verbose information to the terminal.")
58
+ verbose_option_default = False
59
+
60
+ debug_option = typer.Option("--debug", help="Write debug information to the terminal.")
61
+ debug_option_default = False
62
+
63
+ file_include_option = typer.Option(
64
+ "--file-include",
65
+ callback=ToRegex,
66
+ help="Regular expression (based on a posix path) used to include files and/or directories when preserving content.",
67
+ )
68
+ file_include_option_default: list[str] = []
69
+
70
+ file_exclude_option = typer.Option(
71
+ "--file-exclude",
72
+ callback=ToRegex,
73
+ help="Regular expression (based on a posix path) used to exclude files and/or directories when preserving content.",
74
+ )
75
+ file_exclude_option_default: list[str] = []
@@ -0,0 +1,53 @@
1
+ # ----------------------------------------------------------------------
2
+ # |
3
+ # | Copyright (c) 2024 David Brownell
4
+ # | Distributed under the MIT License.
5
+ # |
6
+ # ----------------------------------------------------------------------
7
+ """Tools to backup and restore files and directories."""
8
+
9
+ import sys
10
+
11
+ import typer
12
+
13
+ from typer.core import TyperGroup # type: ignore [import-untyped]
14
+
15
+ from FileBackup import __version__
16
+ from FileBackup.CommandLine import MirrorEntryPoint
17
+ from FileBackup.CommandLine import OffsiteEntryPoint
18
+
19
+
20
+ # ----------------------------------------------------------------------
21
+ class NaturalOrderGrouper(TyperGroup):
22
+ # pylint: disable=missing-class-docstring
23
+ # ----------------------------------------------------------------------
24
+ def list_commands(self, *args, **kwargs): # pylint: disable=unused-argument
25
+ return self.commands.keys() # pragma: no cover
26
+
27
+
28
+ # ----------------------------------------------------------------------
29
+ app = typer.Typer(
30
+ cls=NaturalOrderGrouper,
31
+ help=__doc__,
32
+ no_args_is_help=True,
33
+ pretty_exceptions_show_locals=False,
34
+ pretty_exceptions_enable=False,
35
+ )
36
+
37
+
38
+ app.add_typer(MirrorEntryPoint.app, name="mirror", help=MirrorEntryPoint.__doc__)
39
+ app.add_typer(OffsiteEntryPoint.app, name="offsite", help=OffsiteEntryPoint.__doc__)
40
+
41
+
42
+ @app.command("version", no_args_is_help=False)
43
+ def Version():
44
+ """Displays the current version and exits."""
45
+
46
+ sys.stdout.write(f"FileBackup v{__version__}\n")
47
+
48
+
49
+ # ----------------------------------------------------------------------
50
+ # ----------------------------------------------------------------------
51
+ # ----------------------------------------------------------------------
52
+ if __name__ == "__main__":
53
+ app() # pragma: no cover
@@ -0,0 +1,182 @@
1
+ # ----------------------------------------------------------------------
2
+ # |
3
+ # | MirrorEntryPoint.py
4
+ # |
5
+ # | David Brownell <db@DavidBrownell.com>
6
+ # | 2024-06-12 13:16:52
7
+ # |
8
+ # ----------------------------------------------------------------------
9
+ # |
10
+ # | Copyright David Brownell 2024
11
+ # | Distributed under the MIT License.
12
+ # |
13
+ # ----------------------------------------------------------------------
14
+ """\
15
+ Mirrors backup content: files created locally will be added to the backup data store; files deleted
16
+ locally will be removed from the backup data store; files modified locally will be modified at the
17
+ backup data store.
18
+ """
19
+
20
+ import datetime
21
+ import textwrap
22
+
23
+ from pathlib import Path
24
+ from typing import Annotated, cast, Pattern
25
+
26
+ import typer
27
+
28
+ from dbrownell_Common.Streams.DoneManager import DoneManager, Flags as DoneManagerFlags # type: ignore [import-untyped]
29
+ from typer.core import TyperGroup # type: ignore [import-untyped]
30
+
31
+ from FileBackup.CommandLine import CommandLineArguments
32
+ from FileBackup.Impl import Common
33
+ from FileBackup import Mirror
34
+
35
+
36
+ # ----------------------------------------------------------------------
37
+ class NaturalOrderGrouper(TyperGroup):
38
+ # pylint: disable=missing-class-docstring
39
+ # ----------------------------------------------------------------------
40
+ def list_commands(self, *args, **kwargs): # pylint: disable=unused-argument
41
+ return self.commands.keys() # pragma: no cover
42
+
43
+
44
+ # ----------------------------------------------------------------------
45
+ app = typer.Typer(
46
+ cls=NaturalOrderGrouper,
47
+ help=__doc__,
48
+ no_args_is_help=True,
49
+ pretty_exceptions_show_locals=False,
50
+ pretty_exceptions_enable=False,
51
+ )
52
+
53
+
54
+ # ----------------------------------------------------------------------
55
+ _destination_argument = typer.Argument(
56
+ ...,
57
+ help="Destination data store used when mirroring local content; see the comments below for information on the different data store destination formats.",
58
+ )
59
+
60
+
61
+ # ----------------------------------------------------------------------
62
+ @app.command(
63
+ "execute",
64
+ epilog=Common.GetDestinationHelp(),
65
+ no_args_is_help=True,
66
+ )
67
+ def Execute(
68
+ destination: Annotated[str, _destination_argument],
69
+ input_filename_or_dirs: Annotated[
70
+ list[Path],
71
+ CommandLineArguments.input_filename_or_dirs_argument,
72
+ ],
73
+ ssd: Annotated[bool, CommandLineArguments.ssd_option] = CommandLineArguments.ssd_option_default,
74
+ force: Annotated[bool, CommandLineArguments.force_option] = CommandLineArguments.force_option_default,
75
+ verbose: Annotated[
76
+ bool, CommandLineArguments.verbose_option
77
+ ] = CommandLineArguments.verbose_option_default,
78
+ quiet: Annotated[bool, CommandLineArguments.quiet_option] = CommandLineArguments.quiet_option_default,
79
+ debug: Annotated[bool, CommandLineArguments.debug_option] = CommandLineArguments.debug_option_default,
80
+ file_include_params: Annotated[
81
+ list[str],
82
+ CommandLineArguments.file_include_option,
83
+ ] = CommandLineArguments.file_include_option_default,
84
+ file_exclude_params: Annotated[
85
+ list[str],
86
+ CommandLineArguments.file_exclude_option,
87
+ ] = CommandLineArguments.file_exclude_option_default,
88
+ ) -> None:
89
+ """Mirrors content to a backup data store."""
90
+
91
+ file_includes = cast(list[Pattern], file_include_params)
92
+ file_excludes = cast(list[Pattern], file_exclude_params)
93
+
94
+ del file_include_params
95
+ del file_exclude_params
96
+
97
+ with DoneManager.CreateCommandLine(
98
+ flags=DoneManagerFlags.Create(verbose=verbose, debug=debug),
99
+ ) as dm:
100
+ dm.WriteVerbose(str(datetime.datetime.now()) + "\n\n")
101
+
102
+ Mirror.Backup(
103
+ dm,
104
+ destination,
105
+ input_filename_or_dirs,
106
+ ssd=ssd,
107
+ force=force,
108
+ quiet=quiet,
109
+ file_includes=file_includes,
110
+ file_excludes=file_excludes,
111
+ )
112
+
113
+
114
+ # ----------------------------------------------------------------------
115
+ @app.command(
116
+ "validate",
117
+ no_args_is_help=True,
118
+ epilog=textwrap.dedent(
119
+ """\
120
+ {}
121
+ Validation Types
122
+ ================
123
+ standard: Validates that files and directories at the destination exist and file sizes match the expected values.
124
+ complete: Validates that files and directories at the destination exist and file hashes match the expected values.
125
+ """,
126
+ )
127
+ .replace("\n", "\n\n")
128
+ .format(Common.GetDestinationHelp()),
129
+ )
130
+ def Validate(
131
+ destination: Annotated[str, _destination_argument],
132
+ validate_type: Annotated[
133
+ Mirror.ValidateType,
134
+ typer.Argument(
135
+ case_sensitive=False,
136
+ help="Specifies the type of validation to use; the the comments below for information on the different validation types.",
137
+ ),
138
+ ] = Mirror.ValidateType.standard,
139
+ ssd: Annotated[bool, CommandLineArguments.ssd_option] = CommandLineArguments.ssd_option_default,
140
+ verbose: Annotated[
141
+ bool, CommandLineArguments.verbose_option
142
+ ] = CommandLineArguments.verbose_option_default,
143
+ quiet: Annotated[bool, CommandLineArguments.quiet_option] = CommandLineArguments.quiet_option_default,
144
+ debug: Annotated[bool, CommandLineArguments.debug_option] = CommandLineArguments.debug_option_default,
145
+ ) -> None:
146
+ """Validates previously mirrored content in the backup data store."""
147
+
148
+ with DoneManager.CreateCommandLine(
149
+ flags=DoneManagerFlags.Create(verbose=verbose, debug=debug),
150
+ ) as dm:
151
+ dm.WriteVerbose(str(datetime.datetime.now()) + "\n\n")
152
+
153
+ Mirror.Validate(
154
+ dm,
155
+ destination,
156
+ validate_type,
157
+ ssd=ssd,
158
+ quiet=quiet,
159
+ )
160
+
161
+
162
+ # ----------------------------------------------------------------------
163
+ @app.command(
164
+ "cleanup",
165
+ epilog=Common.GetDestinationHelp(),
166
+ no_args_is_help=True,
167
+ )
168
+ def Cleanup(
169
+ destination: Annotated[str, _destination_argument],
170
+ verbose: Annotated[
171
+ bool, CommandLineArguments.verbose_option
172
+ ] = CommandLineArguments.verbose_option_default,
173
+ debug: Annotated[bool, CommandLineArguments.debug_option] = CommandLineArguments.debug_option_default,
174
+ ) -> None:
175
+ """Cleans a backup data store after a mirror execution that was interrupted or failed."""
176
+
177
+ with DoneManager.CreateCommandLine(
178
+ flags=DoneManagerFlags.Create(verbose=verbose, debug=debug),
179
+ ) as dm:
180
+ dm.WriteVerbose(str(datetime.datetime.now()) + "\n\n")
181
+
182
+ Mirror.Cleanup(dm, destination)