github.com/buildpacks/pack@v0.33.3-0.20240516162812-884dd1837311/pkg/buildpack/locator_type.go (about)

     1  package buildpack
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  	"regexp"
     8  	"strings"
     9  
    10  	"github.com/google/go-containerregistry/pkg/name"
    11  
    12  	"github.com/buildpacks/pack/internal/paths"
    13  	"github.com/buildpacks/pack/internal/style"
    14  	"github.com/buildpacks/pack/pkg/dist"
    15  )
    16  
    17  type LocatorType int
    18  
    19  const (
    20  	InvalidLocator LocatorType = iota
    21  	FromBuilderLocator
    22  	URILocator
    23  	IDLocator
    24  	PackageLocator
    25  	RegistryLocator
    26  	// added entries here should also be added to `String()`
    27  )
    28  
    29  const (
    30  	fromBuilderPrefix           = "urn:cnb:builder"
    31  	deprecatedFromBuilderPrefix = "from=builder"
    32  	fromRegistryPrefix          = "urn:cnb:registry"
    33  	fromDockerPrefix            = "docker:/"
    34  )
    35  
    36  var (
    37  	// https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
    38  	semverPattern   = `(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?`
    39  	registryPattern = regexp.MustCompile(`^[a-z0-9\-\.]+\/[a-z0-9\-\.]+(?:@` + semverPattern + `)?$`)
    40  )
    41  
    42  func (l LocatorType) String() string {
    43  	return []string{
    44  		"InvalidLocator",
    45  		"FromBuilderLocator",
    46  		"URILocator",
    47  		"IDLocator",
    48  		"PackageLocator",
    49  		"RegistryLocator",
    50  	}[l]
    51  }
    52  
    53  // GetLocatorType determines which type of locator is designated by the given input.
    54  // If a type cannot be determined, `INVALID_LOCATOR` will be returned. If an error
    55  // is encountered, it will be returned.
    56  func GetLocatorType(locator string, relativeBaseDir string, buildpacksFromBuilder []dist.ModuleInfo) (LocatorType, error) {
    57  	if locator == deprecatedFromBuilderPrefix {
    58  		return FromBuilderLocator, nil
    59  	}
    60  
    61  	if strings.HasPrefix(locator, fromBuilderPrefix+":") || strings.HasPrefix(locator, deprecatedFromBuilderPrefix+":") {
    62  		if !isFoundInBuilder(locator, buildpacksFromBuilder) {
    63  			return InvalidLocator, fmt.Errorf("%s is not a valid identifier", style.Symbol(locator))
    64  		}
    65  		return IDLocator, nil
    66  	}
    67  
    68  	if strings.HasPrefix(locator, fromRegistryPrefix+":") {
    69  		return RegistryLocator, nil
    70  	}
    71  
    72  	if paths.IsURI(locator) {
    73  		if HasDockerLocator(locator) {
    74  			if _, err := name.ParseReference(locator); err == nil {
    75  				return PackageLocator, nil
    76  			}
    77  		}
    78  		return URILocator, nil
    79  	}
    80  
    81  	return parseNakedLocator(locator, relativeBaseDir, buildpacksFromBuilder), nil
    82  }
    83  
    84  func HasDockerLocator(locator string) bool {
    85  	return strings.HasPrefix(locator, fromDockerPrefix)
    86  }
    87  
    88  func parseNakedLocator(locator, relativeBaseDir string, buildpacksFromBuilder []dist.ModuleInfo) LocatorType {
    89  	// from here on, we're dealing with a naked locator, and we try to figure out what it is. To do this we check
    90  	// the following characteristics in order:
    91  	//   1. Does it match a path on the file system
    92  	//   2. Does it match a buildpack ID in the builder
    93  	//   3. Does it look like a Buildpack Registry ID
    94  	//   4. Does it look like a Docker ref
    95  	if isLocalFile(locator, relativeBaseDir) {
    96  		return URILocator
    97  	}
    98  
    99  	if isFoundInBuilder(locator, buildpacksFromBuilder) {
   100  		return IDLocator
   101  	}
   102  
   103  	if canBeRegistryRef(locator) {
   104  		return RegistryLocator
   105  	}
   106  
   107  	if canBePackageRef(locator) {
   108  		return PackageLocator
   109  	}
   110  
   111  	return InvalidLocator
   112  }
   113  
   114  func canBePackageRef(locator string) bool {
   115  	if _, err := name.ParseReference(locator); err == nil {
   116  		return true
   117  	}
   118  
   119  	return false
   120  }
   121  
   122  func canBeRegistryRef(locator string) bool {
   123  	return registryPattern.MatchString(locator)
   124  }
   125  
   126  func isFoundInBuilder(locator string, candidates []dist.ModuleInfo) bool {
   127  	id, version := ParseIDLocator(locator)
   128  	for _, c := range candidates {
   129  		if id == c.ID && (version == "" || version == c.Version) {
   130  			return true
   131  		}
   132  	}
   133  	return false
   134  }
   135  
   136  func isLocalFile(locator, relativeBaseDir string) bool {
   137  	if !filepath.IsAbs(locator) {
   138  		locator = filepath.Join(relativeBaseDir, locator)
   139  	}
   140  
   141  	if _, err := os.Stat(locator); err == nil {
   142  		return true
   143  	}
   144  
   145  	return false
   146  }