dar-backup 0.6.6__py3-none-any.whl → 0.6.8__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/.darrc CHANGED
@@ -26,19 +26,22 @@ verbose:
26
26
  # -va
27
27
 
28
28
 
29
- extract:
29
+ restore-options:
30
30
  # don't restore File Specific Attributes
31
31
  #--fsa-scope none
32
32
 
33
33
  # ignore owner, useful when used by a non-privileged user
34
34
  --comparison-field=ignore-owner
35
35
 
36
+
37
+
38
+ # Exclude specific file types from compression
39
+ compress-exclusion:
40
+
36
41
  # First setting case insensitive mode on:
37
42
  -an
38
43
  -ag
39
44
 
40
- # Exclude specific file types from compression
41
- compress-exclusion:
42
45
  -Z "*.gz"
43
46
  -Z "*.bz2"
44
47
  -Z "*.xz"
@@ -105,6 +108,5 @@ compress-exclusion:
105
108
  -Z "*.dar"
106
109
 
107
110
  # Now we swap back to case sensitive mode for masks which is the default
108
- # mode:
109
111
  -acase
110
112
 
dar_backup/__about__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.6.6"
1
+ __version__ = "0.6.8"
dar_backup/cleanup.py CHANGED
@@ -22,17 +22,22 @@ import sys
22
22
 
23
23
  from datetime import datetime, timedelta
24
24
  from time import time
25
+ from typing import Dict, List, NamedTuple
25
26
 
26
27
  from . import __about__ as about
27
28
  from dar_backup.config_settings import ConfigSettings
28
29
  from dar_backup.util import extract_error_lines
29
30
  from dar_backup.util import list_backups
31
+ from dar_backup.util import run_command
30
32
  from dar_backup.util import setup_logging
31
33
 
34
+ from dar_backup.util import CommandResult
35
+
36
+
32
37
 
33
38
  logger = None
34
39
 
35
- def delete_old_backups(backup_dir, age, backup_type, backup_definition=None):
40
+ def delete_old_backups(backup_dir, age, backup_type, args, backup_definition=None):
36
41
  """
37
42
  Delete backups older than the specified age in days.
38
43
  Only .dar and .par2 files are considered for deletion.
@@ -46,6 +51,8 @@ def delete_old_backups(backup_dir, age, backup_type, backup_definition=None):
46
51
  now = datetime.now()
47
52
  cutoff_date = now - timedelta(days=age)
48
53
 
54
+ archives_deleted = {}
55
+
49
56
  for filename in sorted(os.listdir(backup_dir)):
50
57
  if not (filename.endswith('.dar') or filename.endswith('.par2')):
51
58
  continue
@@ -64,11 +71,18 @@ def delete_old_backups(backup_dir, age, backup_type, backup_definition=None):
64
71
  try:
65
72
  os.remove(file_path)
66
73
  logger.info(f"Deleted {backup_type} backup: {file_path}")
74
+ archive_name = filename.split('.')[0]
75
+ if not archive_name in archives_deleted:
76
+ logger.debug(f"Archive name: '{archive_name}' added to catalog deletion list")
77
+ archives_deleted[archive_name] = True
67
78
  except Exception as e:
68
79
  logger.error(f"Error deleting file {file_path}: {e}")
69
80
 
81
+ for archive_name in archives_deleted.keys():
82
+ delete_catalog(archive_name, args)
83
+
70
84
 
71
- def delete_archive(backup_dir, archive_name):
85
+ def delete_archive(backup_dir, archive_name, args):
72
86
  """
73
87
  Delete all .dar and .par2 files in the backup directory for the given archive name.
74
88
 
@@ -90,7 +104,9 @@ def delete_archive(backup_dir, archive_name):
90
104
  except Exception as e:
91
105
  logger.error(f"Error deleting archive slice {file_path}: {e}")
92
106
 
93
- if not files_deleted:
107
+ if files_deleted:
108
+ delete_catalog(archive_name, args)
109
+ else:
94
110
  logger.info("No .dar files matched the regex for deletion.")
95
111
 
96
112
  # Delete associated .par2 files
@@ -110,6 +126,29 @@ def delete_archive(backup_dir, archive_name):
110
126
  logger.info("No .par2 matched the regex for deletion.")
111
127
 
112
128
 
