Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds SonarQube as a new supported plugin in the gh-devlake CLI, enabling users to track code quality metrics (coverage, code smells, quality gates) alongside DORA metrics. The implementation closely follows established patterns from the Jira plugin (AccessToken auth, user-provided endpoint, API-based scope selection with pagination).
Changes:
- New
ConnectionDefregistry entry for SonarQube with AccessToken auth,SONARQUBE_TOKEN/SONAR_TOKENenv resolution, andprojectKeyscope ID field - New
scopeSonarQubeHandlerfor interactive project selection via the DevLake remote-scope API with pagination support - New
SonarQubeProjectScopestruct, registry tests, and README documentation update
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
cmd/connection_types.go |
Adds SonarQube ConnectionDef entry to connectionRegistry with AccessToken auth, empty endpoint, and scopeSonarQubeHandler |
cmd/configure_scopes.go |
Implements scopeSonarQubeHandler with paginated remote-scope fetching, interactive multi-select, and SonarQubeProjectScope PUT |
internal/devlake/types.go |
Adds SonarQubeProjectScope struct with connectionId, projectKey, name fields |
cmd/connection_types_test.go |
Table-driven tests validating SonarQube registry entry fields, auth method, env vars, and scope settings |
README.md |
Adds SonarQube to the Supported Plugins table and reorders "Coming soon" entries to the end |
| t.Errorf("jenkins ScopeFlags should include jobs flag") | ||
| } | ||
| } No newline at end of file | ||
| } |
There was a problem hiding this comment.
Missing blank line between TestConnectionRegistry_Jenkins and TestConnectionRegistry_SonarQube. Every other top-level function in this file is separated by a blank line (e.g., lines 557-559). Add a blank line after the closing } on line 589.
| } | |
| } |
|
@claude[agent] Please fix: Add a blank line between |
Fixed in ac06c57 - added blank line between the two test functions for consistency. |
| for i := range allChildren { | ||
| child := &allChildren[i] | ||
| if child.Type == "scope" { | ||
| label := child.Name | ||
| if child.ID != "" { | ||
| label = fmt.Sprintf("%s (key: %s)", child.Name, child.ID) | ||
| } | ||
| projectOptions = append(projectOptions, label) | ||
| projectMap[label] = child | ||
| } | ||
| } | ||
|
|
||
| if len(projectOptions) == 0 { |
There was a problem hiding this comment.
The selection list includes projects even when child.ID (the projectKey) is empty, but those selections are later skipped. This can lead to a confusing UX where a user selects items and then hits no valid projects to add. Consider filtering out child.ID == "" projects when building projectOptions (and not inserting them into projectMap), or fail early with a clear message that the server returned projects without keys.
| for i := range allChildren { | |
| child := &allChildren[i] | |
| if child.Type == "scope" { | |
| label := child.Name | |
| if child.ID != "" { | |
| label = fmt.Sprintf("%s (key: %s)", child.Name, child.ID) | |
| } | |
| projectOptions = append(projectOptions, label) | |
| projectMap[label] = child | |
| } | |
| } | |
| if len(projectOptions) == 0 { | |
| missingKeyCount := 0 | |
| for i := range allChildren { | |
| child := &allChildren[i] | |
| if child.Type != "scope" { | |
| continue | |
| } | |
| // Skip projects without a valid project key (child.ID) | |
| if child.ID == "" { | |
| missingKeyCount++ | |
| continue | |
| } | |
| label := fmt.Sprintf("%s (key: %s)", child.Name, child.ID) | |
| projectOptions = append(projectOptions, label) | |
| projectMap[label] = child | |
| } | |
| if len(projectOptions) == 0 { | |
| if missingKeyCount > 0 { | |
| return nil, fmt.Errorf("no SonarQube projects with valid project keys found for connection %d", connID) | |
| } |
cmd/configure_scopes.go
Outdated
| fmt.Printf(" ⚠️ Skipping project %q: empty project key\n", child.Name) | ||
| continue |
There was a problem hiding this comment.
The selection list includes projects even when child.ID (the projectKey) is empty, but those selections are later skipped. This can lead to a confusing UX where a user selects items and then hits no valid projects to add. Consider filtering out child.ID == "" projects when building projectOptions (and not inserting them into projectMap), or fail early with a clear message that the server returned projects without keys.
| fmt.Printf(" ⚠️ Skipping project %q: empty project key\n", child.Name) | |
| continue | |
| return nil, fmt.Errorf("server returned SonarQube project %q without a project key; cannot add scopes", child.Name) |
| Endpoint: "", // user must provide (e.g., https://sonar.example.com/) | ||
| SupportsTest: true, | ||
| AuthMethod: "AccessToken", | ||
| RateLimitPerHour: 0, // uses default 4500 |
There was a problem hiding this comment.
RateLimitPerHour: 0 is ambiguous (it can mean “disabled”, “unlimited”, or “use default” depending on downstream logic). Since the comment states it should use the default (4500), prefer omitting the field (so the defaulting behavior is explicit), or set it to the actual intended default value to avoid misinterpretation.
cmd/configure_scopes.go
Outdated
| fmt.Println() | ||
| selectedLabels := prompt.SelectMulti("Select SonarQube projects to track", projectOptions) | ||
| if len(selectedLabels) == 0 { | ||
| return nil, fmt.Errorf("at least one project must be selected") |
There was a problem hiding this comment.
This error message loses context in a multi-plugin flow. Consider including the plugin name and action to make it more actionable (e.g., mention SonarQube project selection), consistent with the more specific errors used elsewhere in the handler.
| return nil, fmt.Errorf("at least one project must be selected") | |
| return nil, fmt.Errorf("at least one SonarQube project must be selected") |
|
@claude[agent] Please fix:
|
Fixed in 1a1608b:
|
| if len(scopeData) == 0 { | ||
| return nil, fmt.Errorf("no valid projects to add") | ||
| } | ||
|
|
There was a problem hiding this comment.
The len(scopeData) == 0 check here is unreachable: selectedLabels is guaranteed non-empty (checked at line 844), and the loop body unconditionally appends to scopeData for every label — there are no continue/skip branches unlike the Jira handler (which skips on empty or unparseable board IDs). Consider removing this dead check, or adding a comment noting it's a defensive guard for future-proofing.
| if len(scopeData) == 0 { | |
| return nil, fmt.Errorf("no valid projects to add") | |
| } |
- Add SonarQube ConnectionDef to connectionRegistry with AccessToken auth - Implement scopeSonarQubeHandler for interactive project selection - Add SonarQubeProjectScope type to internal/devlake/types.go - Add comprehensive test coverage in TestConnectionRegistry_SonarQube - Update README with SonarQube in Supported Plugins table - All tests pass (go test ./..., go vet ./...) Co-authored-by: ewega <26189114+ewega@users.noreply.github.com>
Add blank line between TestConnectionRegistry_Jenkins and TestConnectionRegistry_SonarQube for consistency with other test functions in the file. Co-authored-by: ewega <26189114+ewega@users.noreply.github.com>
- Filter out projects with empty project keys from picker to prevent confusing UX where users select items that get silently skipped - Improve error message: "at least one SonarQube project must be selected" for clarity in multi-plugin flows - Remove now-unnecessary empty ID check in scope data loop since filtering happens earlier Co-authored-by: ewega <26189114+ewega@users.noreply.github.com>
1a1608b to
8657cd6
Compare
No description provided.