Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: upgrade packages, refactor report command #238

Merged
merged 10 commits into from
Feb 5, 2024
6 changes: 1 addition & 5 deletions .deepsource.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,4 @@ enabled = true

[[analyzers]]
name = "test-coverage"
enabled = true

[[transformers]]
name = "gofumpt"
enabled = true
enabled = true
27 changes: 27 additions & 0 deletions command/report/dsn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package report

import (
"errors"
"regexp"
)

var ErrInvalidDSN = errors.New("DeepSource | Error | Invalid DSN. Cross verify DEEPSOURCE_DSN value against the settings page of the repository")

type DSN struct {
Protocol string
Host string
Token string
}

func NewDSN(raw string) (*DSN, error) {
dsnPattern := regexp.MustCompile(`^(https?)://([^:@]+)@([^:/]+(?:\:\d+)?)`)
matches := dsnPattern.FindStringSubmatch(raw)
if len(matches) != 4 {
return nil, ErrInvalidDSN
}
return &DSN{
Protocol: matches[1],
Token: matches[2],
Host: matches[3],
}, nil
}
70 changes: 70 additions & 0 deletions command/report/dsn_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package report

import (
"reflect"
"testing"
)

func TestNewDSN(t *testing.T) {
type args struct {
raw string
}
tests := []struct {
name string
args args
want *DSN
wantErr error
}{
{
name: "valid DSN",
args: args{
raw: "https://[email protected]",
},
want: &DSN{
Token: "e1099ed7240c4045b5ab3fedebc7b5d7",
Host: "app.deepsource.com",
Protocol: "https",
},
wantErr: nil,
},
{
name: "valid DSN with port",
args: args{
raw: "http://f59a44307@localhost:8081",
},
want: &DSN{
Token: "f59a44307",
Host: "localhost:8081",
Protocol: "http",
},
},
{
name: "invalid DSN no http",
args: args{
raw: "no http",
},
want: nil,
wantErr: ErrInvalidDSN,
},
{
name: "invalid DSN",
args: args{
raw: "https://e1099ed7240c4045b5ab3fedebc7b5d7",
},
want: nil,
wantErr: ErrInvalidDSN,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := NewDSN(tt.args.raw)
if err != tt.wantErr {
t.Errorf("NewDSN() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("NewDSN() = %v, want %v", got, tt.want)
}
})
}
}
139 changes: 52 additions & 87 deletions command/report/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type ReportOptions struct {
Value string
ValueFile string
SkipCertificateVerification bool
DSN string
}

// NewCmdVersion returns the current version of cli being used
Expand Down Expand Up @@ -72,42 +73,16 @@ func NewCmdReport() *cobra.Command {
return cmd
}

