all repos

viye @ 9f9e365d9f5437ceeb417be7809b0c3c43e42601

my shot at reimplementing xiki

viye/internal/plumbing/shell/shell.go (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
show files content, 2 months ago
1
package shell
2
3
import (
4
	"context"
5
	"fmt"
6
	"os"
7
	"os/exec"
8
	"path/filepath"
9
	"strconv"
10
	"strings"
11
	"syscall"
12
	"time"
13
14
	"github.com/olexsmir/viye/internal/viye"
15
)
16
17
type Tool struct{}
18
19
func (Tool) Name() string { return "shell($, $$, kill, mkdir)" }
20
func (Tool) Match(ctx *viye.Context) bool {
21
	return isCmd(ctx) || isBGCmd(ctx) || isKill(ctx) || isMkdir(ctx)
22
}
23
24
func (Tool) Execute(ctx *viye.Context) (string, error) {
25
	switch {
26
	case isCmd(ctx):
27
		ctxTimeout, cancel := context.WithTimeout(context.Background(), 5*time.Second)
28
		defer cancel()
29
30
		cmd := exec.CommandContext(ctxTimeout, "sh", "-c", strings.Join(ctx.Args, " "))
31
		cmd.Dir = ctx.Dir
32
		out, err := cmd.CombinedOutput()
33
		if err != nil {
34
			if ctxTimeout.Err() == context.DeadlineExceeded {
35
				return "", fmt.Errorf("timeout: command exceeded 5s")
36
			}
37
		}
38
		return viye.FormatOutput(string(out)), nil
39
40
	case isBGCmd(ctx):
41
		cmd := exec.Command("sh", "-c", strings.Join(ctx.Args, " "))
42
		cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
43
		cmd.Dir = ctx.Dir
44
		if err := cmd.Start(); err != nil {
45
			return "", err
46
		}
47
		return fmt.Sprintf("| pid: %d", cmd.Process.Pid), nil
48
49
	case isKill(ctx):
50
		pid, err := strconv.Atoi(ctx.Args[0])
51
		if err != nil {
52
			return "", fmt.Errorf("kill: invalid pid %q", ctx.Args[0])
53
		}
54
		p, err := os.FindProcess(pid)
55
		if err != nil {
56
			return "", fmt.Errorf("kill: %w", err)
57
		}
58
		if err := p.Kill(); err != nil {
59
			return "", fmt.Errorf("kill: %w", err)
60
		}
61
		return "| done\n", nil
62
63
	case isMkdir(ctx):
64
		path := filepath.Join(append([]string{ctx.Dir}, ctx.Args...)...)
65
		if err := os.MkdirAll(path, 0o755); err != nil {
66
			return "", fmt.Errorf("mkdir: %w", err)
67
		}
68
		return "| created " + path + "\n", nil
69
70
	default:
71
		panic("shouldn't have paniced")
72
	}
73
}
74
75
func isCmd(c *viye.Context) bool   { return c.Cmd == "$" }
76
func isBGCmd(c *viye.Context) bool { return c.Cmd == "$$" }
77
func isKill(c *viye.Context) bool  { return c.Cmd == "kill" && len(c.Args) == 1 }
78
func isMkdir(c *viye.Context) bool { return c.Cmd == "mkdir" && len(c.Args) >= 1 }