Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/ssh config #37

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ var (
inputFlag modfile.Config
remotePath string
Tui bool
port string
)

var rootCmd = &cobra.Command{
Expand Down Expand Up @@ -62,6 +63,7 @@ func init() {
rootCmd.PersistentFlags().StringVarP(&inputFlag.EndLabel, "end-label", "e", "", "pass argument to end-label to modify lines up to end-label")
rootCmd.PersistentFlags().StringVarP(&inputFlag.Lang, "language", "L", "", "pass argument to language to specify the language of the input code")
rootCmd.PersistentFlags().StringVarP(&remotePath, "remote", "w", "", "pass remote user, host, and directory in the format user@host:/path/to/directory")
rootCmd.PersistentFlags().StringVarP(&port, "port", "p", "", "Specify the port number to use for connecting to the server. This option allows you to override the default port, which 2222.")
rootCmd.PersistentFlags().BoolVarP(&Tui, "tui", "t", false, "run the terminal user interface")
// Mark flags based on command name
rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
Expand Down Expand Up @@ -100,7 +102,7 @@ func ReadFlags(cmd *cobra.Command) {
// Initialize your model with the current directory
model := tui.Model{
State: "FileSelection",
FilesSelector: modelutils.InitialModel(currentDir, 20),
FilesSelector: modelutils.InitialModel(currentDir, 20, 20),
}
clearScreen()
// Bubble Tea program
Expand Down
55 changes: 40 additions & 15 deletions cmd/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

"github.com/creack/pty"
"github.com/dyne/tgcom/utils/server"
"github.com/kevinburke/ssh_config"
"github.com/spf13/cobra"
"golang.org/x/term"
)
Expand All @@ -23,34 +24,58 @@ var serverCmd = &cobra.Command{
Short: "Start the SSH server",
Long: `Start the SSH server that allows remote interactions with tgcom.`,
Run: func(cmd *cobra.Command, args []string) {
server.StartServer()
server.StartServer(serverPort)
},
}
var serverPort string

func init() {
serverCmd.PersistentFlags().StringVarP(&serverPort, "port", "p", "2222", "Specify the port number to use for connecting to the server. This option allows you to override the default port, which 2222.")
// Register the server command
rootCmd.AddCommand(serverCmd)
}

func executeRemoteCommand(remotePath string) {
parts := strings.SplitN(remotePath, "@", 2)
if len(parts) != 2 {
fmt.Println("Invalid format. Usage: tgcom -w user@remote:/path/folder")
os.Exit(1)
}
var userHost, dir, sshPort string

if strings.Contains(remotePath, ":") && !strings.Contains(remotePath, "@") {
// Using SSH config alias format
parts := strings.SplitN(remotePath, ":", 2)
if len(parts) != 2 {
fmt.Println("Invalid format. Usage: tgcom config:path/to/folder")
os.Exit(1)
}
configAlias := parts[0]
dir = parts[1]

userHost := parts[0]
pathParts := strings.SplitN(parts[1], ":", 2)
if len(pathParts) != 2 {
fmt.Println("Invalid format. Usage: tgcom -w user@remote:/path/folder")
os.Exit(1)
}
userHost = configAlias
sshPort = ssh_config.Get(configAlias, "Port")
if sshPort == "" {
sshPort = port
}
} else {
// Using user@host:/path format
parts := strings.SplitN(remotePath, "@", 2)
if len(parts) != 2 {
fmt.Println("Invalid format. Usage: tgcom -w user@remote:/path/folder or tgcom config:path/to/folder")
os.Exit(1)
}

userHost = parts[0]
pathParts := strings.SplitN(parts[1], ":", 2)
if len(pathParts) != 2 {
fmt.Println("Invalid format. Usage: tgcom -w user@remote:/path/folder or tgcom config:path/to/folder")
os.Exit(1)
}

host := pathParts[0]
dir := pathParts[1]
host := pathParts[0]
dir = pathParts[1]
sshPort = port
userHost = fmt.Sprintf("%s@%s", userHost, host)
}

sshCmd := "ssh"
sshArgs := []string{"-t", "-p", "2222", userHost + "@" + host, "tgcom", dir}
sshArgs := []string{"-t", "-p", sshPort, userHost, "tgcom", dir}

// Start SSH command with PTY
if err := startSSHWithPTY(sshCmd, sshArgs); err != nil {
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ require (
github.com/charmbracelet/ssh v0.0.0-20240604154955-a40c6a0d028f
github.com/charmbracelet/wish v1.4.0
github.com/creack/pty v1.1.21
github.com/kevinburke/ssh_config v1.2.0
github.com/spf13/cobra v1.8.0
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.9.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
Expand Down
6 changes: 3 additions & 3 deletions utils/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,20 @@ var (
dir string
)

func StartServer() {
func StartServer(port string) {
FilippoTrotter marked this conversation as resolved.
Show resolved Hide resolved
withHostKey := wish.WithHostKeyPath(pathHostKey)
if pem, ok := os.LookupEnv(envHostKey); ok {
withHostKey = wish.WithHostKeyPEM([]byte(pem))
}
srv, err := wish.NewServer(
wish.WithAddress(":2222"),
wish.WithAddress(":"+port),
wish.WithMiddleware(
bm.Middleware(func(s ssh.Session) (tea.Model, []tea.ProgramOption) {
pty, _, _ := s.Pty()
// Initialize the file selector model with the directory argument
model := tui.Model{
State: "FileSelection",
FilesSelector: modelutils.InitialModel(dir, pty.Window.Height-5), // Initialize the FilesSelector model with window height
FilesSelector: modelutils.InitialModel(dir, pty.Window.Height-5, pty.Window.Width-5), // Initialize the FilesSelector model with window height
}
if model.Error != nil {
wish.Println(s, model.Error.Error())
Expand Down
14 changes: 7 additions & 7 deletions utils/tui/model_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (

func TestModel(t *testing.T) {
t.Run("Init", func(t *testing.T) {
model := Model{FilesSelector: modelutils.InitialModel(".", 10)}
model := Model{FilesSelector: modelutils.InitialModel(".", 10, 10)}
cmd := model.Init()
assert.Nil(t, cmd)
})
Expand All @@ -32,7 +32,7 @@ func TestModel(t *testing.T) {
name: "FileSelection to ModeSelection",
model: Model{
State: "FileSelection",
FilesSelector: modelutils.InitialModel(".", 10),
FilesSelector: modelutils.InitialModel(".", 10, 10),
},
setup: func(m *Model) {
m.FilesSelector.FilesPath = []string{"path/test/file1", "path/test/file2"}
Expand All @@ -51,7 +51,7 @@ func TestModel(t *testing.T) {
name: "FileSelection to ActionSelection",
model: Model{
State: "FileSelection",
FilesSelector: modelutils.InitialModel(".", 10),
FilesSelector: modelutils.InitialModel(".", 10, 10),
},
setup: func(m *Model) {
m.FilesSelector.FilesPath = []string{"path/test/file1"}
Expand All @@ -69,7 +69,7 @@ func TestModel(t *testing.T) {
name: "No file selected",
model: Model{
State: "FileSelection",
FilesSelector: modelutils.InitialModel(".", 10),
FilesSelector: modelutils.InitialModel(".", 10, 10),
},
msg: tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'x'}},
verify: func(t *testing.T, m Model) {
Expand Down Expand Up @@ -156,7 +156,7 @@ func TestModel(t *testing.T) {
name: "ModeSelection to FileSelection",
model: Model{
State: "ModeSelection",
FilesSelector: modelutils.InitialModel(".", 10),
FilesSelector: modelutils.InitialModel(".", 10, 10),
SpeedSelector: modelutils.NewModeSelector([]string{"Fast mode", "Slow mode"}, "", ""),
Files: []string{"file1.txt", "file2.txt"},
},
Expand All @@ -174,7 +174,7 @@ func TestModel(t *testing.T) {
name: "ActionSelection to FileSelection",
model: Model{
State: "ActionSelection",
FilesSelector: modelutils.InitialModel(".", 10),
FilesSelector: modelutils.InitialModel(".", 10, 10),
SpeedSelector: modelutils.ModeSelector{Selected: "Fast mode"},
ActionSelector: modelutils.NewModeSelector([]string{"toggle", "comment", "uncomment"}, "", ""),
Files: []string{"file1.txt"},
Expand Down Expand Up @@ -424,7 +424,7 @@ func TestModel(t *testing.T) {
tests := []viewTest{
{
name: "FileSelection View",
model: Model{State: "FileSelection", FilesSelector: modelutils.InitialModel(".", 10)},
model: Model{State: "FileSelection", FilesSelector: modelutils.InitialModel(".", 10, 10)},
expected: "Select the files you want to modify",
},
{
Expand Down
47 changes: 38 additions & 9 deletions utils/tui/modelutils/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package modelutils
import (
"fmt"
"os"
"strings"

tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
Expand All @@ -19,9 +20,10 @@ type FilesSelector struct {
WindowHeight int
Error error
NoFileSelected bool
WindowWidth int
}

func InitialModel(currentDir string, windowHeight int) FilesSelector {
func InitialModel(currentDir string, windowHeight int, windowWidth int) FilesSelector {
var filesAndDir []string
selectedFilesAndDir := make(map[int]bool)

Expand All @@ -47,6 +49,7 @@ func InitialModel(currentDir string, windowHeight int) FilesSelector {
FilesAndDir: filesAndDir,
SelectedFilesAndDir: selectedFilesAndDir,
WindowHeight: windowHeight,
WindowWidth: windowWidth,
}
}

Expand Down Expand Up @@ -111,24 +114,41 @@ func (m FilesSelector) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.Done = true
}
}
case tea.WindowSizeMsg:
return m, m.doResize(msg)
FilippoTrotter marked this conversation as resolved.
Show resolved Hide resolved
}
return m, nil
}

func (m *FilesSelector) doResize(msg tea.WindowSizeMsg) tea.Cmd {
m.WindowHeight = msg.Height
m.WindowWidth = msg.Width
return nil
}
FilippoTrotter marked this conversation as resolved.
Show resolved Hide resolved

func (m FilesSelector) View() string {
if m.Error != nil {
return Paint("red").Render(fmt.Sprintf("An error occurred: %v", m.Error))
}

s := Paint("silver").Render("\n Select the files you want to modify...") + "\n"
s += Paint("silver").Render("\n Selected files till now:") + "\n"
// Help messages
helpMessages := []string{
"'q' to quit 'esc' to move to parent directory",
"'↑' to go up 'x' to modify selected files",
"'↓' to go down 'enter' to select pointed file/move to pointed sub folder",
}

// File selection and error messages
var sb strings.Builder
sb.WriteString(Paint("silver").Render("\n Select the files you want to modify...") + "\n")
sb.WriteString(Paint("silver").Render("\n Selected files till now:") + "\n")
if m.NoFileSelected {
s += Paint("red").Render("\n No file selected. Please select at least one file or quit.") + "\n"
sb.WriteString(Paint("red").Render("\n No file selected. Please select at least one file or quit.") + "\n")
}
for i := 0; i < len(m.FilesPath); i++ {
s += fmt.Sprintf(" %s\n", Paint("green").Render(m.FilesPath[i]))
sb.WriteString(fmt.Sprintf(" %s\n", Paint("green").Render(m.FilesPath[i])))
}
s += "\n"
sb.WriteString("\n")

for i := m.scrollOffset; i < m.scrollOffset+m.WindowHeight && i < len(m.FilesAndDir); i++ {
choice := m.FilesAndDir[i]
Expand All @@ -150,10 +170,19 @@ func (m FilesSelector) View() string {
cursor = Paint("red").Render(" ➪")
}

s += fmt.Sprintf("%s %s\n", cursor, choice)
sb.WriteString(fmt.Sprintf("%s %s\n", cursor, choice))
}
s += Paint("silver").Render("\n 'q' to quit 'esc' to move to parent directory\n '↑' to go up 'x' to modify selected files\n '↓' to go down 'enter' to select pointed file/move to pointed sub folder")
return s

fileSelection := sb.String()

// Combine file selection with help messages
helpView := lipgloss.JoinVertical(lipgloss.Left, helpMessages...)
content := lipgloss.JoinVertical(lipgloss.Left, fileSelection, helpView)

// Place the content in the center of the screen
fullView := lipgloss.Place(m.WindowWidth, m.WindowHeight, lipgloss.Left, lipgloss.Left, content)

return fullView
}

func Paint(color string) lipgloss.Style {
Expand Down
Loading
Loading