Skip to content

Commit 6229096

Browse files
Windows path fixes
1 parent 5eb8d7d commit 6229096

File tree

9 files changed

+131
-69
lines changed

9 files changed

+131
-69
lines changed

pkg/engine/cmd.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ import (
88
"io"
99
"os"
1010
"os/exec"
11+
"runtime"
1112
"sort"
1213
"strings"
1314
"sync/atomic"
1415

1516
"github.com/google/shlex"
17+
"github.com/gptscript-ai/gptscript/pkg/env"
1618
"github.com/gptscript-ai/gptscript/pkg/types"
1719
"github.com/gptscript-ai/gptscript/pkg/version"
1820
)
@@ -103,6 +105,7 @@ func envAsMapAndDeDup(env []string) (sortedEnv []string, _ map[string]string) {
103105

104106
var ignoreENV = map[string]struct{}{
105107
"PATH": {},
108+
"Path": {},
106109
"GPTSCRIPT_TOOL_DIR": {},
107110
}
108111

@@ -146,8 +149,8 @@ func appendInputAsEnv(env []string, input string) []string {
146149
}
147150

148151
func (e *Engine) newCommand(ctx context.Context, extraEnv []string, tool types.Tool, input string) (*exec.Cmd, func(), error) {
149-
env := append(e.Env[:], extraEnv...)
150-
env = appendInputAsEnv(env, input)
152+
envvars := append(e.Env[:], extraEnv...)
153+
envvars = appendInputAsEnv(envvars, input)
151154

152155
interpreter, rest, _ := strings.Cut(tool.Instructions, "\n")
153156
interpreter = strings.TrimSpace(interpreter)[2:]
@@ -157,18 +160,22 @@ func (e *Engine) newCommand(ctx context.Context, extraEnv []string, tool types.T
157160
return nil, nil, err
158161
}
159162

160-
env, err = e.getRuntimeEnv(ctx, tool, args, env)
163+
envvars, err = e.getRuntimeEnv(ctx, tool, args, envvars)
161164
if err != nil {
162165
return nil, nil, err
163166
}
164167

165-
env, envMap := envAsMapAndDeDup(env)
168+
envvars, envMap := envAsMapAndDeDup(envvars)
166169
for i, arg := range args {
167170
args[i] = os.Expand(arg, func(s string) string {
168171
return envMap[s]
169172
})
170173
}
171174

175+
if runtime.GOOS == "windows" && (args[0] == "/usr/bin/env" || args[0] == "/bin/env") {
176+
args = args[1:]
177+
}
178+
172179
var (
173180
cmdArgs = args[1:]
174181
stop = func() {}
@@ -192,7 +199,7 @@ func (e *Engine) newCommand(ctx context.Context, extraEnv []string, tool types.T
192199
cmdArgs = append(cmdArgs, f.Name())
193200
}
194201

195-
cmd := exec.CommandContext(ctx, args[0], cmdArgs...)
196-
cmd.Env = env
202+
cmd := exec.CommandContext(ctx, env.Lookup(envvars, args[0]), cmdArgs...)
203+
cmd.Env = envvars
197204
return cmd, stop, nil
198205
}

pkg/env/env.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package env
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"path/filepath"
7+
"strings"
8+
)
9+
10+
func execEquals(bin, check string) bool {
11+
return bin == check ||
12+
bin == check+".exe"
13+
}
14+
15+
func Matches(cmd []string, bin string) bool {
16+
switch len(cmd) {
17+
case 0:
18+
return false
19+
case 1:
20+
return execEquals(cmd[0], bin)
21+
}
22+
if cmd[0] == bin {
23+
return true
24+
}
25+
if cmd[0] == "/usr/bin/env" || cmd[0] == "/bin/env" {
26+
return execEquals(cmd[1], bin)
27+
}
28+
return false
29+
}
30+
31+
func AppendPath(env []string, binPath string) []string {
32+
var newEnv []string
33+
for _, path := range env {
34+
v, ok := strings.CutPrefix(path, "PATH=")
35+
if ok {
36+
newEnv = append(newEnv, fmt.Sprintf("PATH=%s%s%s",
37+
binPath, string(os.PathListSeparator), v))
38+
}
39+
}
40+
return newEnv
41+
}
42+
43+
// Lookup will try to find bin in the PATH in env. It will refer to PATHEXT for Windows support.
44+
// If bin can not be resolved to anything the original bin string is returned.
45+
func Lookup(env []string, bin string) string {
46+
for _, env := range env {
47+
for _, prefix := range []string{"PATH=", "Path="} {
48+
suffix, ok := strings.CutPrefix(env, prefix)
49+
if !ok {
50+
continue
51+
}
52+
for _, path := range strings.Split(suffix, string(os.PathListSeparator)) {
53+
testPath := filepath.Join(path, bin)
54+
55+
if stat, err := os.Stat(testPath); err == nil && !stat.IsDir() {
56+
return testPath
57+
}
58+
59+
for _, ext := range strings.Split(os.Getenv("PATHEXT"), string(os.PathListSeparator)) {
60+
if ext == "" {
61+
continue
62+
}
63+
64+
if stat, err := os.Stat(testPath + ext); err == nil && !stat.IsDir() {
65+
return testPath + ext
66+
}
67+
}
68+
}
69+
}
70+
}
71+
72+
return bin
73+
}

pkg/loader/url.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
"fmt"
66
"net/http"
77
url2 "net/url"
8-
"path/filepath"
8+
"path"
99
"strings"
1010

1111
"github.com/gptscript-ai/gptscript/pkg/types"
@@ -32,9 +32,9 @@ func loadURL(ctx context.Context, base *source, name string) (*source, bool, err
3232

3333
if base.Repo != nil {
3434
newRepo := *base.Repo
35-
newPath := filepath.Join(newRepo.Path, name)
36-
newRepo.Path = filepath.Dir(newPath)
37-
newRepo.Name = filepath.Base(newPath)
35+
newPath := path.Join(newRepo.Path, name)
36+
newRepo.Path = path.Dir(newPath)
37+
newRepo.Name = path.Base(newPath)
3838
repo = &newRepo
3939
}
4040

@@ -61,10 +61,10 @@ func loadURL(ctx context.Context, base *source, name string) (*source, bool, err
6161
}
6262

6363
pathURL := *parsed
64-
pathURL.Path = filepath.Dir(parsed.Path)
65-
path := pathURL.String()
66-
name = filepath.Base(parsed.Path)
67-
url = path + "/" + name
64+
pathURL.Path = path.Dir(parsed.Path)
65+
pathString := pathURL.String()
66+
name = path.Base(parsed.Path)
67+
url = pathString + "/" + name
6868

6969
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
7070
if err != nil {
@@ -83,7 +83,7 @@ func loadURL(ctx context.Context, base *source, name string) (*source, bool, err
8383
return &source{
8484
Content: resp.Body,
8585
Remote: true,
86-
Path: path,
86+
Path: pathString,
8787
Name: name,
8888
Location: url,
8989
Repo: repo,

pkg/repos/download/extract.go

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,43 @@ func Extract(ctx context.Context, downloadURL, digest, targetDir string) error {
2424
return fmt.Errorf("mkdir %s: %w", targetDir, err)
2525
}
2626

27+
tmpFile, err := os.CreateTemp("", "gptscript-download")
28+
if err != nil {
29+
return err
30+
}
31+
defer os.Remove(tmpFile.Name())
32+
defer tmpFile.Close()
33+
2734
resp, err := http.Get(downloadURL)
2835
if err != nil {
2936
return err
3037
}
3138
defer resp.Body.Close()
3239

33-
// NOTE: Because I'm validating the hash at the same time as extracting this isn't actually secure.
34-
// Security is still assumed the source is trusted. Which is bad and should be changed.
3540
digester := sha256.New()
3641
input := io.TeeReader(resp.Body, digester)
3742

43+
if _, err = io.Copy(tmpFile, input); err != nil {
44+
return err
45+
}
46+
47+
resultDigest := digester.Sum(nil)
48+
resultDigestString := hex.EncodeToString(resultDigest[:])
49+
50+
if resultDigestString != digest {
51+
return fmt.Errorf("downloaded %s and expected digest %s but got %s", downloadURL, digest, resultDigestString)
52+
}
53+
3854
parsedURL, err := url.Parse(downloadURL)
3955
if err != nil {
4056
return err
4157
}
4258

43-
format, input, err := archiver.Identify(filepath.Base(parsedURL.Path), input)
59+
if _, err := tmpFile.Seek(0, 0); err != nil {
60+
return err
61+
}
62+
63+
format, input, err := archiver.Identify(filepath.Base(parsedURL.Path), tmpFile)
4464
if err != nil {
4565
return err
4666
}
@@ -90,12 +110,5 @@ func Extract(ctx context.Context, downloadURL, digest, targetDir string) error {
90110
return err
91111
}
92112

93-
resultDigest := digester.Sum(nil)
94-
resultDigestString := hex.EncodeToString(resultDigest[:])
95-
96-
if resultDigestString != digest {
97-
return fmt.Errorf("downloaded %s and expected digest %s but got %s", downloadURL, digest, resultDigestString)
98-
}
99-
100113
return nil
101114
}

pkg/repos/get.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ func (m *Manager) setup(ctx context.Context, runtime Runtime, tool types.Tool, e
9393
return "", nil, err
9494
}
9595

96+
if err := out.Close(); err != nil {
97+
return "", nil, err
98+
}
99+
96100
return targetFinal, append(env, newEnv...), os.Rename(doneFile+".tmp", doneFile)
97101
}
98102

pkg/repos/runtimes/env/env.go

Lines changed: 0 additions & 40 deletions
This file was deleted.

pkg/repos/runtimes/golang/golang.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ import (
1414
"strings"
1515

1616
"github.com/gptscript-ai/gptscript/pkg/debugcmd"
17+
runtimeEnv "github.com/gptscript-ai/gptscript/pkg/env"
1718
"github.com/gptscript-ai/gptscript/pkg/hash"
1819
"github.com/gptscript-ai/gptscript/pkg/repos/download"
19-
runtimeEnv "github.com/gptscript-ai/gptscript/pkg/repos/runtimes/env"
2020
)
2121

2222
//go:embed digests.txt

pkg/repos/runtimes/node/node.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ import (
1414
"strings"
1515

1616
"github.com/gptscript-ai/gptscript/pkg/debugcmd"
17+
runtimeEnv "github.com/gptscript-ai/gptscript/pkg/env"
1718
"github.com/gptscript-ai/gptscript/pkg/hash"
1819
"github.com/gptscript-ai/gptscript/pkg/repos/download"
19-
runtimeEnv "github.com/gptscript-ai/gptscript/pkg/repos/runtimes/env"
2020
)
2121

2222
//go:embed SHASUMS256.txt.asc
@@ -84,7 +84,7 @@ func arch() string {
8484

8585
func (r *Runtime) getReleaseAndDigest() (string, string, error) {
8686
scanner := bufio.NewScanner(bytes.NewReader(releasesData))
87-
key := osName() + "-" + arch()
87+
key := "-" + osName() + "-" + arch()
8888
for scanner.Scan() {
8989
line := scanner.Text()
9090
if strings.Contains(line, "node-v"+r.Version) && strings.Contains(line, key) {
@@ -116,6 +116,11 @@ func (r *Runtime) binDir(rel string) (string, error) {
116116

117117
for _, entry := range entries {
118118
if entry.IsDir() {
119+
if _, err := os.Stat(filepath.Join(rel, entry.Name(), "node.exe")); err == nil {
120+
return filepath.Join(rel, entry.Name()), nil
121+
} else if !errors.Is(err, fs.ErrNotExist) {
122+
return "", err
123+
}
119124
return filepath.Join(rel, entry.Name(), "bin"), nil
120125
}
121126
}

pkg/repos/runtimes/python/python.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ import (
1313
"runtime"
1414

1515
"github.com/gptscript-ai/gptscript/pkg/debugcmd"
16+
runtimeEnv "github.com/gptscript-ai/gptscript/pkg/env"
1617
"github.com/gptscript-ai/gptscript/pkg/hash"
1718
"github.com/gptscript-ai/gptscript/pkg/repos/download"
18-
runtimeEnv "github.com/gptscript-ai/gptscript/pkg/repos/runtimes/env"
1919
)
2020

2121
//go:embed python.json

0 commit comments

Comments
 (0)