129
+ def delete_catalog(catalog_name: str, args: NamedTuple) -> bool:
130
+ """
131
+ Call `manager.py` to delete the specified catalog in it's database
132
+ """
133
+ command = [f"manager", "--remove-specific-archive", catalog_name, "--config-file", args.config_file, '--log-level', 'debug', '--log-stdout']
134
+ logger.info(f"Deleting catalog '{catalog_name}' using config file: '{args.config_file}'")
135
+ try:
136
+ result:CommandResult = run_command(command)
137
+ if result.returncode == 0:
138
+ logger.info(f"Deleted catalog '{catalog_name}', using config file: '{args.config_file}'")
139
+ logger.debug(f"Stdout: manager.py --remove-specific-archive output:\n{result.stdout}")
140
+ return True
141
+ elif result.returncode == 2:
142
+ logger.warning(f"catalog '{catalog_name}' not found in the database, skipping deletion")
143
+ return True
144
+ else:
145
+ logger.error(f"Error deleting catalog {catalog_name}: {result.stderr}")
146
+ return False
147
+ except Exception as e:
148
+ logger.error(f"Error deleting catalog {catalog_name}: {e}")
149
+ return False
150
+
151
+
113
152
  def show_version():
114
153
  script_name = os.path.basename(sys.argv[0])
115
154
  print(f"{script_name} {about.__version__}")
