github.com/joey-fossa/fossa-cli@v0.7.34-0.20190708193710-569f1e8679f0/api/fossa/locator.go (about) 1 package fossa 2 3 import ( 4 "net/url" 5 "regexp" 6 "strings" 7 8 "github.com/apex/log" 9 10 "github.com/fossas/fossa-cli/config" 11 "github.com/fossas/fossa-cli/pkg" 12 ) 13 14 // Locator serializes FOSSA API locators. 15 type Locator struct { 16 Fetcher string `json:"fetcher"` 17 Project string `json:"package"` 18 Revision string `json:"revision"` 19 } 20 21 // String returns a locator converted to a string as a URL path for API access. 22 func (l Locator) String() string { 23 if l.Fetcher == "git" { 24 return "git+" + NormalizeGitURL(l.Project) + "$" + l.Revision 25 } 26 if l.Fetcher == "archive" { 27 orgID, err := GetOrganizationID() 28 if err != nil { 29 log.Warnf("Could not get OrganizationID while constructing locator") 30 } 31 l.Project = orgID + "/" + l.Project 32 } 33 return l.Fetcher + "+" + l.Project + "$" + l.Revision 34 } 35 36 // OrgString returns a locator converted to a string as a URL path for API access. 37 // The OrgID is included for custom fetchers. 38 func (l Locator) OrgString() string { 39 if l.Fetcher == "git" { 40 return "git+" + NormalizeGitURL(l.Project) + "$" + l.Revision 41 } 42 43 if l.Fetcher == "archive" || l.Fetcher == "custom" { 44 orgID, err := GetOrganizationID() 45 if err != nil { 46 log.Warnf("Could not get OrganizationID while constructing locator") 47 } 48 l.Project = orgID + "/" + NormalizeGitURLTest(l.Project) 49 } 50 51 return l.Fetcher + "+" + l.Project + "$" + l.Revision 52 } 53 54 // URL calculates the FOSSA URL for a project's locator. 55 func (l Locator) URL() string { 56 server, err := url.Parse(config.Endpoint()) 57 if err != nil { 58 log.Fatalf("Invalid FOSSA endpoint: %s", err.Error()) 59 } 60 branch := config.Branch() 61 if branch == "" { 62 branch = "master" 63 } 64 url, err := url.Parse( 65 "/projects/" + 66 url.PathEscape(l.Fetcher+"+"+l.Project) + 67 "/refs/branch/" + 68 url.PathEscape(branch) + 69 "/" + 70 url.PathEscape(l.Revision)) 71 if err != nil { 72 log.Fatalf("Invalid FOSSA URL: %s", err.Error()) 73 } 74 return server.ResolveReference(url).String() 75 } 76 77 // ReportURL provides a formatted URL. 78 func (l Locator) ReportURL() string { 79 return ` 80 ============================================================ 81 82 View FOSSA Report: 83 ` + l.URL() + ` 84 85 ============================================================ 86 ` 87 } 88 89 // IsResolved returns true only if a locator is resolved. 90 func (l Locator) IsResolved() bool { 91 return l.Revision != "" 92 } 93 94 // NormalizeGitURL normalizes all forms of git remote URLs to a single standard 95 // form. 96 func NormalizeGitURL(project string) string { 97 // Remove fetcher prefix (in case project is derived from splitting a locator on '$'). 98 noFetcherPrefix := strings.TrimPrefix(project, "git+") 99 100 // Normalize Git URL format. 101 noGitExtension := strings.TrimSuffix(noFetcherPrefix, ".git") 102 handleGitHubSSH := strings.Replace(noGitExtension, "git@github.com:", "github.com/", 1) 103 104 // Remove protocols 105 noHTTPPrefix := strings.TrimPrefix(handleGitHubSSH, "http://") 106 noHTTPSPrefix := strings.TrimPrefix(noHTTPPrefix, "https://") 107 108 return noHTTPSPrefix 109 } 110 111 // NormalizeGitURL normalizes all forms of git remote URLs to a single standard 112 // form. This works around the backend only normalizing strings starting with http. 113 // HACK until the backend and cli are more in sync 114 func NormalizeGitURLTest(project string) string { 115 // Remove fetcher prefix (in case project is derived from splitting a locator on '$'). 116 noFetcherPrefix := strings.TrimPrefix(project, "git+") 117 118 // Normalize Git URL format only if not ssh 119 if strings.HasPrefix(noFetcherPrefix, "http") { 120 noFetcherPrefix = strings.TrimSuffix(noFetcherPrefix, ".git") 121 } 122 123 // Remove protocols 124 noHTTPPrefix := strings.TrimPrefix(noFetcherPrefix, "http://") 125 noHTTPSPrefix := strings.TrimPrefix(noHTTPPrefix, "https://") 126 127 return noHTTPSPrefix 128 } 129 130 // ReadLocator parses a string locator into a Locator. 131 func ReadLocator(locator string) Locator { 132 locatorRegexp := regexp.MustCompile(`^(.*?)\+(.*?)\$(.*?)$`) 133 matches := locatorRegexp.FindStringSubmatch(locator) 134 return Locator{ 135 Fetcher: matches[1], 136 Project: matches[2], 137 Revision: matches[3], 138 } 139 } 140 141 // LocatorOf returns the locator of a pkg.ID. 142 func LocatorOf(id pkg.ID) Locator { 143 // TODO: maybe this should panic 144 if id.Type == pkg.Invalid { 145 log.Warnf("Unrecognized locator") 146 return Locator{} 147 } 148 // Normalize locator fetchers. 149 fetcher := id.Type.String() 150 switch id.Type { 151 case pkg.Composer: 152 fetcher = "comp" 153 case pkg.Gradle: 154 fetcher = "mvn" 155 case pkg.Ant: 156 fetcher = "mvn" 157 case pkg.Scala: 158 fetcher = "mvn" 159 case pkg.Haskell: 160 fetcher = "hackage" 161 } 162 163 return Locator{ 164 Fetcher: fetcher, 165 Project: id.Name, 166 Revision: id.Revision, 167 } 168 }