Skip to content

Commit

Permalink
feat(test): add test command
Browse files Browse the repository at this point in the history
  • Loading branch information
xizhao committed Feb 17, 2018
1 parent 661a7a5 commit 89dcaea
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 1 deletion.
30 changes: 30 additions & 0 deletions cmd/fossa/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package main

import (
"bytes"
"errors"
"io/ioutil"
"net/http"
)

// Common utilities among commands
func makeAPIRequest(requestType string, url string, payload []byte, apiKey string) ([]byte, error) {
req, _ := http.NewRequest(requestType, url, bytes.NewReader(payload))
req.Header.Set("Authorization", "token "+apiKey)
req.Header.Set("Content-Type", "application/json")

resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}

defer resp.Body.Close()

if resp.StatusCode == http.StatusForbidden {
return nil, errors.New("invalid API key, check the $FOSSA_API_KEY environment variable")
} else if resp.StatusCode != http.StatusOK {
return nil, errors.New("bad server response (" + string(resp.StatusCode) + ")")
}
responseBytes, _ := ioutil.ReadAll(resp.Body)
return responseBytes, nil
}
24 changes: 24 additions & 0 deletions cmd/fossa/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,19 @@ func main() {
cli.BoolFlag{Name: "debug", Usage: debugUsage},
},
},
{
Name: "test",
Usage: "Test current revision against FOSSA scan status and exit with errors if issues are found",
Action: testCmd,
Flags: []cli.Flag{
cli.StringFlag{Name: "c, config", Usage: configUsage},
cli.StringFlag{Name: "p, project", Usage: projectUsage},
cli.StringFlag{Name: "r, revision", Usage: revisionUsage},
cli.StringFlag{Name: "e, endpoint", Usage: endpointUsage},
cli.IntFlag{Name: "t, timeout", Usage: "timeout for waiting for build status; defaults to 30m", Value: 1000 * 60 * 30},
cli.BoolFlag{Name: "debug", Usage: debugUsage},
},
},
}

app.Run(os.Args)
Expand All @@ -96,12 +109,22 @@ type cliConfig struct {
endpoint string
modules []moduleConfig
debug bool
timeout int

defaultConfig defaultConfig
analyzeConfig analyzeConfig
buildConfig buildConfig
}

func (conf *cliConfig) getVcsLocator() string {
normalizedPackageSpec := strings.TrimSuffix(conf.project, ".git")
normalizedPackageSpec = strings.TrimPrefix(conf.project, "git+")
normalizedPackageSpec = strings.Replace(normalizedPackageSpec, "git@github.com:", "github.com/", 1)
normalizedPackageSpec = strings.TrimPrefix(conf.project, "http://")
normalizedPackageSpec = strings.TrimPrefix(conf.project, "https://")
return "git+" + normalizedPackageSpec + "$" + conf.revision
}

type defaultConfig struct {
build bool
}
Expand All @@ -112,6 +135,7 @@ func initialize(c *cli.Context) (cliConfig, error) {
project: c.String("project"),
revision: c.String("revision"),
endpoint: c.String("endpoint"),
timeout: c.Int("timeout"),
modules: parseModuleFlag(c.String("modules")),
debug: c.Bool("debug"),

Expand Down
127 changes: 127 additions & 0 deletions cmd/fossa/test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package main

import (
"errors"
"net/url"
"os"
"strconv"
"time"

"github.com/briandowns/spinner"
logging "github.com/op/go-logging"
"github.com/urfave/cli"
emoji "gopkg.in/kyokomi/emoji.v1"

"github.com/tidwall/gjson"
)

var testLogger = logging.MustGetLogger("test")

const PollRequestDelay = 8000

func confirmBuild(config cliConfig, timing int) error {
fossaBaseURL, err := url.Parse(config.endpoint)
if err != nil {
return errors.New("invalid FOSSA endpoint")
}

reqRef, _ := url.Parse("/api/revisions/" + url.PathEscape(config.getVcsLocator()) + "/build")
reqURL := fossaBaseURL.ResolveReference(reqRef).String()

testLogger.Debugf("Querying <%#v>", reqURL)
resp, err := makeAPIRequest("PUT", reqURL, nil, config.apiKey)
if err != nil {
return err
}

buildData := string(resp)
buildStatus := gjson.Get(buildData, "task.status").String()

testLogger.Debugf("Build status returned: %s", buildStatus)

switch buildStatus {
case "":
return errors.New("unable to parse build results")
case "FAILED":
return errors.New("failed to analyze build #" + gjson.Get(buildData, "id").String() + " <" + gjson.Get(buildData, "error").String() + ">; visit FOSSA or contact support@fossa.io")
case "SUCCEEDED":
return nil
default:
}

if timing >= config.timeout {
return errors.New("request series timed out")
}

time.Sleep(time.Duration(PollRequestDelay) * time.Millisecond)

return confirmBuild(config, timing+PollRequestDelay)
}

func confirmScan(config cliConfig, timing int) error {
fossaBaseURL, err := url.Parse(config.endpoint)
if err != nil {
return errors.New("invalid FOSSA endpoint")
}

reqRef, _ := url.Parse("/api/revisions/" + url.PathEscape(config.getVcsLocator()))
reqURL := fossaBaseURL.ResolveReference(reqRef).String()

testLogger.Debugf("Querying <%#v>", reqURL)
resp, err := makeAPIRequest("GET", reqURL, nil, config.apiKey)
if err != nil {
return err
}

revisionData := string(resp)
if gjson.Get(revisionData, "meta.0.last_scan").String() == "" {
// not scanned yet
if timing >= config.timeout {
return errors.New("request series timed out")
}
time.Sleep(time.Duration(PollRequestDelay) * time.Millisecond)

return confirmScan(config, timing+PollRequestDelay)
}

issueCount := 0
gjson.Get(revisionData, "issues.#.resolved").ForEach(func(key, value gjson.Result) bool {
if value.Bool() == false {
issueCount++
}
return true // keep iterating
})
if issueCount > 0 {
return errors.New(strconv.Itoa(issueCount) + " issues found")
}

return nil
}

func testCmd(c *cli.Context) {
config, err := initialize(c)
if err != nil {
testLogger.Fatalf("Could not load configuration: %s", err.Error())
}

s := spinner.New(spinner.CharSets[11], 100*time.Millisecond)
s.Writer = os.Stderr
s.Suffix = " Waiting for analysis job to succeed..."
s.Start()

if err := confirmBuild(config, 0); err != nil {
s.Stop()
testLogger.Fatalf("Error executing test: %#v", err)
}

s.Suffix = " Waiting for FOSSA scan results..."
s.Restart()

if err := confirmScan(config, 0); err != nil {
s.Stop()
testLogger.Fatalf(err.Error())
}
// TODO: pipe issue data into a report function
s.Stop()
emoji.Println("Success; No issues found! :tada:")
}
2 changes: 1 addition & 1 deletion cmd/fossa/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ func doUpload(config cliConfig, results analysis) error {

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

postRef, _ := url.Parse("/api/builds/custom?locator=" + url.QueryEscape(config.project+"$"+config.revision) + "&v=" + version)
postRef, _ := url.Parse("/api/builds/custom?locator=" + url.QueryEscape(config.getVcsLocator()) + "&v=" + version)
postURL := fossaBaseURL.ResolveReference(postRef).String()

analysisLogger.Debugf("Sending build data to <%#v>", postURL)
Expand Down

0 comments on commit 89dcaea

Please sign in to comment.