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  }