feat: backup the original file before manage it by lazyssh (#49)

This commit is contained in:
Adem Baccara
2025-09-12 19:32:09 +01:00
committed by GitHub
parent b7fb586e73
commit e75b61e819
4 changed files with 44 additions and 4 deletions
+8
View File
@@ -53,6 +53,14 @@ It is simply a UI/TUI wrapper around your existing `~/.ssh/config` file.
- File permissions on your SSH config are preserved to ensure security.
## 🛡️ Config Safety: Nondestructive writes and backups
- Nondestructive edits: lazyssh only writes the minimal required changes to your ~/.ssh/config. It uses a parser that preserves existing comments, spacing, order, and any settings it didnt touch. Your handcrafted comments and formatting remain intact.
- Atomic writes: updates are written to a temporary file and then atomically renamed over the original, minimizing the risk of partial writes.
- Backups:
- Onetime original backup: before lazyssh makes its first change, it creates a single snapshot named config.original.backup beside your SSH config. If this file is present, it will never be recreated or overwritten.
- Rolling backups: on every subsequent save, lazyssh also creates a timestamped backup named like: ~/.ssh/config-<timestamp>-lazyssh.backup. The app keeps at most 10 of these backups, automatically removing the oldest ones.
## 📷 Screenshots
<div align="center">
@@ -125,3 +125,29 @@ func (r *Repository) findBackupFiles(dir string) ([]os.FileInfo, error) {
return backupFiles, nil
}
// createOriginalBackupIfNeeded creates a one-time original backup of the current SSH config.
func (r *Repository) createOriginalBackupIfNeeded() error {
// If no SSH config file, nothing to do.
if _, err := r.fileSystem.Stat(r.configPath); os.IsNotExist(err) {
return nil
} else if err != nil {
return fmt.Errorf("failed to check if config file exists: %w", err)
}
configDir := filepath.Dir(r.configPath)
originalBackupPath := filepath.Join(configDir, OriginalBackupName)
if _, err := r.fileSystem.Stat(originalBackupPath); err == nil {
return nil
} else if !r.fileSystem.IsNotExist(err) {
return fmt.Errorf("failed to check if original backup exists: %w", err)
}
if err := r.copyFile(r.configPath, originalBackupPath); err != nil {
return fmt.Errorf("failed to create original backup: %w", err)
}
r.logger.Infof("Created original backup: %s", originalBackupPath)
return nil
}
@@ -66,6 +66,11 @@ func (r *Repository) saveConfig(cfg *ssh_config.Config) error {
return fmt.Errorf("failed to write config to temporary file: %w", err)
}
// Ensure a one-time original backup exists before any modifications managed by lazyssh.
if err := r.createOriginalBackupIfNeeded(); err != nil {
return fmt.Errorf("failed to create original backup: %w", err)
}
if err := r.createBackup(); err != nil {
return fmt.Errorf("failed to create backup: %w", err)
}
@@ -23,10 +23,11 @@ import (
)
const (
MaxBackups = 10
TempSuffix = ".tmp"
BackupSuffix = "lazyssh.backup"
SSHConfigPerms = 0o600
MaxBackups = 10
TempSuffix = ".tmp"
BackupSuffix = "lazyssh.backup"
SSHConfigPerms = 0o600
OriginalBackupName = "config.original.backup"
)
// filterServers filters servers based on the query string.