mirror of
https://github.com/Gu1llaum-3/sshm.git
synced 2025-10-19 01:17:20 +02:00
feat: centralize history storage in config directory
Automatically migrates existing ~/.ssh/sshm_history.json to platform-appropriate config location
This commit is contained in:
parent
aa6be1d92d
commit
8c6f3b01ef
@ -40,8 +40,8 @@ func GetDefaultSSHConfigPath() (string, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSSHMBackupDir returns the SSHM backup directory
|
// GetSSHMConfigDir returns the SSHM config directory
|
||||||
func GetSSHMBackupDir() (string, error) {
|
func GetSSHMConfigDir() (string, error) {
|
||||||
homeDir, err := os.UserHomeDir()
|
homeDir, err := os.UserHomeDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -67,6 +67,15 @@ func GetSSHMBackupDir() (string, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return configDir, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSSHMBackupDir returns the SSHM backup directory
|
||||||
|
func GetSSHMBackupDir() (string, error) {
|
||||||
|
configDir, err := GetSSHMConfigDir()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
return filepath.Join(configDir, "backups"), nil
|
return filepath.Join(configDir, "backups"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,12 +30,23 @@ type HistoryManager struct {
|
|||||||
|
|
||||||
// NewHistoryManager creates a new history manager
|
// NewHistoryManager creates a new history manager
|
||||||
func NewHistoryManager() (*HistoryManager, error) {
|
func NewHistoryManager() (*HistoryManager, error) {
|
||||||
homeDir, err := os.UserHomeDir()
|
configDir, err := config.GetSSHMConfigDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
historyPath := filepath.Join(homeDir, ".ssh", "sshm_history.json")
|
// Ensure config dir exists
|
||||||
|
if err := os.MkdirAll(configDir, 0755); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
historyPath := filepath.Join(configDir, "sshm_history.json")
|
||||||
|
|
||||||
|
// Migration: check if old history file exists and migrate it
|
||||||
|
if err := migrateOldHistoryFile(historyPath); err != nil {
|
||||||
|
// Don't fail if migration fails, just log it
|
||||||
|
// In a production environment, you might want to log this properly
|
||||||
|
}
|
||||||
|
|
||||||
hm := &HistoryManager{
|
hm := &HistoryManager{
|
||||||
historyPath: historyPath,
|
historyPath: historyPath,
|
||||||
@ -54,6 +65,46 @@ func NewHistoryManager() (*HistoryManager, error) {
|
|||||||
return hm, nil
|
return hm, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// migrateOldHistoryFile migrates the old history file from ~/.ssh to ~/.config/sshm
|
||||||
|
// TODO: Remove this migration logic in v2.0.0 (introduced in v1.6.0)
|
||||||
|
func migrateOldHistoryFile(newHistoryPath string) error {
|
||||||
|
// Check if new file already exists, skip migration
|
||||||
|
if _, err := os.Stat(newHistoryPath); err == nil {
|
||||||
|
return nil // New file exists, no migration needed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get old history file path - use same logic as SSH config location
|
||||||
|
sshDir, err := config.GetSSHDirectory()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
oldHistoryPath := filepath.Join(sshDir, "sshm_history.json")
|
||||||
|
|
||||||
|
// Check if old file exists
|
||||||
|
if _, err := os.Stat(oldHistoryPath); os.IsNotExist(err) {
|
||||||
|
return nil // Old file doesn't exist, nothing to migrate
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read old file
|
||||||
|
data, err := os.ReadFile(oldHistoryPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write to new location
|
||||||
|
if err := os.WriteFile(newHistoryPath, data, 0644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove old file only if write was successful
|
||||||
|
if err := os.Remove(oldHistoryPath); err != nil {
|
||||||
|
// Don't fail if we can't remove the old file
|
||||||
|
// The migration was successful even if cleanup failed
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// loadHistory loads the connection history from the JSON file
|
// loadHistory loads the connection history from the JSON file
|
||||||
func (hm *HistoryManager) loadHistory() error {
|
func (hm *HistoryManager) loadHistory() error {
|
||||||
data, err := os.ReadFile(hm.historyPath)
|
data, err := os.ReadFile(hm.historyPath)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package history
|
package history
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@ -94,3 +95,70 @@ func TestHistoryManager_GetConnectionCount(t *testing.T) {
|
|||||||
t.Errorf("Expected connection count 3, got %d", count)
|
t.Errorf("Expected connection count 3, got %d", count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMigrateOldHistoryFile(t *testing.T) {
|
||||||
|
// This test verifies that migration doesn't fail when called
|
||||||
|
// The actual migration logic will be tested in integration tests
|
||||||
|
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
newHistoryPath := filepath.Join(tempDir, "sshm_history.json")
|
||||||
|
|
||||||
|
// Test that migration works when no old file exists (common case)
|
||||||
|
if err := migrateOldHistoryFile(newHistoryPath); err != nil {
|
||||||
|
t.Errorf("migrateOldHistoryFile() with no old file error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that migration skips when new file already exists
|
||||||
|
if err := os.WriteFile(newHistoryPath, []byte(`{"connections":{}}`), 0644); err != nil {
|
||||||
|
t.Fatalf("Failed to write new history file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := migrateOldHistoryFile(newHistoryPath); err != nil {
|
||||||
|
t.Errorf("migrateOldHistoryFile() with existing new file error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// File should be unchanged
|
||||||
|
data, err := os.ReadFile(newHistoryPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed to read new file: %v", err)
|
||||||
|
}
|
||||||
|
if string(data) != `{"connections":{}}` {
|
||||||
|
t.Error("New file was modified when it shouldn't have been")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMigrateOldHistoryFile_NoOldFile(t *testing.T) {
|
||||||
|
// Test migration when no old file exists
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
newHistoryPath := filepath.Join(tempDir, "sshm_history.json")
|
||||||
|
|
||||||
|
// Should not return error when old file doesn't exist
|
||||||
|
if err := migrateOldHistoryFile(newHistoryPath); err != nil {
|
||||||
|
t.Errorf("migrateOldHistoryFile() with no old file error = %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMigrateOldHistoryFile_NewFileExists(t *testing.T) {
|
||||||
|
// Test migration when new file already exists (should skip migration)
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
newHistoryPath := filepath.Join(tempDir, "sshm_history.json")
|
||||||
|
|
||||||
|
// Create new file first
|
||||||
|
if err := os.WriteFile(newHistoryPath, []byte(`{"connections":{}}`), 0644); err != nil {
|
||||||
|
t.Fatalf("Failed to write new history file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migration should skip when new file exists
|
||||||
|
if err := migrateOldHistoryFile(newHistoryPath); err != nil {
|
||||||
|
t.Errorf("migrateOldHistoryFile() with existing new file error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// New file should be unchanged
|
||||||
|
data, err := os.ReadFile(newHistoryPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed to read new file: %v", err)
|
||||||
|
}
|
||||||
|
if string(data) != `{"connections":{}}` {
|
||||||
|
t.Error("New file was modified when it shouldn't have been")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user