commit b8f7766de887b799b54b32355bcad5bb52777f8d Author: Denys Seredenko Date: Sun Jan 12 11:38:01 2025 +0100 * added list and exec commands diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..29b636a --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea +*.iml \ No newline at end of file diff --git a/cmd/exec/exec.go b/cmd/exec/exec.go new file mode 100644 index 0000000..f6fc782 --- /dev/null +++ b/cmd/exec/exec.go @@ -0,0 +1,47 @@ +package exec + +import ( + "errors" + "fmt" + "github.com/creack/pty" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "io" + "os" + "os/exec" +) + +var execCmd = &cobra.Command{ + Use: "exec", + Short: "Execute command of specific alias", + Args: func(cmd *cobra.Command, args []string) error { + if err := cobra.ExactArgs(1)(cmd, args); err != nil { + return err + } + data := viper.GetStringMapString("data") + + if _, ok := data[args[0]]; !ok { + return errors.New("such alias was not found") + } + + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + data := viper.GetStringMapString("data") + command := exec.Command("sh", "-c", data[args[0]]) + ptmx, err := pty.Start(command) + if err != nil { + fmt.Fprintf(os.Stderr, "Error starting PTY: %v\n", err) + os.Exit(1) + } + + defer func() { _ = ptmx.Close() }() + + go func() { _, _ = io.Copy(ptmx, os.Stdin) }() + _, _ = io.Copy(os.Stdout, ptmx) + }, +} + +func Cmd() *cobra.Command { + return execCmd +} diff --git a/cmd/list/list.go b/cmd/list/list.go new file mode 100644 index 0000000..8f8cc7d --- /dev/null +++ b/cmd/list/list.go @@ -0,0 +1,29 @@ +package list + +import ( + "github.com/jedib0t/go-pretty/v6/table" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "os" +) + +var listCmd = &cobra.Command{ + Use: "list", + Short: "List all known commands and their aliases", + Run: func(cmd *cobra.Command, args []string) { + t := table.NewWriter() + t.SetOutputMirror(os.Stdout) + t.AppendHeader(table.Row{"Alias", "Command"}) + + commands := viper.GetStringMapString("data") + + for alias, command := range commands { + t.AppendRow(table.Row{alias, command}) + } + t.Render() + }, +} + +func Cmd() *cobra.Command { + return listCmd +} diff --git a/cmd/root.go b/cmd/root.go new file mode 100644 index 0000000..eb85ddc --- /dev/null +++ b/cmd/root.go @@ -0,0 +1,78 @@ +package cmd + +import ( + "encoding/json" + "errors" + "fmt" + "git.denysoft.de/CubeBit/ssh-hub/cmd/exec" + "git.denysoft.de/CubeBit/ssh-hub/cmd/list" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "os" +) + +var rootCmd = &cobra.Command{ + Use: "ssh-hub", + Short: "ssh-hub is a simple SSH manager. Storing all your ssh commands and managing them", + Long: "ssh-hub was created in order to automatically manage ssh commands and different Groups.\n " + + "\tCreate groups based on projects, environments etc and add there your ssh commands.\n " + + "\tWe will carefully manage all known sessions and groups.", + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("Welcome to ssh-hub!") + }, +} + +func Execute() { + if err := rootCmd.Execute(); err != nil { + fmt.Println(os.Stderr, err) + os.Exit(1) + } +} + +func init() { + cobra.OnInitialize(initCfg) + rootCmd.AddCommand(list.Cmd()) + rootCmd.AddCommand(exec.Cmd()) +} + +func initCfg() { + //TODO: if parameter read config from file provided read from specific file + const defaultCfg = "/.ssh-hub-config.json" + homePath, err := os.UserHomeDir() + cobra.CheckErr(err) + + if _, err := os.Stat(homePath + defaultCfg); errors.Is(err, os.ErrNotExist) { + createdFile, err := os.Create(homePath + defaultCfg) + cobra.CheckErr(err) + bytes, err := json.Marshal(map[string]string{}) + cobra.CheckErr(err) + _, err = createdFile.Write(bytes) + cobra.CheckErr(err) + } else { + cobra.CheckErr(err) + } + + configFile, err := os.ReadFile(homePath + defaultCfg) + + var result map[string]string + err = json.Unmarshal(configFile, &result) + cobra.CheckErr(err) + + viper.Set("data", result) +} + +/* +ssh-hub +├── ssh - Execute an SSH command (with autocomplete) +├── add - Add a new command or group +│ ├── command - Save an SSH command +│ └── group - Create a group of commands +├── exec - Execute a saved command or group +├── remove - Remove a command or group +├── groups - Manage groups +│ ├── add - Add a command to a group +│ ├── remove - Remove a command from a group +│ └── list - List commands in a group +├── autocomplete - Enable or generate shell autocompletion +└── help - Display help information for commands +*/ diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..1abae86 --- /dev/null +++ b/go.mod @@ -0,0 +1,35 @@ +module git.denysoft.de/CubeBit/ssh-hub + +go 1.23.4 + +require ( + github.com/spf13/cobra v1.8.1 + github.com/spf13/viper v1.19.0 +) + +require ( + github.com/creack/pty v1.1.24 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jedib0t/go-pretty/v6 v6.6.5 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.9.0 // indirect + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect + golang.org/x/sys v0.18.0 // indirect + golang.org/x/text v0.21.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/main.go b/main.go new file mode 100644 index 0000000..b03fb52 --- /dev/null +++ b/main.go @@ -0,0 +1,9 @@ +package main + +import ( + "git.denysoft.de/CubeBit/ssh-hub/cmd" +) + +func main() { + cmd.Execute() +}