Skip to content

Commit 6851c41

Browse files
Merge branch 'master' into commit-flag-repo
2 parents 92a61c6 + bd0f271 commit 6851c41

19 files changed

Lines changed: 608 additions & 68 deletions

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.0.51
1+
2.0.54

cmd/deepsource/main.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -73,21 +73,24 @@ func mainRun() (exitCode int) {
7373
func run() int {
7474
v.SetBuildInfo(version, Date, buildMode)
7575

76-
// Check for available updates and notify (skip when running "update" itself)
76+
// Notify about available updates from a previous check (instant, disk-only read).
77+
// Then kick off a background check for the next invocation.
7778
isUpdateCmd := len(os.Args) >= 2 && os.Args[1] == "update"
7879
if !isUpdateCmd && update.ShouldCheckForUpdate() {
79-
client := &http.Client{Timeout: 3 * time.Second}
80-
if err := update.CheckForUpdate(client); err != nil {
81-
debug.Log("update: %v", err)
82-
}
83-
8480
state, err := update.ReadUpdateState()
8581
if err != nil {
8682
debug.Log("update: %v", err)
8783
}
8884
if state != nil {
89-
fmt.Fprintln(os.Stderr, pterm.Yellow(fmt.Sprintf("Update available: v%s, run '%s update' to install.", state.Version, filepath.Base(os.Args[0]))))
85+
fmt.Fprintln(os.Stderr, pterm.Yellow(fmt.Sprintf("Update available: v%s → v%s, run '%s update' to install.", version, state.Version, filepath.Base(os.Args[0]))))
9086
}
87+
88+
go func() {
89+
client := &http.Client{Timeout: 3 * time.Second}
90+
if err := update.CheckForUpdate(client); err != nil {
91+
debug.Log("update: %v", err)
92+
}
93+
}()
9194
}
9295

9396
exitCode := 0

command/auth/login/login.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ func (opts *LoginOptions) Run(cmd *cobra.Command) (err error) {
114114
}
115115

116116
if opts.PAT != "" {
117-
return opts.startPATLoginFlow(svc, cfg, opts.PAT)
117+
return startPATLoginFlow(svc, cfg, opts.PAT)
118118
}
119119

120120
if !opts.TokenExpired && cfg.Token != "" {

command/auth/login/pat_login_flow.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
"github.com/fatih/color"
1010
)
1111

12-
func (opts *LoginOptions) startPATLoginFlow(svc *authsvc.Service, cfg *config.CLIConfig, token string) error {
12+
func startPATLoginFlow(svc *authsvc.Service, cfg *config.CLIConfig, token string) error {
1313
cfg.Token = token
1414

1515
viewer, err := svc.GetViewer(context.Background(), cfg)

command/issues/issues.go

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ func (opts *IssuesOptions) Run(ctx context.Context, cmd *cobra.Command) error {
252252
opts.client = client
253253
opts.remote = remote
254254

255-
issuesList, err := opts.resolveIssues(ctx, client, remote)
255+
issuesList, err := opts.resolveIssuesWithRetry(ctx, client, remote)
256256
if err != nil {
257257
return err
258258
}
@@ -283,6 +283,45 @@ func (opts *IssuesOptions) Run(ctx context.Context, cmd *cobra.Command) error {
283283
return nil
284284
}
285285

286+
// resolveIssuesWithRetry calls resolveIssues and, for monorepos with an
287+
// auto-detected sub-repo path, progressively strips path segments on
288+
// "Repository does not exist" errors until a match is found.
289+
func (opts *IssuesOptions) resolveIssuesWithRetry(ctx context.Context, client *deepsource.Client, remote *vcs.RemoteData) ([]issues.Issue, error) {
290+
issuesList, err := opts.resolveIssues(ctx, client, remote)
291+
if err == nil {
292+
return issuesList, nil
293+
}
294+
295+
if strings.Contains(err.Error(), "This repository is a monorepo") {
296+
return nil, fmt.Errorf("This is a monorepo. Use --repo to specify a sub-project.\n\nHint: %s", err.Error())
297+
}
298+
299+
if !strings.Contains(err.Error(), "Repository does not exist") || remote.SubRepoSuffix == "" {
300+
return nil, err
301+
}
302+
303+
baseName := strings.SplitN(remote.RepoName, ":", 2)[0]
304+
parts := strings.Split(remote.SubRepoSuffix, ":")
305+
for len(parts) > 0 {
306+
parts = parts[:len(parts)-1]
307+
remote.SubRepoSuffix = strings.Join(parts, ":")
308+
if remote.SubRepoSuffix == "" {
309+
remote.RepoName = baseName
310+
} else {
311+
remote.RepoName = baseName + ":" + remote.SubRepoSuffix
312+
}
313+
314+
issuesList, err = opts.resolveIssues(ctx, client, remote)
315+
if err == nil {
316+
return issuesList, nil
317+
}
318+
if !strings.Contains(err.Error(), "Repository does not exist") {
319+
return nil, err
320+
}
321+
}
322+
return nil, err
323+
}
324+
286325
func (opts *IssuesOptions) resolveIssues(ctx context.Context, client *deepsource.Client, remote *vcs.RemoteData) ([]issues.Issue, error) {
287326
serverFilters := opts.buildServerFilters()
288327
prFilters := opts.buildPRFilters()
@@ -314,7 +353,11 @@ func (opts *IssuesOptions) resolveIssues(ctx context.Context, client *deepsource
314353
case ab.PRNumber > 0:
315354
opts.PRNumber = ab.PRNumber
316355
opts.CommitOid = ab.CommitOid
317-
issuesList, err = client.GetPRIssues(ctx, remote.Owner, remote.RepoName, remote.VCSProvider, ab.PRNumber, prFilters)
356+
if ab.Fallback {
357+
issuesList, err = client.GetRunIssuesFlat(ctx, ab.CommitOid, serverFilters)
358+
} else {
359+
issuesList, err = client.GetPRIssues(ctx, remote.Owner, remote.RepoName, remote.VCSProvider, ab.PRNumber, prFilters)
360+
}
318361
case ab.UseRepo:
319362
issuesList, err = client.GetIssues(ctx, remote.Owner, remote.RepoName, remote.VCSProvider)
320363
default:
@@ -624,16 +667,20 @@ func (opts *IssuesOptions) renderHumanIssues() error {
624667
severity := humanizeSeverity(g.Key.IssueSeverity)
625668
sevTag := style.IssueSeverityColor(g.Key.IssueSeverity, "["+severity+"]")
626669

670+
// Build metadata suffix: "issue-code · Analyzer Name"
671+
meta := g.Key.IssueCode
672+
if analyzerName := g.Issues[0].Analyzer.Name; analyzerName != "" {
673+
meta += " · " + analyzerName
674+
}
675+
627676
if len(g.Issues) == 1 {
628-
// Single occurrence: render exactly as before
629-
fmt.Fprintf(w, " %s %s\n", sevTag, g.Key.IssueText)
677+
fmt.Fprintf(w, " %s %s %s\n", sevTag, g.Key.IssueText, pterm.Gray("("+meta+")"))
630678
if opts.Verbose && g.Description != "" {
631679
fmt.Fprintf(w, " %s\n", pterm.Gray(g.Description))
632680
}
633681
fmt.Fprintf(w, " %s\n", pterm.Gray(formatLocation(g.Issues[0], cwd)))
634682
} else {
635-
// Multi-occurrence: show count + compact locations
636-
fmt.Fprintf(w, " %s %s (%d occurrences)\n", sevTag, g.Key.IssueText, len(g.Issues))
683+
fmt.Fprintf(w, " %s %s %s (%d occurrences)\n", sevTag, g.Key.IssueText, pterm.Gray("("+meta+")"), len(g.Issues))
637684
if opts.Verbose && g.Description != "" {
638685
fmt.Fprintf(w, " %s\n", pterm.Gray(g.Description))
639686
}

command/issues/tests/golden_files/commit_scope_response.json

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@
1818
"endLine": 42,
1919
"title": "Unchecked error return value of os.ReadFile",
2020
"shortcode": "GO-W1007",
21-
"explanation": "Return value of io.ReadAll is not checked for errors",
2221
"category": "BUG_RISK",
23-
"severity": "MAJOR"
22+
"severity": "MAJOR",
23+
"issue": {
24+
"shortDescription": "Return value of io.ReadAll is not checked for errors"
25+
}
2426
}
2527
},
2628
{
@@ -31,9 +33,11 @@
3133
"endLine": 91,
3234
"title": "HTTP request built with user-controlled URL",
3335
"shortcode": "GO-S1010",
34-
"explanation": "Constructing HTTP request with user-controlled URL allows SSRF",
3536
"category": "SECURITY",
36-
"severity": "MAJOR"
37+
"severity": "MAJOR",
38+
"issue": {
39+
"shortDescription": "Constructing HTTP request with user-controlled URL allows SSRF"
40+
}
3741
}
3842
}
3943
]
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"repository": {
3+
"analysisRuns": {
4+
"edges": [
5+
{
6+
"node": {
7+
"runUid": "run-uid-running-01",
8+
"commitOid": "831701c7a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
9+
"branchName": "feature/new-auth",
10+
"status": "RUNNING",
11+
"createdAt": "2025-03-12T14:00:00Z",
12+
"finishedAt": null,
13+
"updatedAt": "2025-03-12T14:01:00Z",
14+
"summary": {
15+
"occurrencesIntroduced": 0,
16+
"occurrencesResolved": 0,
17+
"occurrencesSuppressed": 0
18+
},
19+
"reportCard": null
20+
}
21+
},
22+
{
23+
"node": {
24+
"runUid": "run-uid-completed-01",
25+
"commitOid": "862df9f2e3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8",
26+
"branchName": "feature/new-auth",
27+
"status": "SUCCESS",
28+
"createdAt": "2025-03-12T12:00:00Z",
29+
"finishedAt": "2025-03-12T12:05:00Z",
30+
"updatedAt": "2025-03-12T12:05:00Z",
31+
"summary": {
32+
"occurrencesIntroduced": 2,
33+
"occurrencesResolved": 0,
34+
"occurrencesSuppressed": 0
35+
},
36+
"reportCard": null
37+
}
38+
}
39+
],
40+
"pageInfo": {
41+
"hasNextPage": false,
42+
"endCursor": null
43+
}
44+
}
45+
}
46+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"repository": {
3+
"analysisRuns": {
4+
"edges": [
5+
{
6+
"node": {
7+
"runUid": "run-uid-running-01",
8+
"commitOid": "831701c7a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
9+
"branchName": "feature/new-auth",
10+
"status": "RUNNING",
11+
"createdAt": "2025-03-12T14:00:00Z",
12+
"finishedAt": null,
13+
"updatedAt": "2025-03-12T14:01:00Z",
14+
"summary": {
15+
"occurrencesIntroduced": 0,
16+
"occurrencesResolved": 0,
17+
"occurrencesSuppressed": 0
18+
},
19+
"reportCard": null
20+
}
21+
}
22+
],
23+
"pageInfo": {
24+
"hasNextPage": false,
25+
"endCursor": null
26+
}
27+
}
28+
}
29+
}

command/issues/tests/golden_files/pr_scope_response.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
"shortcode": "GO-W1007",
1414
"category": "BUG_RISK",
1515
"severity": "MAJOR",
16-
"explanation": "Return value of io.ReadAll is not checked for errors"
16+
"issue": {
17+
"shortDescription": "Return value of io.ReadAll is not checked for errors"
18+
}
1719
}
1820
},
1921
{
@@ -26,7 +28,9 @@
2628
"shortcode": "GO-S1010",
2729
"category": "SECURITY",
2830
"severity": "MAJOR",
29-
"explanation": "Constructing HTTP request with user-controlled URL allows SSRF"
31+
"issue": {
32+
"shortDescription": "Constructing HTTP request with user-controlled URL allows SSRF"
33+
}
3034
}
3135
}
3236
],

0 commit comments

Comments
 (0)