7
7
"encoding/json"
8
8
"errors"
9
9
"fmt"
10
+ "io"
10
11
"io/fs"
11
12
"os"
12
13
"path/filepath"
@@ -21,7 +22,7 @@ import (
21
22
//go:embed python.json
22
23
var releasesData []byte
23
24
24
- const uvVersion = "uv==0.1.15 "
25
+ const uvVersion = "uv==0.1.24 "
25
26
26
27
type Release struct {
27
28
OS string `json:"os,omitempty"`
@@ -52,13 +53,65 @@ func (r *Runtime) Supports(cmd []string) bool {
52
53
return runtimeEnv .Matches (cmd , "python" ) || runtimeEnv .Matches (cmd , "python3" )
53
54
}
54
55
56
+ func pythonCmd (base string ) string {
57
+ if runtime .GOOS == "windows" {
58
+ return filepath .Join (base , "python.exe" )
59
+ }
60
+ return filepath .Join (base , "python3" )
61
+ }
62
+
63
+ func pythonBin (base string ) string {
64
+ binDir := filepath .Join (base , "python" )
65
+ if runtime .GOOS != "windows" {
66
+ binDir = filepath .Join (binDir , "bin" )
67
+ }
68
+ return binDir
69
+ }
70
+
71
+ func uvBin (binDir string ) string {
72
+ if runtime .GOOS == "windows" {
73
+ return filepath .Join (binDir , "Scripts" , "uv" )
74
+ }
75
+ return filepath .Join (binDir , "uv" )
76
+ }
77
+
55
78
func (r * Runtime ) installVenv (ctx context.Context , binDir , venvPath string ) error {
56
79
log .Infof ("Creating virtualenv in %s" , venvPath )
57
- cmd := debugcmd .New (ctx , filepath .Join (binDir , "uv" ), "venv" , "-p" ,
58
- filepath .Join (binDir , "python3" ), venvPath )
80
+ cmd := debugcmd .New (ctx , uvBin (binDir ), "venv" , "-p" , pythonCmd (binDir ), venvPath )
59
81
return cmd .Run ()
60
82
}
61
83
84
+ func copyFile (to , from string ) error {
85
+ in , err := os .Open (from )
86
+ if err != nil {
87
+ return err
88
+ }
89
+ defer in .Close ()
90
+
91
+ out , err := os .Create (to )
92
+ if err != nil {
93
+ _ = out .Close ()
94
+ return err
95
+ }
96
+ defer out .Close ()
97
+
98
+ if _ , err := io .Copy (out , in ); err != nil {
99
+ return fmt .Errorf ("copying %s => %s" , from , to )
100
+ }
101
+
102
+ return nil
103
+ }
104
+
105
+ func (r * Runtime ) copyPythonForWindows (binDir string ) error {
106
+ for _ , targetBin := range []string {"python3.exe" , "python" + r .ID () + ".exe" } {
107
+ err := copyFile (filepath .Join (binDir , targetBin ), filepath .Join (binDir , "python.exe" ))
108
+ if err != nil {
109
+ return err
110
+ }
111
+ }
112
+ return nil
113
+ }
114
+
62
115
func (r * Runtime ) Setup (ctx context.Context , dataRoot , toolSource string , env []string ) ([]string , error ) {
63
116
binPath , err := r .getRuntime (ctx , dataRoot )
64
117
if err != nil {
@@ -67,6 +120,9 @@ func (r *Runtime) Setup(ctx context.Context, dataRoot, toolSource string, env []
67
120
68
121
venvPath := filepath .Join (dataRoot , "venv" , hash .ID (binPath , toolSource ))
69
122
venvBinPath := filepath .Join (venvPath , "bin" )
123
+ if runtime .GOOS == "windows" {
124
+ venvBinPath = filepath .Join (venvPath , "Scripts" )
125
+ }
70
126
71
127
// Cleanup failed runs
72
128
if err := os .RemoveAll (venvPath ); err != nil {
@@ -80,6 +136,12 @@ func (r *Runtime) Setup(ctx context.Context, dataRoot, toolSource string, env []
80
136
newEnv := runtimeEnv .AppendPath (env , venvBinPath )
81
137
newEnv = append (newEnv , "VIRTUAL_ENV=" + venvPath )
82
138
139
+ if runtime .GOOS == "windows" {
140
+ if err := r .copyPythonForWindows (venvBinPath ); err != nil {
141
+ return nil , err
142
+ }
143
+ }
144
+
83
145
if err := r .runPip (ctx , toolSource , binPath , append (env , newEnv ... )); err != nil {
84
146
return nil , err
85
147
}
@@ -110,7 +172,7 @@ func (r *Runtime) runPip(ctx context.Context, toolSource, binDir string, env []s
110
172
for _ , req := range []string {"requirements-gptscript.txt" , "requirements.txt" } {
111
173
reqFile := filepath .Join (toolSource , req )
112
174
if s , err := os .Stat (reqFile ); err == nil && ! s .IsDir () {
113
- cmd := debugcmd .New (ctx , filepath . Join (binDir , "uv" ), "pip" , "install" , "-r" , reqFile )
175
+ cmd := debugcmd .New (ctx , uvBin (binDir ), "pip" , "install" , "-r" , reqFile )
114
176
cmd .Env = env
115
177
return cmd .Run ()
116
178
}
@@ -120,9 +182,7 @@ func (r *Runtime) runPip(ctx context.Context, toolSource, binDir string, env []s
120
182
}
121
183
122
184
func (r * Runtime ) setupUV (ctx context.Context , tmp string ) error {
123
- cmd := debugcmd .New (ctx , filepath .Join (tmp , "python" , "bin" , "python3" ),
124
- filepath .Join (tmp , "python" , "bin" , "pip" ),
125
- "install" , uvVersion )
185
+ cmd := debugcmd .New (ctx , pythonCmd (tmp ), "-m" , "pip" , "install" , uvVersion )
126
186
return cmd .Run ()
127
187
}
128
188
@@ -133,7 +193,7 @@ func (r *Runtime) getRuntime(ctx context.Context, cwd string) (string, error) {
133
193
}
134
194
135
195
target := filepath .Join (cwd , "python" , hash .ID (url , sha , uvVersion ))
136
- binDir := filepath . Join (target , "python" , "bin" )
196
+ binDir := pythonBin (target )
137
197
if _ , err := os .Stat (target ); err == nil {
138
198
return binDir , nil
139
199
} else if ! errors .Is (err , fs .ErrNotExist ) {
@@ -152,7 +212,7 @@ func (r *Runtime) getRuntime(ctx context.Context, cwd string) (string, error) {
152
212
return "" , err
153
213
}
154
214
155
- if err := r .setupUV (ctx , tmp ); err != nil {
215
+ if err := r .setupUV (ctx , pythonBin ( tmp ) ); err != nil {
156
216
return "" , err
157
217
}
158
218
0 commit comments