func (opts *ReportOptions) Run() int {
// Verify the env variables
dsn := os.Getenv("DEEPSOURCE_DSN")
if dsn == "" {
fmt.Fprintln(os.Stderr, "DeepSource | Error | Environment variable DEEPSOURCE_DSN not set (or) is empty. You can find it under the repository settings page")
return 1
}
sentry.ConfigureScope(func(scope *sentry.Scope) {
scope.SetUser(sentry.User{ID: dsn})
})

/////////////////////
// Command: report //
/////////////////////

reportCommandAnalyzerShortcode := strings.TrimSpace(opts.Analyzer)
reportCommandAnalyzerType := strings.TrimSpace(opts.AnalyzerType)
reportCommandKey := strings.TrimSpace(opts.Key)
reportCommandValue := opts.Value
reportCommandValueFile := strings.TrimSpace(opts.ValueFile)

// Get current path
currentDir, err := os.Getwd()
if err != nil {
fmt.Fprintln(os.Stderr, "DeepSource | Error | Unable to identify current directory")
sentry.CaptureException(err)
return 1
}
sentry.ConfigureScope(func(scope *sentry.Scope) {
scope.SetExtra("currentDir", currentDir)
})

//////////////////
// Validate Key //
//////////////////
func (opts *ReportOptions) sanitize() {
opts.Analyzer = strings.TrimSpace(opts.Analyzer)
opts.AnalyzerType = strings.TrimSpace(opts.AnalyzerType)
opts.Key = strings.TrimSpace(opts.Key)
opts.Value = strings.TrimSpace(opts.Value)
opts.ValueFile = strings.TrimSpace(opts.ValueFile)
opts.DSN = strings.TrimSpace(os.Getenv("DEEPSOURCE_DSN"))
}

func (opts *ReportOptions) validateKey() error {
supportedKeys := map[string]bool{
"python": true,
"go": true,
Expand All @@ -123,66 +98,56 @@ func (opts *ReportOptions) Run() int {
"kotlin": true,
}

allowedKeys := func(m map[string]bool) []string {
keys := make([]string, 0, len(supportedKeys))
for k := range m {
keys = append(keys, k)
}
return keys
if opts.Analyzer == "test-coverage" && !supportedKeys[opts.Key] {
return fmt.Errorf("DeepSource | Error | Invalid Key: %s (Supported Keys: %v)", opts.Key, supportedKeys)
}

if reportCommandAnalyzerShortcode == "test-coverage" && !supportedKeys[reportCommandKey] {
err = fmt.Errorf("DeepSource | Error | Invalid Key: %s (Supported Keys: %v)", reportCommandKey, allowedKeys(supportedKeys))
fmt.Fprintln(os.Stderr, err)
sentry.CaptureException(err)
return nil
}

func (opts *ReportOptions) Run() int {
opts.sanitize()
if opts.DSN == "" {
fmt.Fprintln(os.Stderr, "DeepSource | Error | Environment variable DEEPSOURCE_DSN not set (or) is empty. You can find it under the repository settings page")
return 1
}
sentry.ConfigureScope(func(scope *sentry.Scope) {
scope.SetUser(sentry.User{ID: opts.DSN})
})

//////////////////
// Validate DSN //
//////////////////

// Protocol
dsnSplitProtocolBody := strings.Split(dsn, "://")
/////////////////////
// Command: report //
/////////////////////

// Validate DSN parsing
if len(dsnSplitProtocolBody) != 2 {
err = errors.New("DeepSource | Error | Invalid DSN. Cross verify DEEPSOURCE_DSN value against the settings page of the repository")
fmt.Fprintln(os.Stderr, err)
// Get current path
currentDir, err := os.Getwd()
if err != nil {
fmt.Fprintln(os.Stderr, "DeepSource | Error | Unable to identify current directory")
sentry.CaptureException(err)
return 1
}
sentry.ConfigureScope(func(scope *sentry.Scope) {
scope.SetExtra("currentDir", currentDir)
})

// Check for valid protocol
if !strings.HasPrefix(dsnSplitProtocolBody[0], "http") {
err = errors.New("DeepSource | Error | DSN specified should start with http(s). Cross verify DEEPSOURCE_DSN value against the settings page of the repository")
// validate key
if err := opts.validateKey(); err != nil {
fmt.Fprintln(os.Stderr, err)
sentry.CaptureException(err)
return 1
}
dsnProtocol := dsnSplitProtocolBody[0]

// Parse body of the DSN
dsnSplitTokenHost := strings.Split(dsnSplitProtocolBody[1], "@")

// Validate DSN parsing
if len(dsnSplitTokenHost) != 2 {
err = errors.New("DeepSource | Error | Invalid DSN. Cross verify DEEPSOURCE_DSN value against the settings page of the repository")
dsn, err := NewDSN(opts.DSN)
if err != nil {
fmt.Fprintln(os.Stderr, err)
sentry.CaptureException(err)
return 1
}

// Set values parsed from DSN
dsnHost := dsnSplitTokenHost[1]

///////////////////////
// Generate metadata //
///////////////////////

// Access token
dsnAccessToken := dsnSplitTokenHost[0]

// Head Commit OID
headCommitOID, warning, err := gitGetHead(currentDir)
if err != nil {
Expand All @@ -196,7 +161,7 @@ func (opts *ReportOptions) Run() int {
})

// Flag validation
if reportCommandValue == "" && reportCommandValueFile == "" {
if opts.Value == "" && opts.ValueFile == "" {
fmt.Fprintln(os.Stderr, "DeepSource | Error | '--value' (or) '--value-file' not passed")
return 1
}
Expand All @@ -206,26 +171,26 @@ func (opts *ReportOptions) Run() int {
var artifactKey string
var artifactValue string

analyzerShortcode = reportCommandAnalyzerShortcode
analyzerType = reportCommandAnalyzerType
artifactKey = reportCommandKey
analyzerShortcode = opts.Analyzer
analyzerType = opts.AnalyzerType
artifactKey = opts.Key

if reportCommandValue != "" {
artifactValue = reportCommandValue
if opts.Value != "" {
artifactValue = opts.Value
}

if reportCommandValueFile != "" {
if opts.ValueFile != "" {
// Check file size
_, err := os.Stat(reportCommandValueFile)
_, err := os.Stat(opts.ValueFile)
if err != nil {
fmt.Fprintln(os.Stderr, "DeepSource | Error | Unable to read specified value file:", reportCommandValueFile)
fmt.Fprintln(os.Stderr, "DeepSource | Error | Unable to read specified value file:", opts.ValueFile)
sentry.CaptureException(err)
return 1
}

valueBytes, err := os.ReadFile(reportCommandValueFile)
valueBytes, err := os.ReadFile(opts.ValueFile)
if err != nil {
fmt.Fprintln(os.Stderr, "DeepSource | Error | Unable to read specified value file:", reportCommandValueFile)
fmt.Fprintln(os.Stderr, "DeepSource | Error | Unable to read specified value file:", opts.ValueFile)
sentry.CaptureException(err)
return 1
}
Expand All @@ -244,7 +209,7 @@ func (opts *ReportOptions) Run() int {
}

r, err := makeQuery(
dsnProtocol+"://"+dsnHost+"/graphql/cli/",
dsn.Protocol+"://"+dsn.Host+"/graphql/cli/",
qBytes,
"application/json",
opts.SkipCertificateVerification,
Expand Down Expand Up @@ -284,7 +249,7 @@ func (opts *ReportOptions) Run() int {
compressLevel := 20
compressedBytes, err = zstd.CompressLevel(compressedBytes, []byte(artifactValue), compressLevel)
if err != nil {
fmt.Fprintln(os.Stderr, "DeepSource | Error | Failed to compress value file:", reportCommandValueFile)
fmt.Fprintln(os.Stderr, "DeepSource | Error | Failed to compress value file:", opts.ValueFile)
sentry.CaptureException(err)
return 1
}
Expand All @@ -302,7 +267,7 @@ func (opts *ReportOptions) Run() int {
////////////////////

queryInput := ReportQueryInput{
AccessToken: dsnAccessToken,
AccessToken: dsn.Token,
CommitOID: headCommitOID,
ReporterName: "cli",
ReporterVersion: CliVersion,
Expand Down Expand Up @@ -331,7 +296,7 @@ func (opts *ReportOptions) Run() int {
}

queryResponseBody, err := makeQuery(
dsnProtocol+"://"+dsnHost+"/graphql/cli/",
dsn.Protocol+"://"+dsn.Host+"/graphql/cli/",
queryBodyBytes,
"application/json",
opts.SkipCertificateVerification,
Expand All @@ -347,7 +312,7 @@ func (opts *ReportOptions) Run() int {
return 1
}
queryResponseBody, err = makeQuery(
dsnProtocol+"://"+dsnHost+"/graphql/cli/",
dsn.Protocol+"://"+dsn.Host+"/graphql/cli/",
queryBodyBytes,
"application/json",
opts.SkipCertificateVerification,
Expand Down
Loading
Loading