Skip to content

Commit c07c386

Browse files
Add new commands, auto token refresh, and structured error handling
- Add top-level issues, metrics, and vulnerabilities commands with SDK queries - Add transparent token refresh in GraphQL client (removes need for auth refresh) - Remove version, auth refresh, and auth whoami commands - Add UserError type to skip user-correctable errors from Sentry - Improve Sentry setup with panic recovery, release tagging - Add YAML output format and source filter for run issues - Auto-open browser on login instead of waiting for user input - Update GraphQL schema to use issues instead of occurrences - Use --version flag instead of version subcommand - Clean up error messages and stale code
1 parent 0ce920b commit c07c386

60 files changed

Lines changed: 3870 additions & 1187 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

ARCHITECTURE_REFACTORING_PLAN.md

Lines changed: 0 additions & 75 deletions
This file was deleted.

README.md

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,10 @@ Usage:
7474
7575
Available commands are:
7676
auth Authentication commands (login, logout, refresh, status)
77-
config Generate and Validate DeepSource config
7877
issues Show the list of issues in a file in a repository
7978
repo Operations related to the project repository (status, view)
8079
report Report artifacts to DeepSource
8180
runs View analysis runs and run issues
82-
version Get the version of the DeepSource CLI
83-
whoami Show the currently authenticated user
8481
8582
Help:
8683
Use 'deepsource <command> --help/-h' for more information about the command.

cmd/deepsource/main.go

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
package main
22

33
import (
4+
"errors"
5+
"fmt"
46
"log"
57
"os"
8+
"strings"
69
"time"
710

8-
"github.com/deepsourcelabs/cli/command"
911
v "github.com/deepsourcelabs/cli/buildinfo"
12+
"github.com/deepsourcelabs/cli/command"
13+
clierrors "github.com/deepsourcelabs/cli/internal/errors"
1014
"github.com/getsentry/sentry-go"
1115
"github.com/pterm/pterm"
1216
)
@@ -22,22 +26,50 @@ var (
2226
SentryDSN string
2327
)
2428

29+
func sentryEnvironment(ver string) string {
30+
if strings.HasPrefix(ver, "v") || strings.Contains(ver, ".") {
31+
return "production"
32+
}
33+
return "development"
34+
}
35+
2536
func main() {
2637
log.SetFlags(log.LstdFlags | log.Lshortfile)
2738

2839
// Init sentry
2940
err := sentry.Init(sentry.ClientOptions{
30-
Dsn: SentryDSN,
41+
Dsn: SentryDSN,
42+
Release: "deepsource-cli@" + version,
43+
Environment: sentryEnvironment(version),
3144
})
3245
if err != nil {
3346
log.Println("Could not load sentry.")
3447
}
48+
49+
defer func() {
50+
if r := recover(); r != nil {
51+
sentry.CurrentHub().Recover(r)
52+
sentry.Flush(2 * time.Second)
53+
fmt.Fprintf(os.Stderr, "fatal: unexpected panic: %v\n", r)
54+
os.Exit(2)
55+
}
56+
}()
57+
3558
v.SetBuildInfo(version, Date, "", "")
3659

3760
if err := command.Execute(); err != nil {
38-
// TODO: Handle exit codes here
39-
pterm.Error.Println(err)
40-
sentry.CaptureException(err)
61+
var cliErr *clierrors.CLIError
62+
if errors.As(err, &cliErr) {
63+
pterm.Error.Println(cliErr.Message)
64+
if os.Getenv("DEEPSOURCE_CLI_DEBUG") != "" && cliErr.Cause != nil {
65+
pterm.DefaultBasicText.Printf(" cause: %v\n", cliErr.Cause)
66+
}
67+
} else {
68+
pterm.Error.Println(err)
69+
}
70+
if !clierrors.IsUserError(err) {
71+
sentry.CaptureException(err)
72+
}
4173
sentry.Flush(2 * time.Second)
4274
os.Exit(1)
4375
}

command/auth/auth.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,20 @@ import (
55

66
"github.com/deepsourcelabs/cli/command/auth/login"
77
"github.com/deepsourcelabs/cli/command/auth/logout"
8-
"github.com/deepsourcelabs/cli/command/auth/refresh"
98
"github.com/deepsourcelabs/cli/command/auth/status"
10-
"github.com/deepsourcelabs/cli/command/auth/whoami"
119
)
1210

1311
// Options holds the metadata.
1412
type Options struct{}
1513

16-
// NewCmdAuth handles the auth command which has various sub-commands like `login`, `logout`, `refresh` and `status`
14+
// NewCmdAuth handles the auth command which has various sub-commands like `login`, `logout` and `status`
1715
func NewCmdAuth() *cobra.Command {
1816
cmd := &cobra.Command{
1917
Use: "auth",
2018
Short: "Authenticate with DeepSource",
2119
}
2220
cmd.AddCommand(login.NewCmdLogin())
2321
cmd.AddCommand(logout.NewCmdLogout())
24-
cmd.AddCommand(refresh.NewCmdRefresh())
2522
cmd.AddCommand(status.NewCmdStatus())
26-
cmd.AddCommand(whoami.NewCmdWhoAmI())
2723
return cmd
2824
}

command/auth/login/login_flow.go

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/cli/browser"
1111
"github.com/deepsourcelabs/cli/config"
1212
"github.com/deepsourcelabs/cli/deepsource/auth"
13+
clierrors "github.com/deepsourcelabs/cli/internal/errors"
1314
authsvc "github.com/deepsourcelabs/cli/internal/services/auth"
1415
"github.com/fatih/color"
1516
)
@@ -23,41 +24,42 @@ func (opts *LoginOptions) startLoginFlow(svc *authsvc.Service, cfg *config.CLICo
2324
return err
2425
}
2526

