diff --git a/cmd/main.go b/cmd/main.go index ee9cf15..850ba69 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -16,6 +16,7 @@ package main import ( "fmt" + "io" "os" "path/filepath" @@ -28,44 +29,89 @@ import ( ) var ( - version = "develop" - gitCommit = "unknown" -) + version = "develop" + gitCommit = "unknown" + sshConfigFile string -func main() { - log, err := logger.New("LAZYSSH") - if err != nil { - fmt.Println(err) - os.Exit(1) - } + rootCmd = &cobra.Command{ + Use: ui.AppName, + Short: "Lazy SSH server picker TUI", + RunE: func(cmd *cobra.Command, args []string) error { + log, err := logger.New("LAZYSSH") + if err != nil { + fmt.Println(err) + os.Exit(1) + } - //nolint:errcheck // log.Sync may return an error which is safe to ignore here - defer log.Sync() + //nolint:errcheck // log.Sync may return an error which is safe to ignore here + defer log.Sync() - home, err := os.UserHomeDir() - if err != nil { - log.Errorw("failed to get user home directory", "error", err) - //nolint:gocritic // exitAfterDefer: ensure immediate exit on unrecoverable error - os.Exit(1) - } - sshConfigFile := filepath.Join(home, ".ssh", "config") - metaDataFile := filepath.Join(home, ".lazyssh", "metadata.json") + home, err := os.UserHomeDir() + if err != nil { + log.Errorw("failed to get user home directory", "error", err) + // nolint:gocritic // exitAfterDefer: ensure immediate exit on unrecoverable error + os.Exit(1) + } - serverRepo := ssh_config_file.NewRepository(log, sshConfigFile, metaDataFile) - serverService := services.NewServerService(log, serverRepo) - tui := ui.NewTUI(log, serverService, version, gitCommit) + if sshConfigFile == "" { + sshConfigFile = filepath.Join(home, ".ssh", "config") + } else { + stat, err := os.Stat(sshConfigFile) + if err != nil { + fmt.Fprintf(os.Stderr, "Error getting file info: %v\n", err) + os.Exit(1) + } + if stat.Mode()&os.ModeType != 0 { + f, err := os.CreateTemp("", "tmpfile-") + if err != nil { + log.Fatal(err) + } + + // close and remove the temporary file at the end of the program + defer f.Close() + defer os.Remove(f.Name()) + + // write data to the temporary file + fd, err := os.Open(sshConfigFile) + if err != nil { + fmt.Fprintf(os.Stderr, "Error opening file: %v\n", err) + os.Exit(1) + } + defer fd.Close() + + // Read the entire contents at once + content, err := io.ReadAll(fd) + if err != nil { + fmt.Fprintf(os.Stderr, "Error reading file: %v\n", err) + os.Exit(1) + } + if _, err := f.WriteString(string(content)); err != nil { + log.Fatal(err) + } + + sshConfigFile = f.Name() + } + } + + metaDataFile := filepath.Join(home, ".lazyssh", "metadata.json") + serverRepo := ssh_config_file.NewRepository(log, sshConfigFile, metaDataFile) + serverService := services.NewServerService(log, serverRepo) + tui := ui.NewTUI(log, serverService, version, gitCommit) - rootCmd := &cobra.Command{ - Use: ui.AppName, - Short: "Lazy SSH server picker TUI", - RunE: func(cmd *cobra.Command, args []string) error { return tui.Run() }, } - rootCmd.SilenceUsage = true +) +func main() { if err := rootCmd.Execute(); err != nil { _, _ = fmt.Fprintln(os.Stderr, err) os.Exit(1) } } + +func init() { + rootCmd.PersistentFlags().StringVar(&sshConfigFile, "sshconfig", "", "path to ssh config file (default: ~/.ssh/config)") + + rootCmd.SilenceUsage = true +} diff --git a/internal/adapters/data/ssh_config_file/ssh_config_file_repo.go b/internal/adapters/data/ssh_config_file/ssh_config_file_repo.go index 37e8004..21c50e9 100644 --- a/internal/adapters/data/ssh_config_file/ssh_config_file_repo.go +++ b/internal/adapters/data/ssh_config_file/ssh_config_file_repo.go @@ -164,3 +164,8 @@ func (r *Repository) SetPinned(alias string, pinned bool) error { func (r *Repository) RecordSSH(alias string) error { return r.metadataManager.recordSSH(alias) } + +// GetConfigFile gets the path to the ssh config file. +func (r *Repository) GetConfigFile() string { + return r.configPath +} diff --git a/internal/core/ports/repositories.go b/internal/core/ports/repositories.go index 133e4f8..ccb88bf 100644 --- a/internal/core/ports/repositories.go +++ b/internal/core/ports/repositories.go @@ -23,4 +23,5 @@ type ServerRepository interface { DeleteServer(server domain.Server) error SetPinned(alias string, pinned bool) error RecordSSH(alias string) error + GetConfigFile() string } diff --git a/internal/core/services/server_service.go b/internal/core/services/server_service.go index 7926bc6..4daa252 100644 --- a/internal/core/services/server_service.go +++ b/internal/core/services/server_service.go @@ -156,7 +156,7 @@ func (s *serverService) SetPinned(alias string, pinned bool) error { // SSH starts an interactive SSH session to the given alias using the system's ssh client. func (s *serverService) SSH(alias string) error { s.logger.Infow("ssh start", "alias", alias) - cmd := exec.Command("ssh", alias) + cmd := exec.Command("ssh", "-F", s.serverRepository.GetConfigFile(), alias) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr @@ -177,6 +177,7 @@ func (s *serverService) SSH(alias string) error { func (s *serverService) SSHWithArgs(alias string, extraArgs []string) error { s.logger.Infow("ssh start (with args)", "alias", alias, "args", extraArgs) args := append([]string{}, extraArgs...) + args = append(args, "-F", s.serverRepository.GetConfigFile()) args = append(args, alias) // #nosec G204 cmd := exec.Command("ssh", args...)