From 98c1c593cf130b8b90ae922d6b8f55bbdb264180 Mon Sep 17 00:00:00 2001 From: Adem Baccara <71262172+Adembc@users.noreply.github.com> Date: Fri, 12 Sep 2025 19:21:04 +0100 Subject: [PATCH] feat: backup the original file before manage it by lazyssh --- README.md | 8 ++++++ .../adapters/data/ssh_config_file/backup.go | 26 +++++++++++++++++++ .../data/ssh_config_file/config_io.go | 5 ++++ 3 files changed, 39 insertions(+) diff --git a/README.md b/README.md index 3517768..3394aab 100644 --- a/README.md +++ b/README.md @@ -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: Non‑destructive writes and backups + +- Non‑destructive 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 didn’t 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: + - One‑time 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--lazyssh.backup. The app keeps at most 10 of these backups, automatically removing the oldest ones. + ## 📷 Screenshots
diff --git a/internal/adapters/data/ssh_config_file/backup.go b/internal/adapters/data/ssh_config_file/backup.go index 20357e4..3e6b71a 100644 --- a/internal/adapters/data/ssh_config_file/backup.go +++ b/internal/adapters/data/ssh_config_file/backup.go @@ -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, "config.original.backup") + + 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 +} diff --git a/internal/adapters/data/ssh_config_file/config_io.go b/internal/adapters/data/ssh_config_file/config_io.go index b4a820b..b5a5da1 100644 --- a/internal/adapters/data/ssh_config_file/config_io.go +++ b/internal/adapters/data/ssh_config_file/config_io.go @@ -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) }