diff --git a/CHANGELOG.md b/CHANGELOG.md index 83e0219bad..8955ce687e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ All notable changes to `src-cli` are documented in this file. ### Added +- Add verbosity flag to `lsif upload` action. Supply `-trace=1`, `-trace=2`, or `-trace=3` to the action to specify verbosity. + ### Changed ### Fixed diff --git a/cmd/src/lsif_upload.go b/cmd/src/lsif_upload.go index 7f3d785d64..e653321af3 100644 --- a/cmd/src/lsif_upload.go +++ b/cmd/src/lsif_upload.go @@ -4,14 +4,15 @@ import ( "encoding/json" "flag" "fmt" + "net/http" "net/url" "os" + "sort" "strings" "sync" "time" "github.com/efritz/pentimento" - "github.com/mattn/go-isatty" "github.com/pkg/browser" "github.com/pkg/errors" "github.com/sourcegraph/codeintelutils" @@ -52,6 +53,8 @@ Examples: maxPayloadSizeMb *int ignoreUploadFailures *bool uploadRoute *string + rawVerbosity *int + verbosity lsifUploadVerbosity } flagSet := flag.NewFlagSet("upload", flag.ExitOnError) @@ -67,6 +70,7 @@ Examples: flags.maxPayloadSizeMb = flagSet.Int("max-payload-size", 100, `The maximum upload size (in megabytes). Indexes exceeding this limit will be uploaded over multiple HTTP requests.`) flags.ignoreUploadFailures = flagSet.Bool("ignore-upload-failure", false, `Exit with status code zero on upload failure.`) flags.uploadRoute = flagSet.String("upload-route", "/.api/lsif/upload", "The path of the upload route.") + flags.rawVerbosity = flagSet.Int("trace", 0, "-trace=0 shows no logs; -trace=1 shows requests and response metadata; -trace=2 shows headers, -trace=3 shows response body") parseAndValidateFlags := func(args []string) error { flagSet.Parse(args) @@ -146,6 +150,12 @@ Examples: return errors.New("max-payload-size must be positive") } + // Don't need to check upper bounds as we only compare verbosity ranges + // It's fine if someone supplies -trace=42, but it will just behave the + // same as if they supplied the highest verbosity level we define + // internally. + flags.verbosity = lsifUploadVerbosity(*flags.rawVerbosity) + if !*flags.json { fmt.Println(argsString) } @@ -173,6 +183,7 @@ Examples: MaxRetries: 10, RetryInterval: time.Millisecond * 250, UploadProgressEvents: make(chan codeintelutils.UploadProgressEvent), + Logger: &lsifUploadRequestLogger{verbosity: flags.verbosity}, } var wg sync.WaitGroup @@ -181,7 +192,7 @@ Examples: go func() { defer wg.Done() - if *flags.json || *flags.noProgress { + if *flags.json || *flags.noProgress || flags.verbosity > 0 { return } @@ -202,19 +213,19 @@ Examples: wg.Wait() // Wait for progress bar goroutine to clear screen if err != nil { if err == codeintelutils.ErrUnauthorized { - if *flags.gitHubToken == "" { - return fmt.Errorf("you must provide -github-token=TOKEN, where TOKEN is a GitHub personal access token with 'repo' or 'public_repo' scope") + err = errorWithHint{ + err: err, hint: strings.Join([]string{ + "You may need to specify or update your GitHub access token to use this endpoint.", + "See https://docs.sourcegraph.com/cli/references/lsif/upload.", + }, "\n"), } - if isatty.IsTerminal(os.Stdout.Fd()) { - fmt.Println("You may need to specify or update your GitHub access token to use this endpoint.") - fmt.Println("See https://github.com/sourcegraph/src-cli#authentication.") - } } if *flags.ignoreUploadFailures { - fmt.Printf("error: %s\n", err) - return nil + // Report but don't return + fmt.Println(err.Error()) + err = nil } return err @@ -312,3 +323,76 @@ func digits(n int) int { } return 1 } + +type errorWithHint struct { + err error + hint string +} + +func (e errorWithHint) Error() string { + return fmt.Sprintf("error: %s\n\n%s\n", e.err, e.hint) +} + +type lsifUploadVerbosity int + +const ( + lsifUploadVerbosityNone lsifUploadVerbosity = iota // -trace=0 (default) + lsifUploadVerbosityTrace // -trace=1 + lsifUploadVerbosityTraceShowHeaders // -trace=2 + lsifUploadVerbosityTraceShowResponseBody // -trace=3 +) + +type lsifUploadRequestLogger struct { + verbosity lsifUploadVerbosity +} + +func (l *lsifUploadRequestLogger) LogRequest(req *http.Request) { + if l.verbosity == lsifUploadVerbosityNone { + return + } + + if l.verbosity >= lsifUploadVerbosityTrace { + fmt.Printf("> %s %s\n", req.Method, req.URL) + } + + if l.verbosity >= lsifUploadVerbosityTraceShowHeaders { + fmt.Printf("> Request Headers:\n") + for _, k := range sortHeaders(req.Header) { + fmt.Printf("> %s: %s\n", k, req.Header[k]) + } + } + + fmt.Printf("\n") +} + +func (l *lsifUploadRequestLogger) LogResponse(req *http.Request, resp *http.Response, body []byte, elapsed time.Duration) { + if l.verbosity == lsifUploadVerbosityNone { + return + } + + if l.verbosity >= lsifUploadVerbosityTrace { + fmt.Printf("< %s %s %s in %s\n", req.Method, req.URL, resp.Status, elapsed) + } + + if l.verbosity >= lsifUploadVerbosityTraceShowHeaders { + fmt.Printf("< Response Headers:\n") + for _, k := range sortHeaders(resp.Header) { + fmt.Printf("< %s: %s\n", k, resp.Header[k]) + } + } + + if l.verbosity >= lsifUploadVerbosityTraceShowResponseBody { + fmt.Printf("< Response Body: %s\n", body) + } + + fmt.Printf("\n") +} + +func sortHeaders(header http.Header) []string { + var keys []string + for k := range header { + keys = append(keys, k) + } + sort.Strings(keys) + return keys +} diff --git a/go.mod b/go.mod index ebe9209de1..e12b4fb797 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 github.com/pkg/errors v0.9.1 github.com/sourcegraph/campaignutils v0.0.0-20201124055807-2f9cfa9317e2 - github.com/sourcegraph/codeintelutils v0.0.0-20201118031531-b82ba3167b30 + github.com/sourcegraph/codeintelutils v0.0.0-20210113171425-9ec641b48a8e github.com/sourcegraph/go-diff v0.6.1 github.com/sourcegraph/jsonx v0.0.0-20200629203448-1a936bd500cf github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect diff --git a/go.sum b/go.sum index d569c1db34..bcbbeb1b3a 100644 --- a/go.sum +++ b/go.sum @@ -52,8 +52,8 @@ github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxr github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= github.com/sourcegraph/campaignutils v0.0.0-20201124055807-2f9cfa9317e2 h1:MJu/6WzWdPegzYnZLb04IS0u4VyUpPIAHQyWT5i2vR8= github.com/sourcegraph/campaignutils v0.0.0-20201124055807-2f9cfa9317e2/go.mod h1:xm6i78Mk2t4DBLQDqEFc/3x6IPf7yYZCgbNaTQGhJHA= -github.com/sourcegraph/codeintelutils v0.0.0-20201118031531-b82ba3167b30 h1:HrRrPyskdkHc6MqQS3ehH+DSraFnOhtvBWQ6AzEJC1o= -github.com/sourcegraph/codeintelutils v0.0.0-20201118031531-b82ba3167b30/go.mod h1:HplI8gRslTrTUUsSYwu28hSOderix7m5dHNca7xBzeo= +github.com/sourcegraph/codeintelutils v0.0.0-20210113171425-9ec641b48a8e h1:PdNc6fH0HHQ5xbnCwPkHuFdVCofQilFm9gG40fEQKms= +github.com/sourcegraph/codeintelutils v0.0.0-20210113171425-9ec641b48a8e/go.mod h1:HplI8gRslTrTUUsSYwu28hSOderix7m5dHNca7xBzeo= github.com/sourcegraph/go-diff v0.6.1 h1:hmA1LzxW0n1c3Q4YbrFgg4P99GSnebYa3x8gr0HZqLQ= github.com/sourcegraph/go-diff v0.6.1/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= github.com/sourcegraph/jsonx v0.0.0-20200629203448-1a936bd500cf h1:oAdWFqhStsWiiMP/vkkHiMXqFXzl1XfUNOdxKJbd6bI=