1717
1818from autotarcompress .commands .command import Command
1919from autotarcompress .config import BackupConfig
20- from autotarcompress .utils import SizeCalculator
20+ from autotarcompress .utils import (
21+ SizeCalculator ,
22+ ensure_backup_folder ,
23+ validate_and_expand_paths ,
24+ )
2125
2226
2327class BackupCommand (Command ):
@@ -40,15 +44,49 @@ def execute(self) -> bool:
4044 bool: True if backup succeeded, False otherwise.
4145
4246 """
47+ # Validate and ensure backup directories
48+ existing_dirs , missing_dirs = validate_and_expand_paths (
49+ self .config .dirs_to_backup
50+ )
51+ if missing_dirs :
52+ # Log missing directories; no need to print to stdout here.
53+ self .logger .warning (
54+ "Some configured backup directories do not exist: %s" ,
55+ missing_dirs ,
56+ )
57+
58+ # Use equality comparison instead of identity; lists with the same
59+ # contents should be compared by value.
60+ if existing_dirs != self .config .dirs_to_backup :
61+ self .logger .info (
62+ "Proceeding with existing directories only: %s" ,
63+ existing_dirs ,
64+ )
65+ self .config .dirs_to_backup = existing_dirs
66+
67+ # Ensure backup folder exists
68+ try :
69+ backup_folder_path = ensure_backup_folder (
70+ self .config .backup_folder
71+ )
72+ self .config .backup_folder = str (backup_folder_path )
73+ self .logger .info (
74+ "Backup folder ensured at: %s" ,
75+ self .config .backup_folder ,
76+ )
77+ except (OSError , PermissionError ) as e :
78+ self .logger .error ("Failed to ensure backup folder: %s" , e )
79+ return False
80+
4381 if not self .config .dirs_to_backup :
44- self ._print_and_log (
45- "No directories configured for backup. Skipping backup." , level = "error"
82+ self .logger . error (
83+ "No directories configured for backup. Skipping backup."
4684 )
4785 return False
4886 total_size : int = self ._calculate_total_size ()
4987 if total_size == 0 :
50- self ._print_and_log (
51- "Total backup size is 0 bytes. Nothing to back up." , level = "warning"
88+ self .logger . warning (
89+ "Total backup size is 0 bytes. Nothing to back up."
5290 )
5391 return False
5492 success : bool = self ._run_backup_process (total_size )
@@ -63,7 +101,10 @@ def _calculate_total_size(self) -> int:
63101 int: Total size in bytes.
64102
65103 """
66- calculator = SizeCalculator (self .config .dirs_to_backup , self .config .ignore_list )
104+ calculator = SizeCalculator (
105+ self .config .dirs_to_backup ,
106+ self .config .ignore_list ,
107+ )
67108 return calculator .calculate_total_size ()
68109
69110 def _run_backup_process (self , total_size : int ) -> bool :
@@ -77,39 +118,56 @@ def _run_backup_process(self, total_size: int) -> bool:
77118
78119 """
79120 if os .path .exists (self .config .backup_path ):
80- self ._print_and_log (f"File already exists: { self .config .backup_path } " , level = "warning" )
121+ print (f"File already exists: { self .config .backup_path } " )
122+ self .logger .warning (
123+ "File already exists: %s" ,
124+ self .config .backup_path ,
125+ )
81126 if not self ._prompt_overwrite ():
82- self ._print_and_log ("Backup aborted by user due to existing file." , level = "info" )
127+ msg = "Backup aborted by user due to existing file."
128+ self .logger .info ("%s" , msg )
83129 return False
84130 try :
85131 os .remove (self .config .backup_path )
86- self .logger .info ("Removed existing backup file: %s" , self .config .backup_path )
132+ self .logger .info (
133+ "Removed existing backup file: %s" ,
134+ self .config .backup_path ,
135+ )
87136 # NOTE: Broad except is used here to ensure any file removal error
88137 # is caught during backup overwrite prompt. This is a critical IO
89138 # operation.
90139 except (OSError , PermissionError ) as e :
91- self ._print_and_log ( f "Failed to remove existing backup: { e } " , level = "error" )
140+ self .logger . error ( "Failed to remove existing backup: %s " , e )
92141 return False
93142
94143 cmd = self ._build_tar_command ()
95144 total_size_gb = total_size / 1024 ** 3
96145
97- self .logger .info ("Starting backup to %s" , self .config .backup_path )
98- self .logger .info ("Total size: %.2f GB" , total_size_gb )
146+ self .logger .info (
147+ "Starting backup to %s" ,
148+ self .config .backup_path ,
149+ )
150+ self .logger .info (
151+ "Total size: %.2f GB" ,
152+ total_size_gb ,
153+ )
99154
100155 try :
101156 subprocess .run (cmd , shell = True , check = True )
102157 self .logger .info ("Backup completed successfully" )
103- self ._print_and_log ("Backup completed successfully." , level = "info" )
104158 return True
105159 except subprocess .CalledProcessError as e :
106- self ._print_and_log ( f "Backup failed: { e } " , level = "error" )
160+ self .logger . error ( "Backup failed: %s " , e )
107161 return False
108162
109163 def _build_tar_command (self ) -> str :
110164 """Build the tar+xz command string for backup."""
111- exclude_options = " " .join (f"--exclude={ path } " for path in self .config .ignore_list )
112- dir_paths = [os .path .expanduser (path ) for path in self .config .dirs_to_backup ]
165+ exclude_options = " " .join (
166+ f"--exclude={ path } " for path in self .config .ignore_list
167+ )
168+ dir_paths = [
169+ os .path .expanduser (path ) for path in self .config .dirs_to_backup
170+ ]
113171 quoted_paths = [shlex .quote (path ) for path in dir_paths ]
114172 cpu_count = os .cpu_count () or 1
115173 threads = max (1 , cpu_count - 1 )
@@ -124,18 +182,6 @@ def _prompt_overwrite(self) -> bool:
124182 response = input ("Do you want to remove it? (y/n): " ).strip ().lower ()
125183 return response == "y"
126184
127- def _print_and_log (self , message : str , level : str = "info" ) -> None :
128- """Print and log a message at the specified level."""
129- print (message )
130- if level == "info" :
131- self .logger .info (message )
132- elif level == "warning" :
133- self .logger .warning (message )
134- elif level == "error" :
135- self .logger .error (message )
136- else :
137- self .logger .debug (message )
138-
139185 def _save_backup_info (self , total_size : int ) -> None :
140186 """Save backup information to last-backup-info.json."""
141187 try :
@@ -149,7 +195,9 @@ def _save_backup_info(self, total_size: int) -> None:
149195 }
150196
151197 # Save the info file in the backup folder
152- info_file_path = Path (self .config .backup_folder ) / "last-backup-info.json"
198+ info_file_path = (
199+ Path (self .config .backup_folder ) / "last-backup-info.json"
200+ )
153201
154202 with open (info_file_path , "w" , encoding = "utf-8" ) as f :
155203 json .dump (backup_info , f , indent = 2 )
0 commit comments