26-
// Print the user code and the permission to open browser at verificationURI
27-
c := color.New(color.FgCyan, color.Bold)
28-
c.Printf("Please copy your one-time code: %s\n", deviceRegistrationResponse.UserCode)
29-
c.Printf("Press enter to open deepsource.io in your browser...")
30-
fmt.Scanln()
31-
32-
// Having received the user code, open the browser at verificationURIComplete
27+
// Open the browser for authentication
3328
err = browser.OpenURL(deviceRegistrationResponse.VerificationURIComplete)
3429
if err != nil {
35-
return err
30+
// If the browser fails to open, show the URL for manual access
31+
c := color.New(color.FgCyan, color.Bold)
32+
c.Printf("Open this URL in your browser to authenticate:\n")
33+
fmt.Println(deviceRegistrationResponse.VerificationURIComplete)
34+
} else {
35+
fmt.Println("Opened the authentication page in your browser")
3636
}
3737

38-
// Fetch the PAT using the device registration resonse
38+
fmt.Println("Waiting for authentication")
39+
40+
// Fetch the PAT by polling the server
3941
var tokenData *auth.PAT
4042
tokenData, opts.AuthTimedOut, err = fetchPAT(ctx, deviceRegistrationResponse, svc, cfg)
4143
if err != nil {
4244
return err
4345
}
4446

45-
// Check if it was a success poll or the Auth timed out
4647
if opts.AuthTimedOut {
47-
return fmt.Errorf("Authentication timed out")
48+
return clierrors.NewCLIError(clierrors.ErrAuthRequired, "Authentication timed out. Please try again", nil)
4849
}
4950

50-
// Storing the useful data for future reference and usage
51-
// in a global config object (Cfg)
51+
// Store auth data in config
5252
cfg.User = tokenData.User.Email
5353
cfg.Token = tokenData.Token
5454
cfg.SetTokenExpiry(tokenData.Expiry)
5555

56-
// Having stored the data in the global Cfg object, write it into the config file present in the local filesystem
5756
err = svc.SaveConfig(cfg)
5857
if err != nil {
59-
return fmt.Errorf("Error in writing authentication data to a file. Exiting...")
58+
return clierrors.NewCLIError(clierrors.ErrInvalidConfig, "Failed to save authentication data", err)
6059
}
60+
61+
c := color.New(color.FgGreen, color.Bold)
62+
c.Printf("Logged in as %s\n", tokenData.User.Email)
6163
return nil
6264
}
6365

command/auth/logout/logout.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ package logout
22

33
import (
44
"errors"
5-
"fmt"
65

76
"github.com/deepsourcelabs/cli/config"
87
"github.com/deepsourcelabs/cli/internal/cli/args"
98
"github.com/deepsourcelabs/cli/internal/cli/prompt"
9+
clierrors "github.com/deepsourcelabs/cli/internal/errors"
1010
authsvc "github.com/deepsourcelabs/cli/internal/services/auth"
1111
"github.com/pterm/pterm"
1212
"github.com/spf13/cobra"
@@ -33,7 +33,7 @@ func (opts *LogoutOptions) Run() error {
3333
// Fetch config
3434
cfg, err := svc.LoadConfig()
3535
if err != nil {
36-
return fmt.Errorf("Error while reading DeepSource CLI config : %v", err)
36+
return clierrors.NewCLIError(clierrors.ErrInvalidConfig, "Error reading DeepSource CLI config", err)
3737
}
3838
// Checking if the user has authenticated / logged in or not
3939
if cfg.Token == "" {

command/auth/refresh/refresh.go

Lines changed: 0 additions & 75 deletions
This file was deleted.

command/auth/status/status.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ package status
22

33
import (
44
"errors"
5-
"fmt"
65

76
"github.com/MakeNowJust/heredoc"
87
"github.com/deepsourcelabs/cli/config"
98
"github.com/deepsourcelabs/cli/internal/cli/args"
109
"github.com/deepsourcelabs/cli/internal/cli/style"
10+
clierrors "github.com/deepsourcelabs/cli/internal/errors"
1111
authsvc "github.com/deepsourcelabs/cli/internal/services/auth"
1212
"github.com/pterm/pterm"
1313
"github.com/spf13/cobra"
@@ -41,7 +41,7 @@ func (opts *AuthStatusOptions) Run() error {
4141
// Fetch config
4242
cfg, err := svc.LoadConfig()
4343
if err != nil {
44-
return fmt.Errorf("Error while reading DeepSource CLI config : %v", err)
44+
return clierrors.NewCLIError(clierrors.ErrInvalidConfig, "Error reading DeepSource CLI config", err)
4545
}
4646
// Checking if the user has authenticated / logged in or not
4747
if cfg.Token == "" {
@@ -52,7 +52,7 @@ func (opts *AuthStatusOptions) Run() error {
5252
if !cfg.IsExpired() {
5353
pterm.Printf("Logged in to DeepSource as %s.\n", cfg.User)
5454
} else {
55-
pterm.Println("The authentication has expired. Run \"deepsource auth refresh\" to refresh the credentials.")
55+
pterm.Println("The authentication has expired. Run \"deepsource auth login\" to re-authenticate.")
5656
}
5757
return nil
5858
}

0 commit comments

Comments
 (0)