github.com/ActiveState/cli@v0.0.0-20240508170324-6801f60cd051/pkg/platform/runtime/setup/implementations/camel/metadata.go (about) 1 package camel 2 3 import ( 4 "encoding/json" 5 "os" 6 "path/filepath" 7 "strings" 8 9 "github.com/ActiveState/cli/internal/constants" 10 "github.com/ActiveState/cli/internal/errs" 11 "github.com/ActiveState/cli/internal/fileutils" 12 "github.com/ActiveState/cli/internal/logging" 13 ) 14 15 type ErrMetaData struct{ *errs.WrapperError } 16 17 // TargetedRelocation is a relocation instruction for files in a specific directory 18 type TargetedRelocation struct { 19 // InDir is the directory in which files need to be relocated 20 InDir string `json:"dir"` 21 // SearchString to be replaced 22 SearchString string `json:"search"` 23 // Replacement is the replacement string 24 Replacement string `json:"replace"` 25 } 26 27 // MetaData is used to parse the metadata.json file 28 type MetaData struct { 29 // InstallDir is the root directory of the artifact files that we need to copy on the user's machine 30 InstallDir string 31 32 // AffectedEnv is an environment variable that we should ensure is not set, as it might conflict with the artifact 33 AffectedEnv string `json:"affected_env"` 34 35 // Env is a key value map containing all the env vars, values can contain the RelocationDir value (which will be replaced) 36 Env map[string]string `json:"env"` 37 38 // PathListEnv is a key value map containing all env vars, where the value is a list of paths that we have to prepend to the existing environment 39 PathListEnv map[string]string `json:"path_list_env"` 40 41 // BinaryLocations are locations that we should add to the PATH 42 BinaryLocations []MetaDataBinary `json:"binaries_in"` 43 44 // RelocationDir is the string that we should replace with the actual install dir of the artifact 45 RelocationDir string `json:"relocation_dir"` 46 47 // LibLocation is the place in which .so and .dll files are stored (which binary files will need relocated) 48 RelocationTargetBinaries string `json:"relocation_target_binaries"` 49 50 // TargetedRelocations are relocations that only target specific parts of the installation 51 TargetedRelocations []TargetedRelocation `json:"custom_relocations"` 52 } 53 54 // MetaDataBinary is used to represent a binary path contained within the metadata.json file 55 type MetaDataBinary struct { 56 Path string `json:"path"` 57 Relative bool 58 59 // RelativeInt is used to unmarshal the 'relative' boolean, which is given as a 0 or a 1, which Go's 60 // json package doesn't recognize as bools. 61 // Don't use this field, use Relative instead. 62 RelativeInt int `json:"relative"` 63 } 64 65 // InitMetaData will create an instance of MetaData based on the metadata.json file found under the given artifact install dir 66 func InitMetaData(rootDir string) (*MetaData, error) { 67 var metaData *MetaData 68 metaFile := filepath.Join(rootDir, "support", constants.RuntimeMetaFile) 69 if fileutils.FileExists(metaFile) { 70 contents, err := fileutils.ReadFile(metaFile) 71 if err != nil { 72 return nil, err 73 } 74 75 metaData, err = ParseMetaData(contents) 76 if err != nil { 77 return nil, err 78 } 79 } else { 80 metaData = &MetaData{} 81 } 82 83 if metaData.Env == nil { 84 metaData.Env = map[string]string{} 85 } 86 87 if metaData.PathListEnv == nil { 88 metaData.PathListEnv = map[string]string{} 89 } 90 91 var relInstallDir string 92 installDirs := strings.Split(constants.RuntimeInstallDirs, ",") 93 for _, dir := range installDirs { 94 if fileutils.DirExists(filepath.Join(rootDir, dir)) { 95 relInstallDir = dir 96 } 97 } 98 99 if relInstallDir == "" { 100 logging.Debug("Did not find an installation directory relative to metadata file.") 101 } 102 103 metaData.InstallDir = relInstallDir 104 err := metaData.Prepare(filepath.Join(rootDir, relInstallDir)) 105 if err != nil { 106 return nil, err 107 } 108 109 return metaData, nil 110 } 111 112 // ParseMetaData will parse the given bytes into the MetaData struct 113 func ParseMetaData(contents []byte) (*MetaData, error) { 114 metaData := &MetaData{ 115 Env: make(map[string]string), 116 } 117 err := json.Unmarshal(contents, metaData) 118 if err != nil { 119 return nil, &ErrMetaData{errs.Wrap(err, "Unmarshal failed")} 120 } 121 122 // The JSON decoder does not recognize 0 and 1 as bools, so we have to get crafty 123 for k := range metaData.BinaryLocations { 124 metaData.BinaryLocations[k].Relative = metaData.BinaryLocations[k].RelativeInt == 1 125 } 126 127 return metaData, nil 128 } 129 130 func (m *MetaData) hasBinaryFile(root string, executable string) bool { 131 for _, dir := range m.BinaryLocations { 132 parent := "" 133 if dir.Relative { 134 parent = root 135 } 136 bin := filepath.Join(parent, dir.Path, executable) 137 if fileutils.FileExists(bin) { 138 return true 139 } 140 } 141 142 return false 143 } 144 145 func (m *MetaData) setPythonEnv() { 146 // This is broken for two reasons: 147 // 1. Checking in the OS environment will only happen on installation, but at a later point, the OS environment might have changed, and we will overwrite the user's choice here 148 // 2. python code does not need to depend on PYTHONIOENCODING as pointed out here: https://stackoverflow.com/a/9942822 149 // Follow up story is here: https://www.pivotaltracker.com/story/show/177407383 150 if os.Getenv("PYTHONIOENCODING") == "" { 151 m.Env["PYTHONIOENCODING"] = "utf-8" 152 } else { 153 logging.Debug("Not setting PYTHONIOENCODING as the user already has it set") 154 } 155 }