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 }