Skip to content

Commit 11f0a59

Browse files
Revamp CLI commands, flags, and output formats
- Rename `repo` to `repository` and `runs` to `analysis` - Rename `--run` flag to `--commit` across issues, metrics, and vulnerabilities - Update default hostname from deepsource.io to deepsource.com - Add `human` output format as new default, keep `table` as explicit option - Add `--output-file`, `--verbose`, `--analyzer` filter, and `--limit` flags - Remove legacy YAML config support and debug logging infrastructure - Add `GetEnabledAnalyzers` API endpoint and repository analyzers command - Fix report service using Errorf instead of Printf for info messages
1 parent c07c386 commit 11f0a59

29 files changed

Lines changed: 981 additions & 997 deletions

File tree

README.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,16 @@ commands along with their brief description.
7070

7171
```
7272
Usage:
73-
deepsource <command> [<arguments>]
73+
deepsource <command> [flags]
7474
7575
Available commands are:
76-
auth Authentication commands (login, logout, refresh, status)
77-
issues Show the list of issues in a file in a repository
78-
repo Operations related to the project repository (status, view)
79-
report Report artifacts to DeepSource
80-
runs View analysis runs and run issues
76+
auth Authentication commands (login, logout, refresh, status)
77+
analysis View analysis runs
78+
issues View issues in a repository
79+
repo Operations related to the project repository (status, view)
80+
report Report artifacts to DeepSource
81+
metrics View repository metrics
82+
vulnerabilities View vulnerabilities in a repository
8183
8284
Help:
8385
Use 'deepsource <command> --help/-h' for more information about the command.

cmd/deepsource/main.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,6 @@ func main() {
6161
var cliErr *clierrors.CLIError
6262
if errors.As(err, &cliErr) {
6363
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-
}
6764
} else {
6865
pterm.Error.Println(err)
6966
}
Lines changed: 124 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package list
1+
package analysis
22

33
import (
44
"context"
@@ -18,44 +18,51 @@ import (
1818
"github.com/spf13/cobra"
1919
)
2020

21-
type RunsListOptions struct {
22-
RepoArg string
23-
LimitArg int
24-
ptermTable [][]string
21+
type AnalysisOptions struct {
22+
RepoArg string
23+
LimitArg int
24+
commitOid string
2525
}
2626

27-
func NewCmdRunsList() *cobra.Command {
28-
opts := RunsListOptions{
27+
func NewCmdAnalysis() *cobra.Command {
28+
opts := AnalysisOptions{
2929
LimitArg: 20,
3030
}
3131

3232
doc := heredoc.Docf(`
33-
List analysis runs for a repository.
33+
View analysis runs for a repository.
3434
35-
To list analysis runs for the current repository:
36-
%[1]s
35+
Lists recent analysis runs by default:
36+
%[1]s
3737
38-
To list analysis runs for a specific repository, use the %[2]s flag:
39-
%[3]s
38+
Use %[2]s to scope to a specific repository:
39+
%[3]s
4040
41-
To limit the number of runs shown, use the %[4]s flag:
42-
%[5]s
43-
`, style.Cyan("deepsource runs list"), style.Yellow("--repo"), style.Cyan("deepsource runs list --repo repo_name"), style.Yellow("--limit"), style.Cyan("deepsource runs list --limit 50"))
41+
Use %[4]s to show run metadata and issues summary:
42+
%[5]s
43+
`,
44+
style.Cyan("deepsource analysis"),
45+
style.Yellow("--repo"),
46+
style.Cyan("deepsource analysis --repo repo_name"),
47+
style.Yellow("--commit"),
48+
style.Cyan("deepsource analysis --commit abc123f"),
49+
)
4450

4551
cmd := &cobra.Command{
46-
Use: "list",
47-
Short: "List analysis runs",
52+
Use: "analysis [flags]",
53+
Short: "View analysis runs",
4854
Long: doc,
4955
RunE: func(cmd *cobra.Command, args []string) error {
50-
return opts.Run()
56+
if opts.commitOid != "" {
57+
return opts.runDetail(cmd.Context())
58+
}
59+
return opts.runList()
5160
},
5261
}
5362

54-
// --repo, -r flag
5563
cmd.Flags().StringVarP(&opts.RepoArg, "repo", "r", "", "List history for the specified repository")
56-
57-
// --limit, -l flag
5864
cmd.Flags().IntVarP(&opts.LimitArg, "limit", "l", 20, "Number of analysis runs to fetch")
65+
cmd.Flags().StringVar(&opts.commitOid, "commit", "", "Show metadata and issues summary for a specific commit")
5966

