github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/tiltfile/os/os.go (about) 1 package os 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 "runtime" 8 9 "go.starlark.net/starlark" 10 11 "github.com/tilt-dev/tilt/internal/tiltfile/starkit" 12 "github.com/tilt-dev/tilt/internal/tiltfile/value" 13 ) 14 15 // The starlark OS module. 16 // Modeled after Bazel's repository_os 17 // https://docs.bazel.build/versions/master/skylark/lib/repository_os.html 18 // and Python's OS module 19 // https://docs.python.org/3/library/os.html 20 type Plugin struct { 21 } 22 23 func NewPlugin() Plugin { 24 return Plugin{} 25 } 26 27 func (e Plugin) OnStart(env *starkit.Environment) error { 28 err := env.AddBuiltin("os.getcwd", cwd) 29 if err != nil { 30 return err 31 } 32 33 err = addPathBuiltin(env, "os.path.abspath", abspath) 34 if err != nil { 35 return err 36 } 37 err = env.AddBuiltin("os.path.relpath", relpath) 38 if err != nil { 39 return err 40 } 41 err = addPathBuiltin(env, "os.path.basename", basename) 42 if err != nil { 43 return err 44 } 45 err = addPathBuiltin(env, "os.path.dirname", dirname) 46 if err != nil { 47 return err 48 } 49 err = addPathBuiltin(env, "os.path.exists", exists) 50 if err != nil { 51 return err 52 } 53 err = env.AddBuiltin("os.path.join", join) 54 if err != nil { 55 return err 56 } 57 err = addPathBuiltin(env, "os.path.realpath", realpath) 58 if err != nil { 59 return err 60 } 61 62 err = env.AddValue("os.environ", Environ{}) 63 if err != nil { 64 return err 65 } 66 err = env.AddBuiltin("os.getenv", getenv) 67 if err != nil { 68 return err 69 } 70 err = env.AddBuiltin("os.putenv", putenv) 71 if err != nil { 72 return err 73 } 74 err = env.AddBuiltin("os.unsetenv", unsetenv) 75 if err != nil { 76 return err 77 } 78 79 return env.AddValue("os.name", starlark.String(osName())) 80 } 81 82 // For consistency with 83 // https://docs.python.org/3/library/os.html#os.name 84 func osName() string { 85 if runtime.GOOS == "windows" { 86 return "nt" 87 } 88 return "posix" 89 } 90 91 func getenv(t *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { 92 var key value.Stringable 93 var defaultVal starlark.Value = starlark.None 94 err := starkit.UnpackArgs(t, fn.Name(), args, kwargs, 95 "key", &key, 96 "default?", &defaultVal, 97 ) 98 if err != nil { 99 return nil, err 100 } 101 102 envVal, found := os.LookupEnv(key.Value) 103 if !found { 104 return defaultVal, nil 105 } 106 107 return starlark.String(envVal), nil 108 } 109 110 func putenv(t *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { 111 var key, v value.Stringable 112 err := starkit.UnpackArgs(t, fn.Name(), args, kwargs, 113 "key", &key, 114 "value", &v, 115 ) 116 if err != nil { 117 return nil, err 118 } 119 120 os.Setenv(key.Value, v.Value) 121 return starlark.None, nil 122 } 123 124 func unsetenv(t *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { 125 var key value.Stringable 126 err := starkit.UnpackArgs(t, fn.Name(), args, kwargs, 127 "key", &key, 128 ) 129 if err != nil { 130 return nil, err 131 } 132 133 os.Unsetenv(key.Value) 134 return starlark.None, nil 135 } 136 137 // Fetch the working directory of current Tiltfile execution. 138 // All built-ins will be executed relative to this directory (e.g., local(), docker_build(), etc) 139 // Intended to mirror the API of Python's getcwd 140 // https://docs.python.org/3/library/os.html#os.getcwd 141 func cwd(t *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { 142 err := starkit.UnpackArgs(t, fn.Name(), args, kwargs) 143 if err != nil { 144 return nil, err 145 } 146 147 dir := starkit.AbsWorkingDir(t) 148 return starlark.String(dir), nil 149 } 150 151 // Add a function that takes exactly one parameter, a path string. 152 func addPathBuiltin(env *starkit.Environment, name string, 153 f func(t *starlark.Thread, s string) (starlark.Value, error)) error { 154 builtin := func(t *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { 155 var path string 156 err := starkit.UnpackArgs(t, fn.Name(), args, kwargs, 157 "path", &path, 158 ) 159 if err != nil { 160 return nil, err 161 } 162 return f(t, path) 163 } 164 return env.AddBuiltin(name, starkit.Function(builtin)) 165 } 166 167 func abspath(t *starlark.Thread, path string) (starlark.Value, error) { 168 return starlark.String(starkit.AbsPath(t, path)), nil 169 } 170 171 func relpath(thread *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { 172 var targpath string 173 basepath := starkit.AbsWorkingDir(thread) // by default, relative to CWD 174 175 // Matches syntax of Python's os.path.relpath() 176 if err := starkit.UnpackArgs(thread, fn.Name(), args, kwargs, 177 "targpath", &targpath, 178 "basepath?", &basepath); err != nil { 179 return nil, err 180 } 181 relPath, err := filepath.Rel(basepath, targpath) 182 return starlark.String(relPath), err 183 } 184 185 func basename(t *starlark.Thread, path string) (starlark.Value, error) { 186 return starlark.String(filepath.Base(path)), nil 187 } 188 189 func dirname(t *starlark.Thread, path string) (starlark.Value, error) { 190 return starlark.String(filepath.Dir(path)), nil 191 } 192 193 func exists(t *starlark.Thread, path string) (starlark.Value, error) { 194 absPath := starkit.AbsPath(t, path) 195 196 _, err := os.Stat(absPath) 197 if err != nil { 198 // Return false on error (either not found errors or permission denied 199 // errors), for consistency with the python version. 200 return starlark.Bool(false), nil 201 } 202 203 return starlark.Bool(true), nil 204 } 205 206 func join(t *starlark.Thread, fn *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) { 207 parts := []string{} 208 for i, arg := range args { 209 s, ok := starlark.AsString(arg) 210 if !ok { 211 return nil, fmt.Errorf("os.path.join() only accepts strings. Argument #%d: %s", i, arg) 212 } 213 parts = append(parts, s) 214 } 215 return starlark.String(filepath.Join(parts...)), nil 216 } 217 218 func realpath(t *starlark.Thread, path string) (starlark.Value, error) { 219 realPath, err := filepath.EvalSymlinks(starkit.AbsPath(t, path)) 220 if err != nil { 221 return nil, err 222 } 223 return starlark.String(realPath), nil 224 }