Skip to content

Commit 0a7ee87

Browse files
committed
update
1 parent 34c87ef commit 0a7ee87

1 file changed

Lines changed: 112 additions & 0 deletions

File tree

internal/plugin/runtime.go

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"bytes"
66
"context"
77
"encoding/json"
8+
"errors"
89
"fmt"
910
"io"
1011
"os"
@@ -527,6 +528,70 @@ func spawnStdio(ctx context.Context, spec *CommandSpec, pluginDir string, req ma
527528
}
528529
}
529530

531+
// --- Windows fallback: if shebang not detected/usable, run by extension ---
532+
// On Windows, shebang is not a reliable execution mechanism.
533+
// If entry is a script file, wrap it with an interpreter from PATH.
534+
if runtime.GOOS == "windows" && !useInterpreter {
535+
low := strings.ToLower(entry)
536+
537+
switch {
538+
case strings.HasSuffix(low, ".py"):
539+
// Prefer python3, fallback python.
540+
if p, e := exec.LookPath("python3"); e == nil {
541+
interp = p
542+
} else if p, e := exec.LookPath("python"); e == nil {
543+
interp = p
544+
} else {
545+
writeLogLine(w, map[string]interface{}{
546+
"level": "error",
547+
"message": "python not found in PATH (required for .py stdio scripts on Windows)",
548+
"entry": entry,
549+
})
550+
return map[string]interface{}{
551+
"status": "error",
552+
"message": "python not found in PATH (required for .py stdio scripts on Windows)",
553+
}, 1
554+
}
555+
useInterpreter = true
556+
scriptAbs = entry
557+
558+
case strings.HasSuffix(low, ".js"):
559+
if p, e := exec.LookPath("node"); e == nil {
560+
interp = p
561+
useInterpreter = true
562+
scriptAbs = entry
563+
} else {
564+
writeLogLine(w, map[string]interface{}{
565+
"level": "error",
566+
"message": "node not found in PATH (required for .js stdio scripts on Windows)",
567+
"entry": entry,
568+
})
569+
return map[string]interface{}{
570+
"status": "error",
571+
"message": "node not found in PATH (required for .js stdio scripts on Windows)",
572+
}, 1
573+
}
574+
575+
case strings.HasSuffix(low, ".sh"):
576+
// Optional: if user has Git-Bash/WSL bash in PATH.
577+
if p, e := exec.LookPath("bash"); e == nil {
578+
interp = p
579+
useInterpreter = true
580+
scriptAbs = entry
581+
} else {
582+
writeLogLine(w, map[string]interface{}{
583+
"level": "error",
584+
"message": "bash not found in PATH (required for .sh scripts on Windows)",
585+
"entry": entry,
586+
})
587+
return map[string]interface{}{
588+
"status": "error",
589+
"message": "bash not found in PATH (required for .sh scripts on Windows)",
590+
}, 1
591+
}
592+
}
593+
}
594+
530595
// If no interpreter was resolved from shebang, pick one by file extension.
531596
// This keeps stdio scripts runnable even without a shebang line.
532597
if !useInterpreter {
@@ -792,3 +857,50 @@ func copyFile(src, dst string) error {
792857
_, err = io.Copy(out, in)
793858
return err
794859
}
860+
861+
func normalizeProgramForOS(spec *CommandSpec) (string, []string, error) {
862+
prog := spec.Program
863+
args := append([]string{}, spec.Args...)
864+
865+
// Only apply on Windows. Unix can execute shebang scripts directly.
866+
if runtime.GOOS != "windows" {
867+
return prog, args, nil
868+
}
869+
870+
low := strings.ToLower(strings.TrimSpace(prog))
871+
872+
// If program is plugin-relative like ./scripts/a.py, keep it as-is, exec.Command can handle it
873+
// but Windows can't execute .py directly, so we wrap it with python.
874+
switch {
875+
case strings.HasSuffix(low, ".py"):
876+
// Prefer python3, fallback python.
877+
py := ""
878+
if p, err := exec.LookPath("python3"); err == nil {
879+
py = p
880+
} else if p, err := exec.LookPath("python"); err == nil {
881+
py = p
882+
} else {
883+
return "", nil, errors.New("python not found in PATH (required to run .py stdio scripts on Windows)")
884+
}
885+
// python <script> <args...>
886+
return py, append([]string{prog}, args...), nil
887+
888+
case strings.HasSuffix(low, ".js"):
889+
node, err := exec.LookPath("node")
890+
if err != nil {
891+
return "", nil, errors.New("node not found in PATH (required to run .js stdio scripts on Windows)")
892+
}
893+
return node, append([]string{prog}, args...), nil
894+
895+
case strings.HasSuffix(low, ".sh"):
896+
// If user has Git-Bash or WSL bash in PATH, allow it.
897+
bash, err := exec.LookPath("bash")
898+
if err != nil {
899+
return "", nil, errors.New("bash not found in PATH (required to run .sh scripts on Windows)")
900+
}
901+
return bash, append([]string{prog}, args...), nil
902+
}
903+
904+
// For exe/cmd/ps1, do nothing
905+
return prog, args, nil
906+
}

0 commit comments

Comments
 (0)