github.com/ActiveState/cli@v0.0.0-20240508170324-6801f60cd051/cmd/state-exec/meta.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 "strings" 8 9 "github.com/ActiveState/cli/pkg/platform/runtime/executors/execmeta" 10 ) 11 12 const ( 13 envVarSeparator = "=" 14 pathEnvVarPrefix = "PATH" + envVarSeparator 15 pathListSeparator = string(os.PathListSeparator) 16 ) 17 18 type executorMeta struct { 19 *execmeta.ExecMeta 20 MatchingBin string 21 TransformedEnv []string 22 } 23 24 func newExecutorMeta(execPath string) (*executorMeta, error) { 25 execDir := filepath.Dir(execPath) 26 metaPath := filepath.Join(execDir, execmeta.MetaFileName) 27 meta, err := execmeta.NewFromFile(metaPath) 28 if err != nil { 29 return nil, fmt.Errorf("cannot get execmeta from file: %w", err) 30 } 31 32 matchingBin, err := matchingBinByPath(meta.Bins, execPath) 33 if err != nil { 34 return nil, fmt.Errorf("cannot get matching bin by path: %w", err) 35 } 36 37 em := executorMeta{ 38 ExecMeta: meta, 39 MatchingBin: matchingBin, 40 TransformedEnv: transformedEnv(os.Environ(), meta.Env), 41 } 42 43 return &em, nil 44 } 45 46 // matchingBinByPath receives a list of binaries (from the meta file), as well 47 // as the path to this program. The base name of the path is used to match one 48 // of the binaries that will then be forwarded to as a child process. 49 func matchingBinByPath(bins map[string]string, path string) (string, error) { 50 alias := filepath.Base(path) 51 if dest, ok := bins[alias]; ok { 52 return dest, nil 53 } 54 return "", fmt.Errorf("no matching binary by path %q", path) 55 } 56 57 // transformedEnv will update the current environment. Update entries are 58 // appended (which supersede existing entries) except for: PATH is updated with 59 // the update value prepended to the existing PATH. 60 func transformedEnv(current []string, updates []string) []string { 61 for _, update := range updates { 62 if strings.HasPrefix(strings.ToLower(update), strings.ToLower(pathEnvVarPrefix)) { 63 pathCurrentV, pathCurrentK, ok := getEnvVar(current, pathEnvVarPrefix) 64 if ok { 65 current[pathCurrentK] = update + pathListSeparator + pathCurrentV 66 continue 67 } 68 } 69 current = append(current, update) 70 } 71 return current 72 } 73 74 // getEnvVar returns the value and index of the environment variable from the 75 // provided environment slice. The prefix should end with the environment 76 // variable separator. 77 func getEnvVar(env []string, prefix string) (string, int, bool) { 78 for k, v := range env { 79 if strings.HasPrefix(strings.ToLower(v), strings.ToLower(prefix)) { 80 return v[len(prefix):], k, true 81 } 82 } 83 return "", 0, false 84 }