Apply PR #76 from dannomayernotabot: - Add golangci exclusions for internal package false positives - Tighten file permissions (0644 -> 0600) for sensitive files - Add ReadHeaderTimeout to HTTP server (slowloris prevention) - Explicit error ignoring with _ = for intentional cases - Add //nolint comments with justifications - Spelling: cancelled -> canceled (US locale) Co-Authored-By: dannomayernotabot <noreply@github.com> 🤖 Generated with Claude Code
190 lines
4.8 KiB
Go
190 lines
4.8 KiB
Go
package connection
|
|
|
|
import (
|
|
"io/fs"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
|
|
"github.com/steveyegge/gastown/internal/tmux"
|
|
)
|
|
|
|
// LocalConnection implements Connection for local file and command operations.
|
|
type LocalConnection struct {
|
|
tmux *tmux.Tmux
|
|
}
|
|
|
|
// NewLocalConnection creates a new local connection.
|
|
func NewLocalConnection() *LocalConnection {
|
|
return &LocalConnection{
|
|
tmux: tmux.NewTmux(),
|
|
}
|
|
}
|
|
|
|
// Name returns "local" for local connections.
|
|
func (c *LocalConnection) Name() string {
|
|
return "local"
|
|
}
|
|
|
|
// IsLocal returns true for local connections.
|
|
func (c *LocalConnection) IsLocal() bool {
|
|
return true
|
|
}
|
|
|
|
// ReadFile reads the named file.
|
|
func (c *LocalConnection) ReadFile(path string) ([]byte, error) {
|
|
data, err := os.ReadFile(path) //nolint:gosec // G304: path is from Connection interface, validated by caller
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return nil, &NotFoundError{Path: path}
|
|
}
|
|
if os.IsPermission(err) {
|
|
return nil, &PermissionError{Path: path, Op: "read"}
|
|
}
|
|
return nil, err
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
// WriteFile writes data to the named file.
|
|
func (c *LocalConnection) WriteFile(path string, data []byte, perm fs.FileMode) error {
|
|
err := os.WriteFile(path, data, perm)
|
|
if err != nil {
|
|
if os.IsPermission(err) {
|
|
return &PermissionError{Path: path, Op: "write"}
|
|
}
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// MkdirAll creates a directory and all parent directories.
|
|
func (c *LocalConnection) MkdirAll(path string, perm fs.FileMode) error {
|
|
err := os.MkdirAll(path, perm)
|
|
if err != nil {
|
|
if os.IsPermission(err) {
|
|
return &PermissionError{Path: path, Op: "mkdir"}
|
|
}
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Remove removes the named file or empty directory.
|
|
func (c *LocalConnection) Remove(path string) error {
|
|
err := os.Remove(path)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return nil // Already gone
|
|
}
|
|
if os.IsPermission(err) {
|
|
return &PermissionError{Path: path, Op: "remove"}
|
|
}
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// RemoveAll removes the named file or directory and any children.
|
|
func (c *LocalConnection) RemoveAll(path string) error {
|
|
err := os.RemoveAll(path)
|
|
if err != nil {
|
|
if os.IsPermission(err) {
|
|
return &PermissionError{Path: path, Op: "remove"}
|
|
}
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Stat returns file info for the named file.
|
|
func (c *LocalConnection) Stat(path string) (FileInfo, error) {
|
|
fi, err := os.Stat(path)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return nil, &NotFoundError{Path: path}
|
|
}
|
|
if os.IsPermission(err) {
|
|
return nil, &PermissionError{Path: path, Op: "stat"}
|
|
}
|
|
return nil, err
|
|
}
|
|
return FromOSFileInfo(fi), nil
|
|
}
|
|
|
|
// Glob returns the names of all files matching the pattern.
|
|
func (c *LocalConnection) Glob(pattern string) ([]string, error) {
|
|
matches, err := filepath.Glob(pattern)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return matches, nil
|
|
}
|
|
|
|
// Exists returns true if the path exists.
|
|
func (c *LocalConnection) Exists(path string) (bool, error) {
|
|
_, err := os.Stat(path)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return false, nil
|
|
}
|
|
return false, err
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
// Exec runs a command and returns its combined output.
|
|
func (c *LocalConnection) Exec(cmd string, args ...string) ([]byte, error) {
|
|
return exec.Command(cmd, args...).CombinedOutput()
|
|
}
|
|
|
|
// ExecDir runs a command in the specified directory.
|
|
func (c *LocalConnection) ExecDir(dir, cmd string, args ...string) ([]byte, error) {
|
|
command := exec.Command(cmd, args...)
|
|
command.Dir = dir
|
|
return command.CombinedOutput()
|
|
}
|
|
|
|
// ExecEnv runs a command with additional environment variables.
|
|
func (c *LocalConnection) ExecEnv(env map[string]string, cmd string, args ...string) ([]byte, error) {
|
|
command := exec.Command(cmd, args...)
|
|
command.Env = os.Environ()
|
|
for k, v := range env {
|
|
command.Env = append(command.Env, k+"="+v)
|
|
}
|
|
return command.CombinedOutput()
|
|
}
|
|
|
|
// TmuxNewSession creates a new tmux session.
|
|
func (c *LocalConnection) TmuxNewSession(name, dir string) error {
|
|
return c.tmux.NewSession(name, dir)
|
|
}
|
|
|
|
// TmuxKillSession terminates a tmux session.
|
|
func (c *LocalConnection) TmuxKillSession(name string) error {
|
|
return c.tmux.KillSession(name)
|
|
}
|
|
|
|
// TmuxSendKeys sends keys to a tmux session.
|
|
func (c *LocalConnection) TmuxSendKeys(session, keys string) error {
|
|
return c.tmux.SendKeys(session, keys)
|
|
}
|
|
|
|
// TmuxCapturePane captures the last N lines from a tmux pane.
|
|
func (c *LocalConnection) TmuxCapturePane(session string, lines int) (string, error) {
|
|
return c.tmux.CapturePane(session, lines)
|
|
}
|
|
|
|
// TmuxHasSession returns true if the session exists.
|
|
func (c *LocalConnection) TmuxHasSession(name string) (bool, error) {
|
|
return c.tmux.HasSession(name)
|
|
}
|
|
|
|
// TmuxListSessions returns all tmux session names.
|
|
func (c *LocalConnection) TmuxListSessions() ([]string, error) {
|
|
return c.tmux.ListSessions()
|
|
}
|
|
|
|
// Verify LocalConnection implements Connection.
|
|
var _ Connection = (*LocalConnection)(nil)
|