(lines []string)
| 214 | } |
| 215 | |
| 216 | func writeSSHConfigLines(lines []string) error { |
| 217 | configPath := sshConfigPath() |
| 218 | |
| 219 | // Create .ssh directory if it doesn't exist |
| 220 | sshDir := filepath.Dir(configPath) |
| 221 | if err := os.MkdirAll(sshDir, 0700); err != nil { |
| 222 | return fmt.Errorf("failed to create .ssh directory: %w", err) |
| 223 | } |
| 224 | |
| 225 | // Write to temporary file first (atomic write pattern) |
| 226 | tmpPath := configPath + ".tmp" |
| 227 | content := strings.Join(lines, "\n") |
| 228 | if content != "" && !strings.HasSuffix(content, "\n") { |
| 229 | content += "\n" |
| 230 | } |
| 231 | |
| 232 | if err := os.WriteFile(tmpPath, []byte(content), 0600); err != nil { |
| 233 | return fmt.Errorf("failed to write temp file: %w", err) |
| 234 | } |
| 235 | |
| 236 | // Atomic rename |
| 237 | if err := os.Rename(tmpPath, configPath); err != nil { |
| 238 | os.Remove(tmpPath) // Clean up temp file |
| 239 | return fmt.Errorf("failed to update config: %w", err) |
| 240 | } |
| 241 | |
| 242 | return nil |
| 243 | } |
| 244 | |
| 245 | // findHostBlock finds the line range of a host block |
| 246 | // Returns (start, end, found) where end is exclusive |
no test coverage detected