dar-backup 0.6.13__py3-none-any.whl → 0.6.14__py3-none-any.whl
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.
- dar_backup/__about__.py +1 -1
- dar_backup/dar_backup.py +95 -58
- dar_backup/manager.py +1 -1
- dar_backup/util.py +0 -2
- {dar_backup-0.6.13.dist-info → dar_backup-0.6.14.dist-info}/METADATA +3 -1
- {dar_backup-0.6.13.dist-info → dar_backup-0.6.14.dist-info}/RECORD +9 -9
- {dar_backup-0.6.13.dist-info → dar_backup-0.6.14.dist-info}/WHEEL +0 -0
- {dar_backup-0.6.13.dist-info → dar_backup-0.6.14.dist-info}/entry_points.txt +0 -0
- {dar_backup-0.6.13.dist-info → dar_backup-0.6.14.dist-info}/licenses/LICENSE +0 -0
dar_backup/__about__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.6.
|
|
1
|
+
__version__ = "0.6.14"
|
dar_backup/dar_backup.py
CHANGED
|
@@ -27,11 +27,11 @@ from dar_backup.config_settings import ConfigSettings
|
|
|
27
27
|
from dar_backup.util import list_backups
|
|
28
28
|
from dar_backup.util import run_command
|
|
29
29
|
from dar_backup.util import setup_logging
|
|
30
|
+
from dar_backup.util import get_logger
|
|
30
31
|
from dar_backup.util import BackupError
|
|
31
32
|
from dar_backup.util import RestoreError
|
|
32
33
|
|
|
33
34
|
|
|
34
|
-
RESULT = True
|
|
35
35
|
logger = None
|
|
36
36
|
|
|
37
37
|
def generic_backup(type: str, command: List[str], backup_file: str, backup_definition: str, darrc: str, config_settings: ConfigSettings, args: argparse.Namespace):
|
|
@@ -86,30 +86,37 @@ def generic_backup(type: str, command: List[str], backup_file: str, backup_defin
|
|
|
86
86
|
|
|
87
87
|
|
|
88
88
|
|
|
89
|
-
def find_files_with_paths(
|
|
89
|
+
def find_files_with_paths(xml_doc: str):
|
|
90
90
|
"""
|
|
91
|
-
Finds files within an XML element and returns a list of file
|
|
91
|
+
Finds files within an XML element and returns a list of tuples (file path, size).
|
|
92
92
|
|
|
93
93
|
Args:
|
|
94
|
-
xml_root
|
|
94
|
+
xml_root: str The XML generated by dar -l <archive> -Txml.
|
|
95
95
|
|
|
96
96
|
Returns:
|
|
97
|
-
list: A list of tuples
|
|
97
|
+
list: A list of tuples (file path, size).
|
|
98
98
|
"""
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
99
|
+
#get_logger().debug("Generating list of tuples with file paths and sizes for File elements in dar xml output")
|
|
100
|
+
xml_doc = re.sub(r'<!DOCTYPE[^>]*>', '', xml_doc)
|
|
101
|
+
root = ET.fromstring(xml_doc)
|
|
102
102
|
|
|
103
|
-
|
|
104
|
-
if elem.tag == "Directory":
|
|
105
|
-
current_path.append(elem.get('name'))
|
|
106
|
-
elif elem.tag == "File":
|
|
107
|
-
file_path = ("/".join(current_path + [elem.get('name')]), elem.get('size'))
|
|
108
|
-
files.append(file_path)
|
|
109
|
-
elif elem.tag == "Directory" and elem.get('Name') in current_path:
|
|
110
|
-
current_path.pop()
|
|
103
|
+
files_list = []
|
|
111
104
|
|
|
112
|
-
|
|
105
|
+
def iterate_dir(element, current_path=""):
|
|
106
|
+
for child in element:
|
|
107
|
+
if child.tag == 'Directory':
|
|
108
|
+
dir_name = child.get('name')
|
|
109
|
+
new_path = f"{current_path}/{dir_name}" if current_path else dir_name
|
|
110
|
+
iterate_dir(child, new_path)
|
|
111
|
+
|
|
112
|
+
elif child.tag == 'File':
|
|
113
|
+
file_name = child.get('name')
|
|
114
|
+
file_size = child.get('size')
|
|
115
|
+
file_path = f"{current_path}/{file_name}" if current_path else file_name
|
|
116
|
+
files_list.append((file_path, file_size))
|
|
117
|
+
|
|
118
|
+
iterate_dir(root)
|
|
119
|
+
return files_list
|
|
113
120
|
|
|
114
121
|
|
|
115
122
|
def find_files_between_min_and_max_size(backed_up_files: list[(str, str)], config_settings: ConfigSettings):
|
|
@@ -226,8 +233,7 @@ def verify(args: argparse.Namespace, backup_file: str, backup_definition: str, c
|
|
|
226
233
|
if filecmp.cmp(os.path.join(config_settings.test_restore_dir, restored_file_path.lstrip("/")), os.path.join(root_path, restored_file_path.lstrip("/")), shallow=False):
|
|
227
234
|
logger.info(f"Success: file '{restored_file_path}' matches the original")
|
|
228
235
|
else:
|
|
229
|
-
|
|
230
|
-
result = False
|
|
236
|
+
raise BackupError(f"Failure: file '{restored_file_path}' did not match the original")
|
|
231
237
|
except PermissionError:
|
|
232
238
|
result = False
|
|
233
239
|
logger.exception(f"Permission error while comparing files, continuing....")
|
|
@@ -246,6 +252,7 @@ def restore_backup(backup_name: str, config_settings: ConfigSettings, restore_di
|
|
|
246
252
|
restore_dir (str): The directory where the backup should be restored to.
|
|
247
253
|
selection (str, optional): A selection criteria to restore specific files or directories. Defaults to None.
|
|
248
254
|
"""
|
|
255
|
+
results: List[tuple] = []
|
|
249
256
|
try:
|
|
250
257
|
backup_file = os.path.join(config_settings.backup_dir, backup_name)
|
|
251
258
|
command = ['dar', '-x', backup_file, '-Q', '-D']
|
|
@@ -274,6 +281,7 @@ def restore_backup(backup_name: str, config_settings: ConfigSettings, restore_di
|
|
|
274
281
|
except Exception as e:
|
|
275
282
|
raise RestoreError(f"Unexpected error during restore: {e}") from e
|
|
276
283
|
|
|
284
|
+
return results
|
|
277
285
|
|
|
278
286
|
|
|
279
287
|
def get_backed_up_files(backup_name: str, backup_dir: str):
|
|
@@ -292,15 +300,9 @@ def get_backed_up_files(backup_name: str, backup_dir: str):
|
|
|
292
300
|
try:
|
|
293
301
|
command = ['dar', '-l', backup_path, '-am', '-as', "-Txml" , '-Q']
|
|
294
302
|
logger.info(f"Running command: {' '.join(map(shlex.quote, command))}")
|
|
295
|
-
|
|
303
|
+
command_result = run_command(command)
|
|
296
304
|
# Parse the XML data
|
|
297
|
-
|
|
298
|
-
output = None # help gc
|
|
299
|
-
# Extract full paths and file size for all <file> elements
|
|
300
|
-
file_paths = find_files_with_paths(root)
|
|
301
|
-
root = None # help gc
|
|
302
|
-
logger.trace(str(process))
|
|
303
|
-
logger.trace(file_paths)
|
|
305
|
+
file_paths = find_files_with_paths(command_result.stdout)
|
|
304
306
|
return file_paths
|
|
305
307
|
except subprocess.CalledProcessError as e:
|
|
306
308
|
logger.error(f"Error listing backed up files from DAR archive: '{backup_name}'")
|
|
@@ -371,7 +373,7 @@ def create_backup_command(backup_type: str, backup_file: str, darrc: str, backup
|
|
|
371
373
|
|
|
372
374
|
|
|
373
375
|
|
|
374
|
-
def perform_backup(args: argparse.Namespace, config_settings: ConfigSettings, backup_type: str):
|
|
376
|
+
def perform_backup(args: argparse.Namespace, config_settings: ConfigSettings, backup_type: str) -> List[str]:
|
|
375
377
|
"""
|
|
376
378
|
Perform backup operation.
|
|
377
379
|
|
|
@@ -379,21 +381,27 @@ def perform_backup(args: argparse.Namespace, config_settings: ConfigSettings, ba
|
|
|
379
381
|
args: Command-line arguments.
|
|
380
382
|
config_settings: An instance of the ConfigSettings class.
|
|
381
383
|
backup_type: Type of backup (FULL, DIFF, INCR).
|
|
384
|
+
|
|
385
|
+
Returns:
|
|
386
|
+
List[tuples] - each tuple consists of (<str message>, <exit code>)
|
|
382
387
|
"""
|
|
383
|
-
logger.debug(f"perform_backup({backup_type}) started")
|
|
384
388
|
backup_definitions = []
|
|
389
|
+
results: List[tuple] = []
|
|
385
390
|
|
|
386
391
|
# Gather backup definitions
|
|
387
392
|
if args.backup_definition:
|
|
388
393
|
if '_' in args.backup_definition:
|
|
389
|
-
|
|
390
|
-
|
|
394
|
+
msg = f"Skipping backup definition: '{args.backup_definition}' due to '_' in name"
|
|
395
|
+
logger.error(msg)
|
|
396
|
+
return results.append((msg, 1))
|
|
391
397
|
backup_definitions.append((os.path.basename(args.backup_definition).split('.')[0], os.path.join(config_settings.backup_d_dir, args.backup_definition)))
|
|
392
398
|
else:
|
|
393
399
|
for root, _, files in os.walk(config_settings.backup_d_dir):
|
|
394
400
|
for file in files:
|
|
395
401
|
if '_' in file:
|
|
396
|
-
|
|
402
|
+
msg = f"Skipping backup definition: '{file} due to '_' in: name"
|
|
403
|
+
logger.error(msg)
|
|
404
|
+
results.append((msg, 1))
|
|
397
405
|
continue
|
|
398
406
|
backup_definitions.append((file.split('.')[0], os.path.join(root, file)))
|
|
399
407
|
|
|
@@ -403,7 +411,9 @@ def perform_backup(args: argparse.Namespace, config_settings: ConfigSettings, ba
|
|
|
403
411
|
backup_file = os.path.join(config_settings.backup_dir, f"{backup_definition}_{backup_type}_{date}")
|
|
404
412
|
|
|
405
413
|
if os.path.exists(backup_file + '.1.dar'):
|
|
406
|
-
|
|
414
|
+
msg = f"Backup file {backup_file}.1.dar already exists. Skipping backup [1]."
|
|
415
|
+
logger.error(msg)
|
|
416
|
+
results.append((msg, 1))
|
|
407
417
|
continue
|
|
408
418
|
|
|
409
419
|
latest_base_backup = None
|
|
@@ -414,15 +424,18 @@ def perform_backup(args: argparse.Namespace, config_settings: ConfigSettings, ba
|
|
|
414
424
|
latest_base_backup = os.path.join(config_settings.backup_dir, args.alternate_reference_archive)
|
|
415
425
|
logger.info(f"Using alternate reference archive: {latest_base_backup}")
|
|
416
426
|
if not os.path.exists(latest_base_backup + '.1.dar'):
|
|
417
|
-
|
|
418
|
-
|
|
427
|
+
msg = f"Alternate reference archive: \"{latest_base_backup}.1.dar\" does not exist, exiting..."
|
|
428
|
+
logger.error(msg)
|
|
429
|
+
results.append((msg, 1))
|
|
430
|
+
return results
|
|
419
431
|
else:
|
|
420
432
|
base_backups = sorted(
|
|
421
433
|
[f for f in os.listdir(config_settings.backup_dir) if f.startswith(f"{backup_definition}_{base_backup_type}_") and f.endswith('.1.dar')],
|
|
422
434
|
key=lambda x: datetime.strptime(x.split('_')[-1].split('.')[0], '%Y-%m-%d')
|
|
423
435
|
)
|
|
424
436
|
if not base_backups:
|
|
425
|
-
|
|
437
|
+
msg = f"No {base_backup_type} backup found for {backup_definition}. Skipping {backup_type} backup."
|
|
438
|
+
results.append((msg, 1))
|
|
426
439
|
continue
|
|
427
440
|
latest_base_backup = os.path.join(config_settings.backup_dir, base_backups[-1].rsplit('.', 2)[0])
|
|
428
441
|
|
|
@@ -437,19 +450,19 @@ def perform_backup(args: argparse.Namespace, config_settings: ConfigSettings, ba
|
|
|
437
450
|
if verify_result:
|
|
438
451
|
logger.info("Verification completed successfully.")
|
|
439
452
|
else:
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
453
|
+
msg = f"Verification of '{backup_file}' failed."
|
|
454
|
+
logger.error(msg)
|
|
455
|
+
results.append((msg, 1))
|
|
443
456
|
logger.info("Generate par2 redundancy files.")
|
|
444
457
|
generate_par2_files(backup_file, config_settings, args)
|
|
445
458
|
logger.info("par2 files completed successfully.")
|
|
446
459
|
|
|
447
460
|
except Exception as e:
|
|
448
|
-
|
|
449
|
-
RESULT = False
|
|
461
|
+
results.aapend((repr(e), 1))
|
|
450
462
|
logger.exception(f"Error during {backup_type} backup process, continuing to next backup definition.")
|
|
451
463
|
|
|
452
|
-
|
|
464
|
+
logger.trace(f"perform_backup() results[]: {results}")
|
|
465
|
+
return results
|
|
453
466
|
|
|
454
467
|
def generate_par2_files(backup_file: str, config_settings: ConfigSettings, args):
|
|
455
468
|
"""
|
|
@@ -593,18 +606,23 @@ INCR back of a single backup definition in backup.d
|
|
|
593
606
|
|
|
594
607
|
def requirements(type: str, config_setting: ConfigSettings):
|
|
595
608
|
"""
|
|
596
|
-
Perform PREREQ or POSTREQ
|
|
609
|
+
Perform PREREQ or POSTREQ requirements.
|
|
597
610
|
|
|
598
611
|
Args:
|
|
599
612
|
type (str): The type of prereq (PREREQ, POSTREQ).
|
|
600
613
|
config_settings (ConfigSettings): An instance of the ConfigSettings class.
|
|
601
614
|
|
|
602
615
|
Raises:
|
|
603
|
-
RuntimeError: If a subprocess
|
|
616
|
+
RuntimeError: If a subprocess returns anything but zero.
|
|
617
|
+
|
|
618
|
+
subprocess.CalledProcessError: if CalledProcessError is raised in subprocess.run(), let it bobble up.
|
|
604
619
|
"""
|
|
605
|
-
if
|
|
606
|
-
|
|
607
|
-
|
|
620
|
+
if type is None or config_setting is None:
|
|
621
|
+
raise RuntimeError(f"requirements: 'type' or config_setting is None")
|
|
622
|
+
|
|
623
|
+
allowed_types = ['PREREQ', 'POSTREQ']
|
|
624
|
+
if type not in allowed_types:
|
|
625
|
+
raise RuntimeError(f"requirements: {type} not in: {allowed_types}")
|
|
608
626
|
|
|
609
627
|
|
|
610
628
|
logger.info(f"Performing {type}")
|
|
@@ -624,7 +642,8 @@ def requirements(type: str, config_setting: ConfigSettings):
|
|
|
624
642
|
|
|
625
643
|
|
|
626
644
|
def main():
|
|
627
|
-
global logger
|
|
645
|
+
global logger
|
|
646
|
+
results: List[(str,int)] = [] # a list op tuples (<msg>, <exit code>)
|
|
628
647
|
|
|
629
648
|
MIN_PYTHON_VERSION = (3, 9)
|
|
630
649
|
if version_info < MIN_PYTHON_VERSION:
|
|
@@ -737,26 +756,27 @@ def main():
|
|
|
737
756
|
if args.list:
|
|
738
757
|
list_backups(config_settings.backup_dir, args.backup_definition)
|
|
739
758
|
elif args.full_backup and not args.differential_backup and not args.incremental_backup:
|
|
740
|
-
perform_backup(args, config_settings, "FULL")
|
|
759
|
+
results.extend(perform_backup(args, config_settings, "FULL"))
|
|
741
760
|
elif args.differential_backup and not args.full_backup and not args.incremental_backup:
|
|
742
|
-
perform_backup(args, config_settings, "DIFF")
|
|
761
|
+
results.extend(perform_backup(args, config_settings, "DIFF"))
|
|
743
762
|
elif args.incremental_backup and not args.full_backup and not args.differential_backup:
|
|
744
|
-
perform_backup(args, config_settings, "INCR")
|
|
763
|
+
results.extend(perform_backup(args, config_settings, "INCR"))
|
|
764
|
+
logger.debug(f"results from perform_backup(): {results}")
|
|
745
765
|
elif args.list_contents:
|
|
746
766
|
list_contents(args.list_contents, config_settings.backup_dir, args.selection)
|
|
747
767
|
elif args.restore:
|
|
748
768
|
logger.debug(f"Restoring {args.restore} to {restore_dir}")
|
|
749
|
-
restore_backup(args.restore, config_settings, restore_dir, args.darrc, args.selection)
|
|
769
|
+
results.extend(restore_backup(args.restore, config_settings, restore_dir, args.darrc, args.selection))
|
|
750
770
|
else:
|
|
751
771
|
parser.print_help()
|
|
752
772
|
|
|
773
|
+
logger.debug(f"results[]: {results}")
|
|
774
|
+
|
|
753
775
|
requirements('POSTREQ', config_settings)
|
|
754
776
|
|
|
755
777
|
except Exception as e:
|
|
756
|
-
logger.exception("An error occurred")
|
|
757
778
|
logger.error("Exception details:", exc_info=True)
|
|
758
|
-
|
|
759
|
-
RESULT = False
|
|
779
|
+
results.append((repr(e), 1))
|
|
760
780
|
finally:
|
|
761
781
|
end_time=int(time())
|
|
762
782
|
logger.info(f"END TIME: {end_time}")
|
|
@@ -767,9 +787,26 @@ def main():
|
|
|
767
787
|
logger.info(f"Removed filtered .darrc: {args.darrc}")
|
|
768
788
|
|
|
769
789
|
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
790
|
+
error = False
|
|
791
|
+
logger.debug(f"results[]: {results}")
|
|
792
|
+
if results:
|
|
793
|
+
i = 0
|
|
794
|
+
for result in results:
|
|
795
|
+
if isinstance(result, tuple) and len(result) == 2:
|
|
796
|
+
msg, exit_code = result
|
|
797
|
+
logger.debug(f"exit code: {exit_code}, msg: {msg}")
|
|
798
|
+
if exit_code == 1:
|
|
799
|
+
error = True
|
|
800
|
+
args.verbose and print(msg)
|
|
801
|
+
else:
|
|
802
|
+
logger.error(f"not correct result type: {result}, which must be a tuple (<msg>, <exit_code>)")
|
|
803
|
+
i=i+1
|
|
804
|
+
if error:
|
|
805
|
+
args.verbose and print("\033[1m\033[31mErrors\033[0m encountered")
|
|
773
806
|
exit(1)
|
|
807
|
+
else:
|
|
808
|
+
args.verbose and print("\033[1m\033[32mSuccess\033[0m all backups completed")
|
|
809
|
+
exit(0)
|
|
810
|
+
|
|
774
811
|
if __name__ == "__main__":
|
|
775
812
|
main()
|
dar_backup/manager.py
CHANGED
|
@@ -233,7 +233,7 @@ def add_specific_archive(archive: str, config_settings: ConfigSettings, director
|
|
|
233
233
|
database_path = os.path.realpath(os.path.join(config_settings.backup_dir, database))
|
|
234
234
|
logger.info(f'Add "{archive_path}" to catalog: "{database}"')
|
|
235
235
|
|
|
236
|
-
command = ['dar_manager', '--base', database_path, "--add", archive_path, "-
|
|
236
|
+
command = ['dar_manager', '--base', database_path, "--add", archive_path, "-Q"]
|
|
237
237
|
process = run_command(command)
|
|
238
238
|
stdout, stderr = process.stdout, process.stderr
|
|
239
239
|
|
dar_backup/util.py
CHANGED
|
@@ -195,8 +195,6 @@ def run_command(command: List[str], timeout: int = 30, no_output_log: bool = Fal
|
|
|
195
195
|
stdout = "\n".join(stdout_lines)
|
|
196
196
|
stderr = "\n".join(stderr_lines)
|
|
197
197
|
|
|
198
|
-
print(f"run_command(): stdout: {stdout}")
|
|
199
|
-
|
|
200
198
|
#Build the result object
|
|
201
199
|
result = CommandResult(process=process, stdout=stdout, stderr=stderr, returncode=process.returncode, timeout=timeout, command=command)
|
|
202
200
|
return result
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dar-backup
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.14
|
|
4
4
|
Summary: A script to do full, differential and incremental backups using dar. Some files are restored from the backups during verification, after which par2 redundancy files are created. The script also has a cleanup feature to remove old backups and par2 files.
|
|
5
5
|
Project-URL: Homepage, https://github.com/per2jensen/dar-backup/tree/main/v2
|
|
6
6
|
Project-URL: Changelog, https://github.com/per2jensen/dar-backup/blob/main/v2/Changelog.md
|
|
@@ -1355,6 +1355,8 @@ In order to not clutter that log file with the output of commands being run, a n
|
|
|
1355
1355
|
- FULL, DIFF and INCR backups.
|
|
1356
1356
|
- cleanup.
|
|
1357
1357
|
|
|
1358
|
+
- fix --log-stdout spams console with command output
|
|
1359
|
+
|
|
1358
1360
|
## Reference
|
|
1359
1361
|
|
|
1360
1362
|
### dar-backup
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
dar_backup/.darrc,sha256=-aerqivZmOsW_XBCh9IfbYTUvw0GkzDSr3Vx4GcNB1g,2113
|
|
2
|
-
dar_backup/__about__.py,sha256=
|
|
2
|
+
dar_backup/__about__.py,sha256=gpxZhHkmXQLM1tz9VrFJMN82NzCMmjk_6Z0FrzPb8BE,22
|
|
3
3
|
dar_backup/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
dar_backup/clean_log.py,sha256=cGhtKYnQJ2ceNQfw5XcCln_WNBasbmlfhO3kRydjDNk,5196
|
|
5
5
|
dar_backup/cleanup.py,sha256=1g2si9-jPEL8T4OKaiGSKFGsF6rWh-Ke1-zQHE7HaPc,11703
|
|
6
6
|
dar_backup/config_settings.py,sha256=uicCq6FnpxPFzbv7xfYSXNnQf1tfLk1Z3VIO9M71fsE,4659
|
|
7
7
|
dar_backup/dar-backup.conf,sha256=-wXqP4vj5TS7cCfMJN1nbk-1Sqkq00Tg22ySQXynUF4,902
|
|
8
|
-
dar_backup/dar_backup.py,sha256=
|
|
8
|
+
dar_backup/dar_backup.py,sha256=8RcSVsqRy-BUCjJBroo0oKC8ZabZ5HXZrRFwxZV82b4,37360
|
|
9
9
|
dar_backup/installer.py,sha256=ehp4KSgTc8D9Edsyve5v3NY2MuDbuTFYQQPgou8woV8,4331
|
|
10
|
-
dar_backup/manager.py,sha256=
|
|
11
|
-
dar_backup/util.py,sha256=
|
|
12
|
-
dar_backup-0.6.
|
|
13
|
-
dar_backup-0.6.
|
|
14
|
-
dar_backup-0.6.
|
|
15
|
-
dar_backup-0.6.
|
|
16
|
-
dar_backup-0.6.
|
|
10
|
+
dar_backup/manager.py,sha256=sQl0xdWwBgui11S9Ekg0hOSC4gt89nz_Z8Bt8IPXCDw,21640
|
|
11
|
+
dar_backup/util.py,sha256=lfgfxC_C6x3I8f9vzyxpQE7P-7rm6tTEr3P-2uWlVtQ,12278
|
|
12
|
+
dar_backup-0.6.14.dist-info/METADATA,sha256=P8yWZZJ8WDrSMmN8UCbEoKZU2y76L_-05Y2vPmubNOA,72750
|
|
13
|
+
dar_backup-0.6.14.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
14
|
+
dar_backup-0.6.14.dist-info/entry_points.txt,sha256=Z7P5BUbhtJxo8_nB9qNIMay2eGDbsMKB3Fjwv3GMa4g,202
|
|
15
|
+
dar_backup-0.6.14.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
16
|
+
dar_backup-0.6.14.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|