github.com/joey-fossa/fossa-cli@v0.7.34-0.20190708193710-569f1e8679f0/api/fossa/upload.go (about) 1 package fossa 2 3 import ( 4 "encoding/json" 5 "net/url" 6 7 "github.com/pkg/errors" 8 9 "github.com/apex/log" 10 "github.com/fossas/fossa-cli/cmd/fossa/version" 11 ) 12 13 // Errors related to preconditions. 14 var ( 15 ErrNoProject = errors.New("no project provided for upload") 16 ErrNoRevision = errors.New("no revision provided for upload") 17 ErrNoBuildData = errors.New("no build data to upload") 18 ) 19 20 // Errors resulting from a bad API response. 21 var ( 22 ErrForbidden = errors.New("authentication failed (is the API key correct?)") 23 ErrRevisionDoesNotExist = errors.New("revision does not exist (are the project and revision correct and published in FOSSA?)") 24 ) 25 26 // UploadOptions are optional keys that provide extra metadata for an upload. 27 type UploadOptions struct { 28 Branch string 29 ProjectURL string 30 JIRAProjectKey string 31 Link string 32 Team string 33 } 34 35 // Upload uploads a project's analysis. 36 func Upload(title string, locator Locator, options UploadOptions, data []SourceUnit) (Locator, error) { 37 // Check preconditions. 38 if locator.Fetcher == "git" && locator.Revision == "" { 39 return Locator{}, errors.New("Could not infer revision name from `git` remote named `origin`. To submit a custom project, set Fetcher to `custom` in `.fossa.yml`") 40 } 41 if locator.Project == "" { 42 return Locator{}, errors.New("Could not infer project name from either `.fossa.yml` or `git` remote named `origin`") 43 } 44 if len(data) == 0 { 45 return Locator{}, errors.New("No data to upload") 46 } 47 48 payload, err := json.Marshal(data) 49 if err != nil { 50 return Locator{}, errors.Wrap(err, "could not marshal upload data") 51 } 52 log.WithFields(log.Fields{ 53 "modules": data, 54 "payload": string(payload), 55 }).Debug("uploading build") 56 57 q := url.Values{} 58 q.Add("locator", locator.String()) 59 q.Add("v", version.ShortString()) 60 if locator.Fetcher == "custom" { 61 q.Add("managedBuild", "true") 62 q.Add("title", title) 63 } 64 65 if options.Branch != "" { 66 q.Add("branch", options.Branch) 67 } 68 if options.ProjectURL != "" { 69 q.Add("projectURL", options.ProjectURL) 70 } 71 if options.JIRAProjectKey != "" { 72 q.Add("jiraProjectKey", options.JIRAProjectKey) 73 } 74 if options.Link != "" { 75 q.Add("link", options.Link) 76 } 77 if options.Team != "" { 78 q.Add("team", options.Team) 79 } 80 81 endpoint, err := url.Parse("/api/builds/custom?" + q.Encode()) 82 if err != nil { 83 return Locator{}, errors.New("Failed to generate upload URL") 84 } 85 log.WithField("endpoint", endpoint.String()).Debug("uploading build") 86 87 res, statusCode, err := Post(endpoint.String(), payload) 88 log.WithField("response", res).Debug("build upload completed") 89 90 if statusCode == 428 { 91 // TODO: handle "Managed Project" workflow 92 return Locator{}, errors.New("invalid project or revision; make sure this version is published and FOSSA has access to your repo (to submit a custom project, set Fetcher to `custom` in `.fossa.yml`)") 93 } else if statusCode == 403 { 94 return Locator{}, errors.New("you do not have permission to upload builds for this project") 95 } else if err != nil { 96 return Locator{}, errors.Wrap(err, "could not upload") 97 } 98 log.Debug("build upload succeeded") 99 100 var unmarshalled struct { 101 Locator string 102 Error string 103 } 104 err = json.Unmarshal([]byte(res), &unmarshalled) 105 if err != nil || unmarshalled.Error != "" { 106 return Locator{}, errors.Errorf("Error response from API: %s", res) 107 } 108 109 if unmarshalled.Locator == "" { 110 return Locator{}, errors.Errorf("bad response: %s", res) 111 } 112 113 return ReadLocator(unmarshalled.Locator), nil 114 }