@@ -118,8 +157,6 @@ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW,
118
157
  See section 15 and section 16 in the supplied "LICENSE" file.''')
119
158
 
120
159
  def main():
121
-
122
-
123
160
  global logger
124
161
 
125
162
  parser = argparse.ArgumentParser(description="Cleanup old archives according to AGE configuration.")
@@ -127,9 +164,11 @@ def main():
127
164
  parser.add_argument('-c', '--config-file', '-c', type=str, help="Path to 'dar-backup.conf'", default='~/.config/dar-backup/dar-backup.conf')
128
165
  parser.add_argument('-v', '--version', action='store_true', help="Show version information.")
129
166
  parser.add_argument('--alternate-archive-dir', type=str, help="Cleanup in this directory instead of the default one.")
130
- parser.add_argument('--cleanup-specific-archive', type=str, help="List of archives to cleanup")
167
+ parser.add_argument('--cleanup-specific-archives', type=str, help="Commas separated list of archives to cleanup")
131
168
  parser.add_argument('-l', '--list', action='store_true', help="List available archives.")
132
169
  parser.add_argument('--verbose', action='store_true', help="Print various status messages to screen")
170
+ parser.add_argument('--log-level', type=str, help="`debug` or `trace`, default is `info`", default="info")
171
+ parser.add_argument('--log-stdout', action='store_true', help='also print log messages to stdout')
133
172
  args = parser.parse_args()
134
173
 
135
174
  args.config_file = os.path.expanduser(args.config_file)
@@ -142,7 +181,8 @@ def main():
142
181
  config_settings = ConfigSettings(args.config_file)
143
182
 
144
183
  start_time=int(time())
145
- logger = setup_logging(config_settings.logfile_location, logging.INFO)
184
+ logger = setup_logging(config_settings.logfile_location, args.log_level, args.log_stdout)
185
+
146
186
  logger.info(f"=====================================")
147
187
  logger.info(f"cleanup.py started, version: {about.__version__}")
148
188
 
@@ -156,7 +196,7 @@ def main():
156
196
  args.verbose and (print(f"Backup dir: {config_settings.backup_dir}"))
157
197
  args.verbose and (print(f"Logfile location: {config_settings.logfile_location}"))
158
198
  args.verbose and (print(f"--alternate-archive-dir: {args.alternate_archive_dir}"))
159
- args.verbose and (print(f"--cleanup-specific-archive: {args.cleanup_specific_archive}"))
199
+ args.verbose and (print(f"--cleanup-specific-archives:{args.cleanup_specific_archives}"))
160
200
 
161
201
  # run PREREQ scripts
162
202
  if 'PREREQ' in config_settings.config:
@@ -175,15 +215,21 @@ def main():
175
215
 
176
216
 
177
217
  if args.alternate_archive_dir:
218
+ if not os.path.exists(args.alternate_archive_dir):
219
+ logger.error(f"Alternate archive directory does not exist: {args.alternate_archive_dir}, exiting")
220
+ sys.exit(1)
221
+ if not os.path.isdir(args.alternate_archive_dir):
222
+ logger.error(f"Alternate archive directory is not a directory, exiting")
223
+ sys.exit(1)
178
224
  config_settings.backup_dir = args.alternate_archive_dir
179
225
 
180
226
 
181
- if args.cleanup_specific_archive:
182
- print(f"Cleaning up specific archives: {args.cleanup_specific_archive}")
183
- archive_names = args.cleanup_specific_archive.split(',')
227
+ if args.cleanup_specific_archives:
228
+ logger.info(f"Cleaning up specific archives: {args.cleanup_specific_archives}")
229
+ archive_names = args.cleanup_specific_archives.split(',')
184
230
  for archive_name in archive_names:
185
- print(f"Deleting archive: {archive_name}")
186
- delete_archive(config_settings.backup_dir, archive_name.strip())
231
+ logger.info(f"Deleting archive: {archive_name}")
232
+ delete_archive(config_settings.backup_dir, archive_name.strip(), args)
187
233
  elif args.list:
188
234
  list_backups(config_settings.backup_dir, args.backup_definition)
189
235
  else:
@@ -196,22 +242,22 @@ def main():
196
242
  backup_definitions.append(file.split('.')[0])
197
243
 
198
244
  for definition in backup_definitions:
199
- delete_old_backups(config_settings.backup_dir, config_settings.diff_age, 'DIFF', definition)
200
- delete_old_backups(config_settings.backup_dir, config_settings.incr_age, 'INCR', definition)
245
+ delete_old_backups(config_settings.backup_dir, config_settings.diff_age, 'DIFF', args, definition)
246
+ delete_old_backups(config_settings.backup_dir, config_settings.incr_age, 'INCR', args, definition)
201
247
 
202
248
 
203
249
  end_time=int(time())
204
250
  logger.info(f"END TIME: {end_time}")
205
251
 
206
- error_lines = extract_error_lines(config_settings.logfile_location, start_time, end_time)
207
- if len(error_lines) > 0:
208
- args.verbose and print("\033[1m\033[31mErrors\033[0m encountered")
209
- for line in error_lines:
210
- args.verbose and print(line)
211
- sys.exit(1)
212
- else:
213
- args.verbose and print("\033[1m\033[32mSUCCESS\033[0m No errors encountered")
214
- sys.exit(0)
252
+ # error_lines = extract_error_lines(config_settings.logfile_location, start_time, end_time)
253
+ # if len(error_lines) > 0:
254
+ # args.verbose and print("\033[1m\033[31mErrors\033[0m encountered")
255
+ # for line in error_lines:
256
+ # args.verbose and print(line)
257
+ # sys.exit(1)
258
+ # else:
259
+ # args.verbose and print("\033[1m\033[32mSUCCESS\033[0m No errors encountered")
260
+ # sys.exit(0)
215
261
 
216
262
  if __name__ == "__main__":
217
263
  main()
dar_backup/dar_backup.py CHANGED
@@ -66,7 +66,7 @@ def generic_backup(type: str, command: List[str], backup_file: str, backup_defin
66
66
  raise Exception(str(process))
67
67
 
68
68
  if process.returncode == 0 or process.returncode == 5:
69
- add_catalog_command = ['manager', '--add-specific-archive' ,backup_file, '--config-file', args.config_file, '--log-level', "debug", "--log-stdout"]
69
+ add_catalog_command = ['manager', '--add-specific-archive' ,backup_file, '--config-file', args.config_file]
70
70
  command_result = run_command(add_catalog_command, config_settings.command_timeout_secs)
71
71
  if command_result.returncode == 0:
72
72
  logger.info(f"Catalog for archive '{backup_file}' added successfully to its manager.")
@@ -209,7 +209,7 @@ def verify(args: argparse.Namespace, backup_file: str, backup_definition: str, c
209
209
  for restored_file_path in random_files:
210
210
  try:
211
211
  logger.info(f"Restoring file: '{restored_file_path}' from backup to: '{config_settings.test_restore_dir}' for file comparing")
212
- command = ['dar', '-x', backup_file, '-g', restored_file_path.lstrip("/"), '-R', config_settings.test_restore_dir, '-O', '-Q']
212
+ command = ['dar', '-x', backup_file, '-g', restored_file_path.lstrip("/"), '-R', config_settings.test_restore_dir, '-Q', '-B', args.darrc, 'restore-options']
213
213
  logger.info(f"Running command: {' '.join(map(shlex.quote, command))}")
214
214
  process = run_command(command, config_settings.command_timeout_secs)
215
215
  if process.returncode != 0:
@@ -228,7 +228,7 @@ def verify(args: argparse.Namespace, backup_file: str, backup_definition: str, c
228
228
 
229
229
 
230
230
 
231
- def restore_backup(backup_name: str, config_settings: ConfigSettings, restore_dir: str, selection: str =None):
231
+ def restore_backup(backup_name: str, config_settings: ConfigSettings, restore_dir: str, darrc: str, selection: str =None):
232
232
  """
233
233
  Restores a backup file to a specified directory.
234
234
 
@@ -239,7 +239,7 @@ def restore_backup(backup_name: str, config_settings: ConfigSettings, restore_di
239
239
  selection (str, optional): A selection criteria to restore specific files or directories. Defaults to None.
240
240
  """
241
241
  backup_file = os.path.join(config_settings.backup_dir, backup_name)
242
- command = ['dar', '-x', backup_file, '-O', '-Q', '-D']
242
+ command = ['dar', '-x', backup_file, '-Q', '-D']
243
243
  if restore_dir:
244
244
  if not os.path.exists(restore_dir):
245
245
  os.makedirs(restore_dir)
@@ -249,6 +249,7 @@ def restore_backup(backup_name: str, config_settings: ConfigSettings, restore_di
249
249
  if selection:
250
250
  selection_criteria = shlex.split(selection)
251
251
  command.extend(selection_criteria)
252
+ command.extend(['-B', darrc, 'restore-options']) # the .darrc `restore-options` section
252
253
  logger.info(f"Running restore command: {' '.join(map(shlex.quote, command))}")
253
254
  try:
254
255
  process = run_command(command, config_settings.command_timeout_secs)
@@ -428,10 +429,9 @@ def perform_backup(args: argparse.Namespace, config_settings: ConfigSettings, ba
428
429
  logger.error("Verification failed.")
429
430
 
430
431
 
431
- if verify_result and config_settings.par2_enabled:
432
- logger.info("Generate par2 redundancy files.")
433
- generate_par2_files(backup_file, config_settings, args)
434
- logger.info("par2 files completed successfully.")
432
+ logger.info("Generate par2 redundancy files.")
433
+ generate_par2_files(backup_file, config_settings, args)
434
+ logger.info("par2 files completed successfully.")
435
435
 
436
436
 
437
437
  except Exception as e:
@@ -677,7 +677,7 @@ def main():
677
677
  list_contents(args.list_contents, config_settings.backup_dir, args.selection)
678
678
  elif args.restore:
679
679
  logger.debug(f"Restoring {args.restore} to {restore_dir}")
680
- restore_backup(args.restore, config_settings, restore_dir, args.selection)
680
+ restore_backup(args.restore, config_settings, restore_dir, args.darrc, args.selection)
681
681
  else:
682
682
  parser.print_help()
683
683
 
dar_backup/manager.py CHANGED
@@ -31,6 +31,8 @@ from . import __about__ as about
31
31
  from dar_backup.config_settings import ConfigSettings
32
32
  from dar_backup.util import run_command
33
33
  from dar_backup.util import setup_logging
34
+ from dar_backup.util import CommandResult
35
+
34
36
  from datetime import datetime
35
37
  from time import time
36
38
  from typing import Dict, List, NamedTuple
@@ -91,8 +93,16 @@ def list_catalogs(backup_def: str, config_settings: ConfigSettings) -> NamedTupl
91
93
  database = f"{backup_def}{DB_SUFFIX}"
92
94
  database_path = os.path.join(config_settings.backup_dir, database)
93
95
  if not os.path.exists(database_path):
94
- logger.error(f'Database not found: "{database_path}"')
95
- return 1
96
+ error_msg = f'Database not found: "{database_path}"'
97
+ logger.error(error_msg)
98
+ commandResult = CommandResult(
99
+ process=None,
100
+ stdout='',
101
+ stderr=error_msg,
102
+ returncode=1,
103
+ timeout=1,
104
+ command=[])
105
+ return commandResult
96
106
  command = ['dar_manager', '--base', database_path, '--list']
97
107
  process = run_command(command)
98
108
  stdout, stderr = process.stdout, process.stderr
@@ -131,16 +141,41 @@ def cat_no_for_name(archive: str, config_settings: ConfigSettings) -> int:
131
141
 
132
142
 
133
143
 
144
+ def list_archive_contents(archive: str, config_settings: ConfigSettings) -> int :
145
+ """
146
+ List the contents of a specific archive, given the archive name
147
+ """
148
+ backup_def = backup_def_from_archive(archive)
149
+ database = f"{backup_def}{DB_SUFFIX}"
150
+ database_path = os.path.join(config_settings.backup_dir, database)
151
+ if not os.path.exists(database_path):
152
+ logger.error(f'Database not found: "{database_path}"')
153
+ return 1
154
+ cat_no = cat_no_for_name(archive, config_settings)
155
+ if cat_no < 0:
156
+ logger.error(f"archive: '{archive}' not found in database: '{database_path}'")
157
+ return 1
158
+ command = ['dar_manager', '--base', database_path, '-u', f"{cat_no}"]
159
+ process = run_command(command)
160
+ stdout, stderr = process.stdout, process.stderr
161
+ if process.returncode != 0:
162
+ logger.error(f'Error listing catalogs for: "{database_path}"')
163
+ logger.error(f"stderr: {stderr}")
164
+ logger.error(f"stdout: {stdout}")
165
+ else:
166
+ print(stdout)
167
+ return process.returncode
168
+
134
169
 
135
170
 
136
- def list_catalog_contents(catalog_number: int, backup_def: str, config_settings: ConfigSettings):
171
+ def list_catalog_contents(catalog_number: int, backup_def: str, config_settings: ConfigSettings) -> int:
137
172
  """
138
173
  List the contents of catalog # in catalog database for given backup definition
139
174
  """
140
175
  database = f"{backup_def}{DB_SUFFIX}"
141
176
  database_path = os.path.join(config_settings.backup_dir, database)
142
177
  if not os.path.exists(database_path):
143
- logger.error(f'Database not found: "{database_path}"')
178
+ logger.error(f'Catalog database not found: "{database_path}"')
144
179
  return 1
145
180
  command = ['dar_manager', '--base', database_path, '-u', f"{catalog_number}"]
146
181
  process = run_command(command)
@@ -278,32 +313,44 @@ def backup_def_from_archive(archive: str) -> str:
278
313
  """
279
314
  return the backup definition from archive name
280
315
  """
316
+ logger.debug(f"Get backup definition from archive: '{archive}'")
281
317
  search = re.search("(.*?)_", archive)
282
- backup_def = search.group(1)
283
- logger.debug(f"backup definition: '{backup_def}' from given archive '{archive}'")
284
- return backup_def
318
+ if search:
319
+ backup_def = search.group(1)
320
+ logger.debug(f"backup definition: '{backup_def}' from given archive '{archive}'")
321
+ return backup_def
322
+ logger.error(f"Could not find backup definition from archive name: '{archive}'")
323
+ return None
285
324
 
286
325
 
287
326
 
288
327
  def remove_specific_archive(archive: str, config_settings: ConfigSettings) -> int:
328
+ """
329
+
330
+ Returns:
331
+ - 0 if the archive was removed from it's catalog
332
+ - 1 if there was an error removing the archive
333
+ - 2 if the archive was not found in the catalog
334
+
335
+ """
289
336
  backup_def = backup_def_from_archive(archive)
290
337
  database_path = os.path.join(config_settings.backup_dir, f"{backup_def}{DB_SUFFIX}")
291
338
  cat_no = cat_no_for_name(archive, config_settings)
292
339
  if cat_no >= 0:
293
340
  command = ['dar_manager', '--base', database_path, "--delete", str(cat_no)]
294
- process = run_command(command)
341
+ process: CommandResult = run_command(command)
342
+ logger.info(f"CommandResult: {process}")
295
343
  else:
296
- logger.error(f"archive: '{archive}' not found in in't catalog database: {database_path}")
297
- return cat_no
344
+ logger.warning(f"archive: '{archive}' not found in it's catalog database: {database_path}")
345
+ return 2
298
346
 
299
347
  if process.returncode == 0:
300
348
  logger.info(f"'{archive}' removed from it's catalog")
349
+ return 0
301
350
  else:
302
351
  logger.error(process.stdout)
303
352
  logger.error(process.sterr)
304
-
305
- return process.returncode
306
-
353
+ return 1
307
354
 
308
355
 
309
356
 
@@ -323,8 +370,9 @@ def main():
323
370
  parser.add_argument('-d', '--backup-def', type=str, help='Restrict to work only on this backup definition')
324
371
  parser.add_argument('--add-specific-archive', type=str, help='Add this archive to catalog database')
325
372
  parser.add_argument('--remove-specific-archive', type=str, help='Remove this archive from catalog database')
326
- parser.add_argument('--list-catalog', action='store_true', help='List catalogs in databases for all backup definitions')
373
+ parser.add_argument('-l', '--list-catalogs', action='store_true', help='List catalogs in databases for all backup definitions')
327
374
  parser.add_argument('--list-catalog-contents', type=int, help="List contents of a catalog. Argument is the 'archive #', '-d <definition>' argument is also required")
375
+ parser.add_argument('--list-archive-contents', type=str, help="List contents of the archive's catalog.")
328
376
  parser.add_argument('--find-file', type=str, help="List catalogs containing <path>/file. '-d <definition>' argument is also required")
329
377
  parser.add_argument('--verbose', action='store_true', help='Be more verbose')
330
378
  parser.add_argument('--log-level', type=str, help="`debug` or `trace`, default is `info`", default="info")
@@ -393,6 +441,11 @@ See section 15 and section 16 in the supplied "LICENSE" file.''')
393
441
  sys.exit(1)
