1- package list
1+ package analysis
22
33import (
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+
161243func 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")
177258func formatTime (t time.Time ) string {
178259 now := time .Now ()
179260 diff := now .Sub (t )
0 commit comments