diff --git a/internal/dotfiles/dotfiles.go b/internal/dotfiles/dotfiles.go index 9d929af..5ff5a0f 100644 --- a/internal/dotfiles/dotfiles.go +++ b/internal/dotfiles/dotfiles.go @@ -315,7 +315,8 @@ func backupConflicts(pkgDir, targetDir string) ([][2]string, error) { // Target doesn't exist or is already a symlink — no conflict. return nil } - if info.IsDir() { + if !info.Mode().IsRegular() { + // Skip non-regular files (directories, sockets, named pipes, devices, etc.). return nil } diff --git a/internal/dotfiles/dotfiles_test.go b/internal/dotfiles/dotfiles_test.go index d58d9c5..4a6ad6f 100644 --- a/internal/dotfiles/dotfiles_test.go +++ b/internal/dotfiles/dotfiles_test.go @@ -1,6 +1,7 @@ package dotfiles import ( + "net" "os" "os/exec" "path/filepath" @@ -564,6 +565,32 @@ func TestBackupConflicts_SkipsSymlinks(t *testing.T) { assert.Len(t, backed, 0) } +func TestBackupConflicts_SkipsSocketFiles(t *testing.T) { + // Use os.MkdirTemp with an empty dir so the path stays short. + // macOS enforces a 104-byte limit on Unix socket paths; t.TempDir() embeds + // the full test name and can exceed that limit, causing net.Listen to fail. + tmpDir, err := os.MkdirTemp("", "ob") + require.NoError(t, err) + t.Cleanup(func() { os.RemoveAll(tmpDir) }) + + pkgDir := filepath.Join(tmpDir, "pkg") + require.NoError(t, os.MkdirAll(pkgDir, 0755)) + require.NoError(t, os.WriteFile(filepath.Join(pkgDir, "s.sock"), []byte("new"), 0644)) + + targetDir := filepath.Join(tmpDir, "home") + require.NoError(t, os.MkdirAll(targetDir, 0755)) + + // Create a Unix domain socket at the conflict path. + socketPath := filepath.Join(targetDir, "s.sock") + ln, err := net.Listen("unix", socketPath) + require.NoError(t, err) + defer ln.Close() + + backed, err := backupConflicts(pkgDir, targetDir) + require.NoError(t, err) + assert.Len(t, backed, 0, "socket files must not be backed up") +} + func TestBackupConflicts_SkipsMissingTargets(t *testing.T) { tmpDir := t.TempDir()