394
442
 
395
443
 
444
+ if args.list_archive_contents and not args.list_archive_contents.strip():
445
+ logger.error(f"--list-archive-contents <param> not given, exiting")
446
+ sys.exit(1)
447
+
448
+
396
449
  if args.list_catalog_contents and not args.backup_def:
397
450
  logger.error(f"--list-catalog-contents requires the --backup-def, exiting")
398
451
  sys.exit(1)
@@ -431,15 +484,11 @@ See section 15 and section 16 in the supplied "LICENSE" file.''')
431
484
 
432
485
 
433
486
  if args.remove_specific_archive:
434
-
435
- if remove_specific_archive(args.remove_specific_archive, config_settings) == 0:
436
- sys.exit(0)
437
- else:
438
- sys.exit(1)
487
+ return remove_specific_archive(args.remove_specific_archive, config_settings)
439
488
 
440
489
 
441
490
 
442
- if args.list_catalog:
491
+ if args.list_catalogs:
443
492
  if args.backup_def:
444
493
  process = list_catalogs(args.backup_def, config_settings)
445
494
  result = process.returncode
@@ -452,6 +501,11 @@ See section 15 and section 16 in the supplied "LICENSE" file.''')
452
501
  result = 1
453
502
  sys.exit(result)
454
503
 
