Skip to content

Commit

Permalink
Merge pull request #40 from fossas/alex/managed-builds
Browse files Browse the repository at this point in the history
Managed builds support added
  • Loading branch information
xizhao authored Mar 2, 2018
2 parents c3aa016 + 3105635 commit d0720f8
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 41 deletions.
7 changes: 4 additions & 3 deletions .fossa.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ cli:
# # Defaults to https://app.fossa.io
# server: https://fossa.on-prem
# api_key: some-key-here
# # If `project` or `locator` are unset, infer locator from VCS.
# project: git+github.com/fossas/fossa-cli
# locator: git+github.com/fossas/fossa-cli$revision
# # If you're uploading to an existing git project, or a custom project in FOSSA
# fetcher: custom
# # If `project` is unset infer from VCS.
# project: fossa-proj

analyze:
# ignores:
Expand Down
5 changes: 5 additions & 0 deletions cmd/fossa/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ var mainLogger = logging.MustGetLogger("main")

const (
configUsage = "path to config file (default: .fossa.{yml,yaml})"
fetcherUsage = "type of fetcher to use for fossa. Default's to custom"
projectUsage = "the FOSSA project name (default: VCS remote 'origin')"
revisionUsage = "the FOSSA project's revision hash (default: VCS hash HEAD)"
endpointUsage = "the FOSSA server endpoint (default: https://app.fossa.io)"
Expand All @@ -46,6 +47,7 @@ func main() {
cli.StringFlag{Name: "r, revision", Usage: revisionUsage},
cli.StringFlag{Name: "e, endpoint", Usage: endpointUsage},
cli.StringFlag{Name: "m, modules", Usage: "the modules to build and analyze"},
cli.StringFlag{Name: "fetcher", Usage: fetcherUsage},
cli.BoolFlag{Name: "o, output", Usage: analyzeOutputUsage},
cli.BoolFlag{Name: "allow-unresolved", Usage: analyzeAllowResolvedUsage},
cli.BoolFlag{Name: "b, build", Usage: "run a default build in module directories if they have not been pre-built"},
Expand Down Expand Up @@ -82,6 +84,7 @@ func main() {
Action: analyzeCmd,
Flags: []cli.Flag{
cli.StringFlag{Name: "c, config", Usage: configUsage},
cli.StringFlag{Name: "fetcher", Usage: fetcherUsage},
cli.StringFlag{Name: "p, project", Usage: projectUsage},
cli.StringFlag{Name: "r, revision", Usage: revisionUsage},
cli.StringFlag{Name: "e, endpoint", Usage: endpointUsage},
Expand All @@ -97,6 +100,7 @@ func main() {
Action: testCmd,
Flags: []cli.Flag{
cli.StringFlag{Name: "c, config", Usage: configUsage},
cli.StringFlag{Name: "fetcher", Usage: fetcherUsage},
cli.StringFlag{Name: "p, project", Usage: projectUsage},
cli.StringFlag{Name: "r, revision", Usage: revisionUsage},
cli.StringFlag{Name: "e, endpoint", Usage: endpointUsage},
Expand All @@ -110,6 +114,7 @@ func main() {
Action: uploadCmd,
Flags: []cli.Flag{
cli.StringFlag{Name: "c, config", Usage: configUsage},
cli.StringFlag{Name: "fetcher", Usage: fetcherUsage},
cli.StringFlag{Name: "p, project", Usage: projectUsage},
cli.StringFlag{Name: "r, revision", Usage: revisionUsage},
cli.StringFlag{Name: "e, endpoint", Usage: endpointUsage},
Expand Down
16 changes: 8 additions & 8 deletions cmd/fossa/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ type buildResponse struct {
}
}

func getBuild(endpoint, apiKey, project, revision string) (buildResponse, error) {
func getBuild(endpoint, apiKey, fetcher, project, revision string) (buildResponse, error) {
server, err := url.Parse(endpoint)
if err != nil {
return buildResponse{}, fmt.Errorf("invalid FOSSA endpoint: %s", err.Error())
}

buildsURL, err := url.Parse(fmt.Sprintf(buildsEndpoint, url.PathEscape(config.MakeLocator(project, revision))))
buildsURL, err := url.Parse(fmt.Sprintf(buildsEndpoint, url.PathEscape(config.MakeLocator(fetcher, project, revision))))
if err != nil {
return buildResponse{}, fmt.Errorf("invalid FOSSA builds endpoint: %s", err.Error())
}
Expand Down Expand Up @@ -72,13 +72,13 @@ type issueResponse struct {
// RevisionID string // TODO: We need to get this information from /api/issues?fromRevision=
}

func getRevision(endpoint, apiKey, project, revision string) (revisionResponse, error) {
func getRevision(endpoint, apiKey, fetcher, project, revision string) (revisionResponse, error) {
server, err := url.Parse(endpoint)
if err != nil {
return revisionResponse{}, fmt.Errorf("invalid FOSSA endpoint: %s", err.Error())
}

revisionsURL, err := url.Parse(fmt.Sprintf(revisionsEndpoint, url.PathEscape(config.MakeLocator(project, revision))))
revisionsURL, err := url.Parse(fmt.Sprintf(revisionsEndpoint, url.PathEscape(config.MakeLocator(fetcher, project, revision))))
if err != nil {
return revisionResponse{}, fmt.Errorf("invalid FOSSA issues endpoint: %s", err.Error())
}
Expand All @@ -98,13 +98,13 @@ func getRevision(endpoint, apiKey, project, revision string) (revisionResponse,
return revisionJSON, nil
}

func doTest(s *spinner.Spinner, race chan testResult, endpoint, apiKey, project, revision string) {
func doTest(s *spinner.Spinner, race chan testResult, endpoint, apiKey, fetcher, project, revision string) {
s.Suffix = " Waiting for analysis to complete..."
s.Start()

buildLoop:
for {
build, err := getBuild(endpoint, apiKey, project, revision)
build, err := getBuild(endpoint, apiKey, fetcher, project, revision)
if isTimeout(err) {
time.Sleep(pollRequestDelay)
continue
Expand All @@ -129,7 +129,7 @@ buildLoop:
s.Restart()

for {
revision, err := getRevision(endpoint, apiKey, project, revision)
revision, err := getRevision(endpoint, apiKey, fetcher, project, revision)
if err != nil {
race <- testResult{err: err, issues: nil}
return
Expand Down Expand Up @@ -157,7 +157,7 @@ func testCmd(c *cli.Context) {
s := spinner.New(spinner.CharSets[11], 100*time.Millisecond)
s.Writer = os.Stderr
race := make(chan testResult, 1)
go doTest(s, race, conf.Endpoint, conf.APIKey, conf.Project, conf.Revision)
go doTest(s, race, conf.Endpoint, conf.APIKey, conf.Fetcher, conf.Project, conf.Revision)
select {
case result := <-race:
s.Stop()
Expand Down
3 changes: 3 additions & 0 deletions cmd/fossa/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ const updateEndpoint = "fossas/fossa-cli"
var updateLogger = logging.MustGetLogger("update")

func getSemver(v string) string {
if v == "" {
return ""
}
if v[0] != 'v' {
return ""
}
Expand Down
17 changes: 11 additions & 6 deletions cmd/fossa/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,12 +134,12 @@ func doUpload(conf config.CLIConfig, results []normalizedModule) (string, error)
return "", fmt.Errorf("invalid FOSSA endpoint")
}

if conf.Revision == "" {
analysisLogger.Fatal("no revision found in working directory; try running in a git repo or passing a locator")
if conf.Project == "" {
analysisLogger.Fatal("Could not infer project name from either `.fossa.yml` or `git` remote named `origin`")
}

if conf.Project == "" {
analysisLogger.Fatal("could not infer project name from either `.fossa.yaml` or `git` remote named `origin`")
if conf.Fetcher != "custom" && conf.Revision == "" {
analysisLogger.Fatal("Could not infer revision name from `git` remote named `origin`. To submit a custom project, set Fetcher to `custom` in `.fossa.yml`")
}

// Re-marshal into build data
Expand All @@ -150,7 +150,12 @@ func doUpload(conf config.CLIConfig, results []normalizedModule) (string, error)

analysisLogger.Debugf("Uploading build data from (%#v) modules: %#v", len(results), string(buildData))

postRef, _ := url.Parse("/api/builds/custom?locator=" + url.QueryEscape(config.MakeLocator(conf.Project, conf.Revision)) + "&v=" + version)
fossaEndpoint := "/api/builds/custom?locator=" + url.QueryEscape(config.MakeLocator(conf.Fetcher, conf.Project, conf.Revision)) + "&v=" + version
if conf.Fetcher == "custom" {
fossaEndpoint += "&managedBuild=true"
}

postRef, _ := url.Parse(fossaEndpoint)
postURL := fossaBaseURL.ResolveReference(postRef).String()

analysisLogger.Debugf("Sending build data to <%#v>", postURL)
Expand All @@ -172,7 +177,7 @@ func doUpload(conf config.CLIConfig, results []normalizedModule) (string, error)
return "", fmt.Errorf("invalid API key (check the $FOSSA_API_KEY environment variable)")
} else if resp.StatusCode == http.StatusPreconditionRequired {
// TODO: handle "Managed Project" workflow
return "", fmt.Errorf("invalid project or revision; make sure this version is published and FOSSA has access to your repo")
return "", fmt.Errorf("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`")
} else if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("bad server response (%#v)", responseStr)
}
Expand Down
25 changes: 11 additions & 14 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ type UploadConfig struct {
// CLIConfig specifies the config available to the cli
type CLIConfig struct {
APIKey string
Fetcher string
Project string
Revision string
Endpoint string
Expand All @@ -58,7 +59,10 @@ type CLIConfig struct {
}

// MakeLocator creates a locator string given a package and revision
func MakeLocator(project string, revision string) string {
func MakeLocator(fetcher string, project string, revision string) string {
if fetcher != "git" {
return fetcher + "+" + project + "$" + revision
}
// Remove fetcher prefix (in case project is derived from splitting a locator on '$')
noFetcherPrefix := strings.TrimPrefix(project, "git+")

Expand Down Expand Up @@ -104,6 +108,7 @@ func New(c *cli.Context) (CLIConfig, error) {

var config = CLIConfig{
APIKey: c.String("api_key"),
Fetcher: c.String("fetcher"),
Project: c.String("project"),
Revision: c.String("revision"),
Endpoint: c.String("endpoint"),
Expand Down Expand Up @@ -141,23 +146,15 @@ func New(c *cli.Context) (CLIConfig, error) {
return CLIConfig{}, err
}

var locatorSections []string
var locatorProject string
var locatorRevision string

if configFile.CLI.Locator != "" {
locatorSections = strings.Split(configFile.CLI.Locator, "$")
locatorProject = strings.TrimPrefix(locatorSections[0], "git+")
locatorRevision = locatorSections[1]
}
if config.Project == "" {
config.Project = configFile.CLI.Project
if config.Project == "" {
config.Project = locatorProject
}
}
if config.Revision == "" {
config.Revision = locatorRevision
config.Revision = configFile.CLI.Revision
}

if config.Fetcher == "" {
config.Fetcher = configFile.CLI.Fetcher
}

if config.APIKey == "" {
Expand Down
28 changes: 18 additions & 10 deletions config/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ type configFileV1 struct {

type configFileCLIV1 struct {
// Upload configuration.
APIKey string `yaml:"api_key,omitempty"`
Server string `yaml:"server,omitempty"`
Project string `yaml:"project,omitempty"`
Locator string `yaml:"locator,omitempty"`
APIKey string `yaml:"api_key,omitempty"`
Server string `yaml:"server,omitempty"`
Project string `yaml:"project,omitempty"`
Revision string `yaml:"revision,omitempty"`
Fetcher string `yaml:"fetcher,omitempty"` // fetcher defaults to custom
}

type configFileAnalyzeV1 struct {
Expand Down Expand Up @@ -86,8 +87,13 @@ func setDefaultValues(c configFileV1) (configFileV1, error) {
c.CLI.APIKey = apiKey
}

// Default to custom.
if c.CLI.Fetcher == "" {
c.CLI.Fetcher = "custom"
}

// Infer default locator and project from `git`.
if c.CLI.Locator == "" {
if c.CLI.Project == "" || c.CLI.Revision == "" {
// TODO: this needs to happen in the module directory, not the working
// directory
repo, err := git.PlainOpen(".")
Expand All @@ -100,10 +106,12 @@ func setDefaultValues(c configFileV1) (configFileV1, error) {
c.CLI.Project = project
}
}

revision, err := repo.Head()
if err == nil {
c.CLI.Locator = "git+" + project + "$" + revision.Hash().String()
revision := c.CLI.Revision
if revision == "" {
revision, err := repo.Head()
if err == nil {
c.CLI.Revision = revision.Hash().String()
}
}
}
}
Expand All @@ -128,12 +136,12 @@ func WriteConfigFile(conf *CLIConfig) error {
APIKey: keyToWrite,
Server: conf.Endpoint,
Project: conf.Project,
Fetcher: conf.Fetcher,
},
Analyze: configFileAnalyzeV1{
Modules: conf.Modules,
},
}

yamlConfig, err := yaml.Marshal(writeConfig)
if err != nil {
return err
Expand Down

0 comments on commit d0720f8

Please sign in to comment.