Skip to content

Commit 1d9f60a

Browse files
Auto-detect sub-repo from CWD + add analyzer to PR issues query
- Detect CWD relative to git root and append colon-delimited sub-repo suffix to the repo name sent to the API - Progressively strip path segments on 404 to find the right sub-project - Show friendly error when monorepo is detected without --repo - Include analyzer name/shortcode in PR issues GraphQL response
1 parent a4680e9 commit 1d9f60a

File tree

4 files changed

+103
-17
lines changed

4 files changed

+103
-17
lines changed

command/issues/issues.go

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,34 @@ func (opts *IssuesOptions) Run(ctx context.Context, cmd *cobra.Command) error {
254254

255255
issuesList, err := opts.resolveIssues(ctx, client, remote)
256256
if err != nil {
257-
return err
257+
// If the API says this is a monorepo, show a friendly message.
258+
if strings.Contains(err.Error(), "This repository is a monorepo") {
259+
return fmt.Errorf("This is a monorepo. Use --repo to specify a sub-project.\n\nHint: %s", err.Error())
260+
}
261+
262+
// If we auto-detected a sub-repo path and the exact path wasn't found,
263+
// progressively strip the last segment and retry.
264+
if strings.Contains(err.Error(), "Repository does not exist") && remote.SubRepoSuffix != "" {
265+
parts := strings.Split(remote.SubRepoSuffix, ":")
266+
// Try stripping from the end: e.g. a:b:c → a:b → a
267+
for len(parts) > 1 {
268+
parts = parts[:len(parts)-1]
269+
remote.SubRepoSuffix = strings.Join(parts, ":")
270+
baseName := strings.SplitN(remote.RepoName, ":", 2)[0]
271+
remote.RepoName = baseName + ":" + remote.SubRepoSuffix
272+
273+
issuesList, err = opts.resolveIssues(ctx, client, remote)
274+
if err == nil {
275+
break
276+
}
277+
if !strings.Contains(err.Error(), "Repository does not exist") {
278+
return err
279+
}
280+
}
281+
}
282+
if err != nil {
283+
return err
284+
}
258285
}
259286
if issuesList == nil {
260287
return nil

deepsource/issues/queries/pr_issues.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ const fetchPRIssuesQuery = `query GetPRIssues(
3535
category
3636
severity
3737
explanation
38+
issue {
39+
analyzer {
40+
name
41+
shortcode
42+
}
43+
}
3844
}
3945
}
4046
pageInfo {
@@ -77,6 +83,12 @@ type PRIssuesListResponse struct {
7783
Category string `json:"category"`
7884
Severity string `json:"severity"`
7985
Explanation string `json:"explanation"`
86+
Issue *struct {
87+
Analyzer struct {
88+
Name string `json:"name"`
89+
Shortcode string `json:"shortcode"`
90+
} `json:"analyzer"`
91+
} `json:"issue"`
8092
} `json:"node"`
8193
} `json:"edges"`
8294
PageInfo pagination.PageInfo `json:"pageInfo"`
@@ -139,6 +151,12 @@ func (r *PRIssuesListRequest) Do(ctx context.Context) ([]issues.Issue, error) {
139151
},
140152
},
141153
}
154+
if node.Issue != nil {
155+
issue.Analyzer = issues.AnalyzerMeta{
156+
Name: node.Issue.Analyzer.Name,
157+
Shortcode: node.Issue.Analyzer.Shortcode,
158+
}
159+
}
142160
allIssues = append(allIssues, issue)
143161
}
144162

internal/vcs/remote_resolver.go

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ type RemoteData struct {
1313
Owner string
1414
RepoName string
1515
VCSProvider string
16+
17+
// SubRepoSuffix is the colon-delimited sub-repo path detected from CWD
18+
// (e.g. "libs:shared:ui-elements"). Empty when at git root or using --repo.
19+
SubRepoSuffix string
1620
}
1721

1822
func ResolveRemote(repoArg string) (*RemoteData, error) {
@@ -50,27 +54,34 @@ func ResolveRemote(repoArg string) (*RemoteData, error) {
5054
remote.RepoName = value[1]
5155
remote.VCSProvider = value[2]
5256
}
53-
debug.Log("remote: resolved to %s/%s (provider=%s)", remote.Owner, remote.RepoName, remote.VCSProvider)
54-
return &remote, nil
55-
}
57+
} else {
58+
var promptOpts []string
59+
for _, value := range remotesData {
60+
promptOpts = append(promptOpts, value[3])
61+
}
5662

57-
var promptOpts []string
58-
for _, value := range remotesData {
59-
promptOpts = append(promptOpts, value[3])
60-
}
63+
selectedRemote, err := prompt.SelectFromOptions("Please select the repository:", "", promptOpts)
64+
if err != nil {
65+
return nil, err
66+
}
6167

62-
selectedRemote, err := prompt.SelectFromOptions("Please select the repository:", "", promptOpts)
63-
if err != nil {
64-
return nil, err
68+
for _, value := range remotesData {
69+
if value[3] == selectedRemote {
70+
remote.Owner = value[0]
71+
remote.RepoName = value[1]
72+
remote.VCSProvider = value[2]
73+
}
74+
}
6575
}
6676

67-
for _, value := range remotesData {
68-
if value[3] == selectedRemote {
69-
remote.Owner = value[0]
70-
remote.RepoName = value[1]
71-
remote.VCSProvider = value[2]
72-
}
77+
// Detect sub-repo path from CWD for monorepo support.
78+
if subPath := detectSubRepoPath(); subPath != "" {
79+
remote.SubRepoSuffix = subPath
80+
remote.RepoName = remote.RepoName + ":" + subPath
81+
debug.Log("remote: appended sub-repo suffix %q → %s", subPath, remote.RepoName)
7382
}
83+
84+
debug.Log("remote: resolved to %s/%s (provider=%s)", remote.Owner, remote.RepoName, remote.VCSProvider)
7485
return &remote, nil
7586
}
7687

internal/vcs/remotes.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,36 @@ func ListRemotes() (map[string][]string, error) {
130130
return remoteMap, nil
131131
}
132132

133+
// detectSubRepoPath returns the CWD's path relative to the git root,
134+
// with "/" replaced by ":" (the DeepSource sub-repo delimiter).
135+
// If CWD is the git root itself, it returns "".
136+
func detectSubRepoPath() string {
137+
toplevel, err := runCmd("git", []string{"rev-parse", "--show-toplevel"})
138+
if err != nil {
139+
return ""
140+
}
141+
toplevel = strings.TrimSpace(toplevel)
142+
143+
cwd, err := runCmd("pwd", nil)
144+
if err != nil {
145+
return ""
146+
}
147+
cwd = strings.TrimSpace(cwd)
148+
149+
if cwd == toplevel {
150+
return ""
151+
}
152+
153+
rel := strings.TrimPrefix(cwd, toplevel+"/")
154+
if rel == cwd {
155+
// cwd is not under toplevel (shouldn't happen)
156+
return ""
157+
}
158+
159+
debug.Log("git: sub-repo relative path %q", rel)
160+
return strings.ReplaceAll(rel, "/", ":")
161+
}
162+
133163
func runCmd(command string, args []string) (string, error) {
134164
debug.Log("git: exec %s %s", command, strings.Join(args, " "))
135165
output, err := exec.Command(command, args...).Output()

0 commit comments

Comments
 (0)