504
+
505
+ if args.list_archive_contents:
506
+ result = list_archive_contents(args.list_archive_contents, config_settings)
507
+ sys.exit(result)
508
+
455
509
  if args.list_catalog_contents:
456
510
  result = list_catalog_contents(args.list_catalog_contents, args.backup_def, config_settings)
457
511
  sys.exit(result)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dar-backup
3
- Version: 0.6.6
3
+ Version: 0.6.8
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
@@ -681,7 +681,7 @@ License: GNU GENERAL PUBLIC LICENSE
681
681
  Public License instead of this License. But first, please read
682
682
  <https://www.gnu.org/licenses/why-not-lgpl.html>.
683
683
  License-File: LICENSE
684
- Classifier: Development Status :: 3 - Alpha
684
+ Classifier: Development Status :: 4 - Beta
685
685
  Classifier: Intended Audience :: End Users/Desktop
686
686
  Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
687
687
  Classifier: Operating System :: POSIX :: Linux
@@ -718,9 +718,13 @@ Description-Content-Type: text/markdown
718
718
  These scripts are licensed under the GPLv3 license.
719
719
  Read more here: https://www.gnu.org/licenses/gpl-3.0.en.html, or have a look at the ["LICENSE"](https://github.com/per2jensen/dar-backup/blob/main/LICENSE) file in this repository.
