mirror of
https://github.com/Gu1llaum-3/sshm.git
synced 2025-10-19 01:17:20 +02:00
feat: add direct host connection via sshm <host>
with history tracking
This commit is contained in:
parent
8c6f3b01ef
commit
71bf8ea2bb
79
cmd/root.go
79
cmd/root.go
@ -5,10 +5,13 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Gu1llaum-3/sshm/internal/config"
|
"github.com/Gu1llaum-3/sshm/internal/config"
|
||||||
|
"github.com/Gu1llaum-3/sshm/internal/history"
|
||||||
"github.com/Gu1llaum-3/sshm/internal/ui"
|
"github.com/Gu1llaum-3/sshm/internal/ui"
|
||||||
"github.com/Gu1llaum-3/sshm/internal/version"
|
"github.com/Gu1llaum-3/sshm/internal/version"
|
||||||
|
|
||||||
@ -23,27 +26,32 @@ var configFile string
|
|||||||
|
|
||||||
// RootCmd is the base command when called without any subcommands
|
// RootCmd is the base command when called without any subcommands
|
||||||
var RootCmd = &cobra.Command{
|
var RootCmd = &cobra.Command{
|
||||||
Use: "sshm",
|
Use: "sshm [host]",
|
||||||
Short: "SSH Manager - A modern SSH connection manager",
|
Short: "SSH Manager - A modern SSH connection manager",
|
||||||
Long: `SSHM is a modern SSH manager for your terminal.
|
Long: `SSHM is a modern SSH manager for your terminal.
|
||||||
|
|
||||||
Main usage:
|
Main usage:
|
||||||
Running 'sshm' (without arguments) opens the interactive TUI window to browse, search, and connect to your SSH hosts graphically.
|
Running 'sshm' (without arguments) opens the interactive TUI window to browse, search, and connect to your SSH hosts graphically.
|
||||||
|
Running 'sshm <host>' connects directly to the specified host and records the connection in your history.
|
||||||
|
|
||||||
You can also use sshm in CLI mode for direct operations.
|
You can also use sshm in CLI mode for other operations like adding, editing, or searching hosts.
|
||||||
|
|
||||||
Hosts are read from your ~/.ssh/config file by default.`,
|
Hosts are read from your ~/.ssh/config file by default.`,
|
||||||
Version: AppVersion,
|
Version: AppVersion,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Args: cobra.ArbitraryArgs,
|
||||||
|
SilenceUsage: true,
|
||||||
|
SilenceErrors: true, // We'll handle errors ourselves
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
// If no arguments provided, run interactive mode
|
// If no arguments provided, run interactive mode
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
runInteractiveMode()
|
runInteractiveMode()
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a host name is provided, connect directly
|
// If a host name is provided, connect directly
|
||||||
hostName := args[0]
|
hostName := args[0]
|
||||||
connectToHost(hostName)
|
connectToHost(hostName)
|
||||||
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,20 +132,46 @@ func connectToHost(hostName string) {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect to the host
|
// Record the connection in history
|
||||||
fmt.Printf("Connecting to %s...\n", hostName)
|
historyManager, err := history.NewHistoryManager()
|
||||||
|
if err != nil {
|
||||||
// Build the SSH command with the appropriate config file
|
// Log the error but don't prevent the connection
|
||||||
var sshCmd []string
|
fmt.Printf("Warning: Could not initialize connection history: %v\n", err)
|
||||||
if configFile != "" {
|
|
||||||
sshCmd = []string{"ssh", "-F", configFile, hostName}
|
|
||||||
} else {
|
} else {
|
||||||
sshCmd = []string{"ssh", hostName}
|
err = historyManager.RecordConnection(hostName)
|
||||||
|
if err != nil {
|
||||||
|
// Log the error but don't prevent the connection
|
||||||
|
fmt.Printf("Warning: Could not record connection history: %v\n", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: In a real implementation, you'd use exec.Command here
|
// Build and execute the SSH command
|
||||||
// For now, just print the command that would be executed
|
fmt.Printf("Connecting to %s...\n", hostName)
|
||||||
fmt.Printf("%s\n", strings.Join(sshCmd, " "))
|
|
||||||
|
var sshCmd *exec.Cmd
|
||||||
|
if configFile != "" {
|
||||||
|
sshCmd = exec.Command("ssh", "-F", configFile, hostName)
|
||||||
|
} else {
|
||||||
|
sshCmd = exec.Command("ssh", hostName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up the command to use the same stdin, stdout, and stderr as the parent process
|
||||||
|
sshCmd.Stdin = os.Stdin
|
||||||
|
sshCmd.Stdout = os.Stdout
|
||||||
|
sshCmd.Stderr = os.Stderr
|
||||||
|
|
||||||
|
// Execute the SSH command
|
||||||
|
err = sshCmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
if exitError, ok := err.(*exec.ExitError); ok {
|
||||||
|
// SSH command failed, exit with the same code
|
||||||
|
if status, ok := exitError.Sys().(syscall.WaitStatus); ok {
|
||||||
|
os.Exit(status.ExitStatus())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Printf("Error executing SSH command: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getVersionWithUpdateCheck returns a custom version string with update check
|
// getVersionWithUpdateCheck returns a custom version string with update check
|
||||||
@ -166,7 +200,20 @@ func getVersionWithUpdateCheck() string {
|
|||||||
|
|
||||||
// Execute adds all child commands to the root command and sets flags appropriately.
|
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||||
func Execute() {
|
func Execute() {
|
||||||
|
// Custom error handling for unknown commands that might be host names
|
||||||
if err := RootCmd.Execute(); err != nil {
|
if err := RootCmd.Execute(); err != nil {
|
||||||
|
// Check if this is an "unknown command" error and the argument might be a host name
|
||||||
|
errStr := err.Error()
|
||||||
|
if strings.Contains(errStr, "unknown command") {
|
||||||
|
// Extract the command name from the error
|
||||||
|
parts := strings.Split(errStr, "\"")
|
||||||
|
if len(parts) >= 2 {
|
||||||
|
potentialHost := parts[1]
|
||||||
|
// Try to connect to this as a host
|
||||||
|
connectToHost(potentialHost)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,8 @@ import (
|
|||||||
|
|
||||||
func TestRootCommand(t *testing.T) {
|
func TestRootCommand(t *testing.T) {
|
||||||
// Test that the root command is properly configured
|
// Test that the root command is properly configured
|
||||||
if RootCmd.Use != "sshm" {
|
if RootCmd.Use != "sshm [host]" {
|
||||||
t.Errorf("Expected Use 'sshm', got '%s'", RootCmd.Use)
|
t.Errorf("Expected Use 'sshm [host]', got '%s'", RootCmd.Use)
|
||||||
}
|
}
|
||||||
|
|
||||||
if RootCmd.Short != "SSH Manager - A modern SSH connection manager" {
|
if RootCmd.Short != "SSH Manager - A modern SSH connection manager" {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user