github.com/c3pm-labs/c3pm@v0.3.0/ctpm/add.go (about)

     1  package ctpm
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/Masterminds/semver/v3"
     6  	"github.com/c3pm-labs/c3pm/config"
     7  	"github.com/c3pm-labs/c3pm/config/manifest"
     8  	"github.com/c3pm-labs/c3pm/dependencies"
     9  	"github.com/c3pm-labs/c3pm/env"
    10  	"github.com/c3pm-labs/c3pm/registry"
    11  	"regexp"
    12  	"strings"
    13  )
    14  
    15  func Add(pc *config.ProjectConfig, opts AddOptions) error {
    16  	options := buildOptions(opts)
    17  	for _, dep := range opts.Dependencies {
    18  		name, version, err := getRequiredVersion(dep, options)
    19  		if err != nil {
    20  			return fmt.Errorf("error getting dependencies: %w", err)
    21  		}
    22  		_, err = dependencies.Install(dependencies.PackageRequest{Name: name, Version: version.String()}, InstallHandler{})
    23  		if err != nil {
    24  			return fmt.Errorf("error adding %s: %w", dep, err)
    25  		}
    26  		libPath := config.LibCachePath(name, version.String())
    27  		libPc, err := config.Load(libPath)
    28  		if err != nil {
    29  			return fmt.Errorf("failed to read c3pm.yml: %w", err)
    30  		}
    31  		err = Build(libPc)
    32  		if err != nil {
    33  			return err
    34  		}
    35  		if pc.Manifest.Dependencies == nil {
    36  			pc.Manifest.Dependencies = make(manifest.Dependencies)
    37  		}
    38  		pc.Manifest.Dependencies[name] = version.String()
    39  	}
    40  	if err := pc.Save(); err != nil {
    41  		return fmt.Errorf("error saving project config: %w", err)
    42  	}
    43  	return nil
    44  }
    45  
    46  type AddOptions struct {
    47  	Force       bool
    48  	RegistryURL string
    49  
    50  	Dependencies []string
    51  }
    52  
    53  func buildOptions(opts AddOptions) AddOptions {
    54  	if opts.RegistryURL == "" {
    55  		opts.RegistryURL = env.REGISTRY_ENDPOINT
    56  	}
    57  	return opts
    58  }
    59  
    60  const depRegexString = `^[\-a-zA-Z0-9_\/]*(@.*)?$`
    61  
    62  var depRegex *regexp.Regexp
    63  
    64  func getDepRegexp() (regex *regexp.Regexp, err error) {
    65  	if depRegex == nil {
    66  		depRegex, err = regexp.Compile(depRegexString)
    67  	}
    68  	regex = depRegex
    69  	return
    70  }
    71  
    72  func validateDependency(dep string) error {
    73  	regex, err := getDepRegexp()
    74  	if err != nil {
    75  		return fmt.Errorf("error compiling validation regexp: %w", err)
    76  	}
    77  	if regex.MatchString(dep) {
    78  		return nil
    79  	}
    80  	return fmt.Errorf("%s is not a valid dependency string", dep)
    81  }
    82  
    83  func getRequiredVersion(dep string, options AddOptions) (name string, version *semver.Version, err error) {
    84  	if err := validateDependency(dep); err != nil {
    85  		return "", nil, err
    86  	}
    87  	dependency := strings.Split(dep, "@")
    88  	if len(dependency) == 1 {
    89  		version, err = registry.GetLastVersion(dep, registry.Options{
    90  			RegistryURL: options.RegistryURL,
    91  		})
    92  		name = dep
    93  		return
    94  	}
    95  	name = dependency[0]
    96  	version, err = semver.NewVersion(dependency[1])
    97  	if err != nil {
    98  		return "", nil, fmt.Errorf("invalid semver string: %w", err)
    99  	}
   100  	return
   101  }