720
720
 
721
+
721
722
  # Status
722
723
  As of August 8, 2024 I am using the alpha versions of `dar-backup` (alpha-0.5.9 onwards) in my automated backup routine.
723
724
 
725
+ As of February 13, 2025, I have changed the status from alpha --> beta, as the featureset is in place and the alphas have worked well for a very long time.
726
+
727
+
724
728
  **Breaking change in version 0.6.0**
725
729
 
726
730
  Version 0.6.0 and forwards requires the config variable *COMMAND_TIMEOUT_SECS* in the config file.
@@ -931,8 +935,12 @@ in place in BACKUP.D_DIR (see config file)
931
935
  ````
932
936
  dar-backup --full-backup
933
937
  ````
938
+ If you want to see dar-backup's log entries in the terminal, use the `--log-stdout` option. This is also useful if dar-backup is started by systemd.
939
+
940
+ If you want more log messages, use the `--log-level debug` option.
941
+
934
942
 
935
- or a backup of a single definition. The definition's name is the filename of the definition in the `backup.d` config directory.
943
+ If you want a backup of a single definition, use the `-d <backup definition>` option. The definition's name is the filename of the definition in the `backup.d` config directory.
936
944
  ````
937
945
  dar-backup --full-backup -d <your backup definition>
938
946
  ````