6067
_ = cmd.RegisterFlagCompletionFunc("repo", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
6168
return completion.RepoCompletionCandidates(), cobra.ShellCompDirectiveNoFileComp
@@ -64,9 +71,8 @@ func NewCmdRunsList() *cobra.Command {
6471
return cmd
6572
}
6673

67-
// Execute the command
68-
func (opts *RunsListOptions) Run() error {
69-
// Load configuration
74+
// runList fetches and displays a table of recent analysis runs.
75+
func (opts *AnalysisOptions) runList() error {
7076
cfgMgr := config.DefaultManager()
7177
cfg, err := cfgMgr.Load()
7278
if err != nil {
@@ -76,13 +82,11 @@ func (opts *RunsListOptions) Run() error {
7682
return err
7783
}
7884

79-
// Resolve remote repository
8085
remote, err := vcs.ResolveRemote(opts.RepoArg)
8186
if err != nil {
8287
return err
8388
}
8489

85-
// Create DeepSource client
8690
client, err := deepsource.New(deepsource.ClientOpts{
8791
Token: cfg.Token,
8892
HostName: cfg.Host,
@@ -92,51 +96,94 @@ func (opts *RunsListOptions) Run() error {
9296
return err
9397
}
9498

95-
// Fetch analysis runs
9699
ctx := context.Background()
97-
runs, err := client.GetAnalysisRuns(ctx, remote.Owner, remote.RepoName, remote.VCSProvider, opts.LimitArg)
100+
analysisRuns, err := client.GetAnalysisRuns(ctx, remote.Owner, remote.RepoName, remote.VCSProvider, opts.LimitArg)
98101
if err != nil {
99102
return err
100103
}
101104

102-
if len(runs) == 0 {
105+
if len(analysisRuns) == 0 {
103106
pterm.Info.Println("No analysis runs found for this repository.")
104107
return nil
105108
}
106109

107-
opts.showHistory(runs)
110+
showRunsTable(analysisRuns)
108111
return nil
109112
}
110113

111-
// Format and display the runs using pterm
112-
func (opts *RunsListOptions) showHistory(analysisRuns []runs.AnalysisRun) {
113-
// Create table header
114+
// runDetail fetches and displays metadata + issues summary for a single commit.
115+
func (opts *AnalysisOptions) runDetail(ctx context.Context) error {
116+
cfgMgr := config.DefaultManager()
117+
cfg, err := cfgMgr.Load()
118+
if err != nil {
119+
return clierrors.NewCLIError(clierrors.ErrInvalidConfig, "Error reading DeepSource CLI config", err)
120+
}
121+
if err := cfg.VerifyAuthentication(); err != nil {
122+
return err
123+
}
124+
125+
client, err := deepsource.New(deepsource.ClientOpts{
126+
Token: cfg.Token,
127+
HostName: cfg.Host,
128+
OnTokenRefreshed: cfgMgr.TokenRefreshCallback(),
129+
})
130+
if err != nil {
131+
return err
132+
}
133+
134+
commitOid := opts.commitOid
135+
runWithIssues, err := client.GetRunIssues(ctx, commitOid)
136+
if err != nil {
137+
return clierrors.NewCLIError(clierrors.ErrAPIError, "Failed to fetch run details", err)
138+
}
139+
140+
commitShort := commitOid
141+
if len(commitShort) > 8 {
142+
commitShort = commitShort[:8]
143+
}
144+
145+
pterm.DefaultBox.WithTitle("Analysis Run").WithTitleTopCenter().Println(
146+
fmt.Sprintf("%s %s\n%s %s\n%s %s",
147+
pterm.Bold.Sprint("Commit:"),
148+
commitShort,
149+
pterm.Bold.Sprint("Branch:"),
150+
runWithIssues.BranchName,
151+
pterm.Bold.Sprint("Status:"),
152+
formatStatus(runWithIssues.Status),
153+
),
154+
)
155+
156+
showIssuesSummary(runWithIssues.Issues)
157+
158+
pterm.Println()
159+
pterm.Info.Printfln("Run %s to view full issue details",
160+
style.Cyan("deepsource issues --commit %s", commitShort))
161+
162+
return nil
163+
}
164+
165+
// --- Display helpers ---
166+
167+
func showRunsTable(analysisRuns []runs.AnalysisRun) {
114168
header := []string{"COMMIT", "BRANCH", "STATUS", "INTRODUCED", "RESOLVED", "SUPPRESSED", "FINISHED"}
115169
data := [][]string{header}
116170

117-
// Add data rows
118171
for _, run := range analysisRuns {
119-
// Truncate commit OID for display
120172
commitShort := run.CommitOid
121173
if len(commitShort) > 8 {
122174
commitShort = commitShort[:8]
123175
}
124176

125-
// Format branch name
126177
branch := run.BranchName
127178
if branch == "" {
128179
branch = "-"
129180
}
130181

131-
// Format status with color
132182
status := formatStatus(run.Status)
133-
134-
// Format counts
135183
introduced := fmt.Sprintf("%d", run.OccurrencesIntroduced)
136184
resolved := fmt.Sprintf("%d", run.OccurrencesResolved)
137185
suppressed := fmt.Sprintf("%d", run.OccurrencesSuppressed)
138186

139-
// Format finished time
140187
finished := "-"
141188
if run.FinishedAt != nil {
142189
finished = formatTime(*run.FinishedAt)
@@ -153,11 +200,46 @@ func (opts *RunsListOptions) showHistory(analysisRuns []runs.AnalysisRun) {
153200
})
154201
}
155202

156-
// Render table
157203
pterm.DefaultTable.WithHasHeader().WithData(data).Render()
158204
}
159205

160-
// Format status with appropriate styling
206+
func showIssuesSummary(issues []runs.RunIssue) {
207+
if len(issues) == 0 {
208+
pterm.Println()
209+
pterm.Success.Println("No issues found in this run")
210+
return
211+
}
212+
213+
var critical, major, minor int
214+
for _, issue := range issues {
215+
switch strings.ToUpper(issue.Severity) {
216+
case "CRITICAL":
217+
critical++
218+
case "MAJOR":
219+
major++
220+
case "MINOR":
221+
minor++
222+
}
223+
}
224+
225+
pterm.Println()
226+
pterm.Println(pterm.Bold.Sprintf("Issues: %d total", len(issues)))
227+
228+
parts := []string{}
229+
if critical > 0 {
230+
parts = append(parts, pterm.Red(fmt.Sprintf("%d critical", critical)))
231+
}
232+
if major > 0 {
233+
parts = append(parts, pterm.LightRed(fmt.Sprintf("%d major", major)))
234+
}
235+
if minor > 0 {
236+
parts = append(parts, pterm.Yellow(fmt.Sprintf("%d minor", minor)))
237+
}
238+
if len(parts) > 0 {
239+
pterm.Println(" " + strings.Join(parts, ", "))
240+
}
241+
}
242+
161243
func formatStatus(status string) string {
162244
switch strings.ToUpper(status) {
163245
case "SUCCESS":
@@ -173,7 +255,6 @@ func formatStatus(status string) string {
173255
}
174256
}
175257

176-
// Format time to relative time (e.g., "2 hours ago")
177258
func formatTime(t time.Time) string {
178259
now := time.Now()
179260
diff := now.Sub(t)

command/auth/login/login.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212
"github.com/spf13/cobra"
1313
)
1414

15-
var accountTypes = []string{"DeepSource (deepsource.io)", "DeepSource Enterprise"}
15+
var accountTypes = []string{"DeepSource (deepsource.com)", "Enterprise Server"}
1616

1717
// LoginOptions hold the metadata related to login operation
1818
type LoginOptions struct {
@@ -67,8 +67,11 @@ func NewCmdLogin() *cobra.Command {
6767
// Run executes the auth command and starts the login flow if not already authenticated
6868
func (opts *LoginOptions) Run() (err error) {
6969
svc := authsvc.NewService(config.DefaultManager())
70-
// Fetch config
71-
cfg, _ := svc.LoadConfig()
70+
// Fetch config (errors are non-fatal: a zero config just means "not logged in")
71+
cfg, err := svc.LoadConfig()
72+
if err != nil {
73+
cfg = &config.CLIConfig{}
74+
}
7275
opts.User = cfg.User
7376
opts.TokenExpired = cfg.IsExpired()
7477

@@ -81,7 +84,7 @@ func (opts *LoginOptions) Run() (err error) {
8184
}
8285

8386
// Checking if the user passed a hostname. If yes, storing it in the config
84-
// Else using the default hostname (deepsource.io)
87+
// Else using the default hostname (deepsource.com)
8588
if opts.HostName != "" {
8689
cfg.Host = opts.HostName
8790
} else {
@@ -130,7 +133,7 @@ func (opts *LoginOptions) handleInteractiveLogin() error {
130133
return err
131134
}
132135
// Prompt the user for hostname only in the case of on-premise
133-
if loginType == "DeepSource Enterprise" {
136+
if loginType == "Enterprise Server" {
134137
opts.HostName, err = prompt.GetSingleLineInput(hostPromptMessage, hostPromptHelpText)
135138
if err != nil {
136139
return err

command/auth/logout/logout.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,6 @@ func (opts *LogoutOptions) Run() error {
5353
return err
5454
}
5555
}
56-
pterm.Println("Logged out from DeepSource (deepsource.io)")
56+
pterm.Println("Logged out from DeepSource (deepsource.com)")
5757
return nil
5858
}

0 commit comments

Comments
 (0)