diff options
| author | ewy <ewy0@protonmail.com> | 2026-04-14 16:37:17 +0200 |
|---|---|---|
| committer | ewy <ewy0@protonmail.com> | 2026-04-14 16:37:17 +0200 |
| commit | 45a297a8e526094e8fce6e2c5c0fd89b381d1765 (patch) | |
| tree | 852ebc3a0112c94dc9726d0b27ab057bf6383660 /runner/shell/shell.go | |
i have to commit at some point!
Diffstat (limited to 'runner/shell/shell.go')
| -rw-r--r-- | runner/shell/shell.go | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/runner/shell/shell.go b/runner/shell/shell.go new file mode 100644 index 0000000..5333279 --- /dev/null +++ b/runner/shell/shell.go @@ -0,0 +1,176 @@ +package shell + +import ( + "bufio" + "errors" + "io/fs" + "os/exec" + "path/filepath" + "pik/identity" + "pik/indexers/pikdex" + "pik/model" + "pik/runner" + "pik/spool" + "slices" + "strings" +) + +//TODO: Clean up shell selection? Maybe default to bash? + +var NoContentError = errors.New("not enough content in target") +var NoShellError = errors.New("could not find any shell interpreters") + +var ExtShellMap = map[string]string{ + ".sh": "bash", + ".ps1": "powershell", +} + +var Shells = []string{"bash", "bash.exe", "zsh", "fish", "powershell", "powershell.exe", "cmd.exe"} + +var Runner = &shell{ + Locations: map[string]string{}, +} + +type shell struct { + Locations map[string]string +} + +var WrongTargetError = errors.New("wrong target type") + +func (s *shell) Hydrate(target model.Target) (model.HydratedTarget, error) { + cast, ok := target.(*ShellTarget) + if !ok { + return nil, WrongTargetError + } + hyd := &HydratedShellTarget{BaseHydration: runner.Hydrated(cast)} + return hyd, nil +} + +func (s *shell) Wants(f fs.FS, file string, entry fs.DirEntry) (bool, error) { + if entry != nil && entry.IsDir() { + return false, nil + } + + fd, err := f.Open(file) + if err != nil { + return false, err + } + scanner := bufio.NewScanner(fd) + scanner.Split(bufio.ScanRunes) + if !scanner.Scan() { + return false, nil + } + txt := scanner.Text() + if txt == "#" { // + return true, nil + } + for k, _ := range ExtShellMap { + if strings.HasSuffix(file, k) { + return true, nil + } + } + return false, nil +} + +func (s *shell) Find(shell string) (string, error) { + if s.Locations[shell] != "" { + return s.Locations[shell], nil + } + + if p, err := exec.LookPath(shell); err == nil { + s.Locations[shell] = p + return shell, nil + } else { + return "", err + } +} + +func (s *shell) CreateTarget(fs fs.FS, src string, file string, _ fs.DirEntry) (model.Target, error) { + shell, err := s.ShellFor(fs, file) + if err != nil { + return nil, err + } + _, filename := filepath.Split(file) + var sub []string + split := strings.Split(file, "/") + for _, p := range split { + if slices.Contains(pikdex.Roots, p) { + continue + } + if filename == p { + continue + } + sub = append(sub, p) + } + return &ShellTarget{ + BaseTarget: runner.BaseTarget{ + Identity: identity.New(filename), + MyTags: model.TagsFromFilename(filename), + }, + Shell: shell, + Script: file, + SubValue: sub, + }, nil +} + +func (s *shell) ShellFor(fs fs.FS, file string) (string, error) { + + var shell, shebang string + + // low-hanging fruit - indicative filename + if byFile := s.ShellByFilename(file); byFile != "" { + return byFile, nil + } + + fd, err := fs.Open(file) + if err != nil { + return "", err + } + scanner := bufio.NewScanner(fd) + scanner.Split(bufio.ScanLines) + if !scanner.Scan() { + return "", NoContentError + } + txt := scanner.Text() + if strings.HasPrefix(txt, "#!") { + // shebang found + for _, potentialShell := range Shells { + if strings.Contains(txt, potentialShell) { + shebang = shell + if loc, err := s.Find(potentialShell); err == nil { + shell = loc + } else { + _, _ = spool.Warn("script has %s but could not find %s (%s)\n", shebang, potentialShell) + } + } + } + } + + if shebang == "" { + // if no shebang, just send the first one we find + for _, s := range Shells { + if p, err := exec.LookPath(s); err != nil { + shell = p + } + } + } + + if shell == "" { + return "", NoShellError + } + + return shell, nil + +} + +func (s *shell) ShellByFilename(file string) string { + ext := filepath.Ext(file) + if ExtShellMap[ext] != "" { + sh, err := s.Find(ExtShellMap[ext]) + if err == nil { + return sh + } + } + + return "" +} |