@@ -0,0 +1,13 @@
1
+ dar_backup/.darrc,sha256=-aerqivZmOsW_XBCh9IfbYTUvw0GkzDSr3Vx4GcNB1g,2113
2
+ dar_backup/__about__.py,sha256=qbWTdDuFyvScwNj95KArnOcQph9tiLZZ8rgNJYlJ4AE,21
3
+ dar_backup/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ dar_backup/cleanup.py,sha256=9yEdRR84XPtEvBGc2QfwGBQl2tdTPttjetHeiSc_TsM,11419
5
+ dar_backup/config_settings.py,sha256=CBMUhLOOZ-x7CRdS3vBDk4TYaGqC4N1Ot8IMH-qPaI0,3617
6
+ dar_backup/dar_backup.py,sha256=VbpyiCnoVvJuMWS7LO9wo8WIcDegCPVjb5Q7xN9J9Gg,32731
7
+ dar_backup/manager.py,sha256=HDa8eYF89QFhlBRR4EWRzzmswOW00S_w8ToZ5SARO_o,21359
8
+ dar_backup/util.py,sha256=SSSJYM9lQZfubhTUBlX1xDGWmCpYEF3ePARmlY544xM,11283
9
+ dar_backup-0.6.8.dist-info/METADATA,sha256=U3iy7Gzz-D6p5UePy0VLzX2lNbELs0teKBEZXr-sSqc,64610
10
+ dar_backup-0.6.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
11
+ dar_backup-0.6.8.dist-info/entry_points.txt,sha256=x9vnW-JEl8mpDJC69f_XBcn0mBSkV1U0cyvFV-NAP1g,126
12
+ dar_backup-0.6.8.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
13
+ dar_backup-0.6.8.dist-info/RECORD,,
@@ -1,13 +0,0 @@
1
- dar_backup/.darrc,sha256=3d9opAnnZGU9XLyQpTDsLtgo6hqsvZ3JU-yMLz-7_f0,2110
2
- dar_backup/__about__.py,sha256=GTf8rijLTDSqTWQgxKQN312t-j2E-t3ioZB4U22DXxc,21
3
- dar_backup/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- dar_backup/cleanup.py,sha256=DgmxSUwKrLLIQuYSIY_yTRhIuOMgI6ivjlQuH4u3wX4,9057
5
- dar_backup/config_settings.py,sha256=CBMUhLOOZ-x7CRdS3vBDk4TYaGqC4N1Ot8IMH-qPaI0,3617
6
- dar_backup/dar_backup.py,sha256=oUlGCLeYwkJKSqn1qzKqkhpoQVTp-fCWyJcpmkSnLjc,32703
7
- dar_backup/manager.py,sha256=7bo64O4Pk5mLBp5NaID7YQ9GOuLunl5R42z_SUORW-Q,19215
8
- dar_backup/util.py,sha256=SSSJYM9lQZfubhTUBlX1xDGWmCpYEF3ePARmlY544xM,11283
9
- dar_backup-0.6.6.dist-info/METADATA,sha256=NklByKxLaD_ATU-XniHmM_EaOiBc5Gen93QkqVO2jfY,64184
10
- dar_backup-0.6.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
11
- dar_backup-0.6.6.dist-info/entry_points.txt,sha256=x9vnW-JEl8mpDJC69f_XBcn0mBSkV1U0cyvFV-NAP1g,126
12
- dar_backup-0.6.6.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
13
- dar_backup-0.6.6.dist-info/RECORD,,