Skip to content

Addressing PR comments#124

Merged
ewega merged 5 commits into
mainfrom
codex/add-jenkins-support
Mar 5, 2026
Merged

Addressing PR comments#124
ewega merged 5 commits into
mainfrom
codex/add-jenkins-support

Conversation

@Codex

@Codex Codex AI commented Mar 5, 2026

Copy link
Copy Markdown
Contributor
Original prompt

This section details on the original issue you should resolve

<issue_title>Jenkins plugin support</issue_title>
<issue_description>## Summary

Add Jenkins as a supported DevOps tool in the CLI. Jenkins is the most popular CI/CD server (~44% market share), providing deployment frequency and build data for DORA metrics.

Upstream Plugin

jenkins in incubator-devlake/backend/plugins/jenkins/

Property Value
Plugin slug jenkins
Auth BasicAuth only (username + password/API token)
Scope type Jobs (FullName string, path-based)
Scope ID field fullName
Default endpoint User-provided Jenkins server URL
Remote-scope API Yes
Connection test Yes

Dependencies

Blocked by:

Changes

  • Add ConnectionDef for jenkins in connectionRegistry:
    • AuthMethod: "BasicAuth"
    • NeedsUsername: true
    • UsernamePrompt: "Jenkins username"
    • UsernameEnvVars: []string{"JENKINS_USER", "JENKINS_USERNAME"}
    • Endpoint: "" (user must provide)
    • SupportsTest: true
    • TokenPrompt: "Jenkins API token or password"
    • EnvVarNames: []string{"JENKINS_TOKEN", "JENKINS_PASSWORD"}
    • EnvFileKeys: []string{"JENKINS_TOKEN", "JENKINS_PASSWORD"}
    • ScopeIDField: "fullName"
    • ScopeFunc: scopeJenkinsHandler
  • Implement scopeJenkinsHandler:
    • Use client.ListRemoteScopes("jenkins", connID, "", "") to browse job tree
    • Let user select jobs interactively or via --jobs flag
    • PUT selected jobs as scopes
  • Set Available: true

Acceptance Criteria

  • gh devlake configure connection add --plugin jenkins --username admin --endpoint https://jenkins.example.com creates a Jenkins connection
  • Jenkins appears in interactive plugin picker
  • Jenkins scopes list jobs for selection
  • Connection test works
  • go build ./..., go test ./..., go vet ./... pass</issue_description>

Comments on the Issue (you are @codex[agent] in this section)

@ewega ## Agent Context for Implementation

This repo uses a declarative plugin registry pattern. Here's what you need to do:

1. Add ConnectionDef entry to cmd/connection_types.go

Add a new entry to connectionRegistry slice. Use the existing github entry as a template. Key fields for Jenkins:

  • Plugin: "jenkins"
  • DisplayName: "Jenkins"
  • Available: true
  • Endpoint: "" (user must provide Jenkins server URL)
  • SupportsTest: true
  • AuthMethod: "BasicAuth"
  • NeedsUsername: true
  • UsernamePrompt: "Jenkins username"
  • UsernameEnvVars: []string{"JENKINS_USER", "JENKINS_USERNAME"}
  • UsernameEnvFileKeys: []string{"JENKINS_USER", "JENKINS_USERNAME"}
  • TokenPrompt: "Jenkins API token or password"
  • EnvVarNames: []string{"JENKINS_TOKEN", "JENKINS_PASSWORD"}
  • EnvFileKeys: []string{"JENKINS_TOKEN", "JENKINS_PASSWORD"}
  • ScopeIDField: "fullName"
  • ScopeFunc: scopeJenkinsHandler

2. Implement scopeJenkinsHandler in cmd/configure_scopes.go

Follow the pattern of scopeGitHubHandler. Use client.ListRemoteScopes("jenkins", connID, "", "") to browse the job tree. Let user select jobs interactively via prompt.SelectMulti. PUT selected jobs as scopes.

3. Key conventions

  • Use doGet[T], doPost[T] generic helpers from internal/devlake/client.go — never raw HTTP
  • Error wrapping: fmt.Errorf("context: %w", err)
  • Terminal output: \n before emoji-prefixed steps, 3-space indent for sub-items
  • Imports: stdlib → external → internal, separated by blank lines
  • Tests: add entries to cmd/connection_types_test.go verifying the new registry entry
  • The BasicAuth model (NeedsUsername, UsernamePrompt, etc.) was just added — the resolveUsername() function and BuildCreateRequest/BuildTestRequest already handle BasicAuth automatically based on these fields

4. Do NOT

  • Add switch/case branches on plugin names outside connectionRegistry
  • Modify existing plugin entries
  • Change any files in internal/devlake/ unless adding new types needed for Jenkins scope responses</comment_new>

@Codex Codex AI assigned Codex and ewega Mar 5, 2026
@ewega ewega marked this pull request as ready for review March 5, 2026 12:32
Copilot AI review requested due to automatic review settings March 5, 2026 12:32
@Codex Codex AI changed the title [WIP] Add Jenkins plugin support in CLI Add Jenkins plugin with BasicAuth and job scoping Mar 5, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds first-class Jenkins plugin support to the gh-devlake GitHub CLI extension by extending the plugin registry, implementing Jenkins scope selection via the DevLake remote-scope API, and updating docs/tests accordingly.

Changes:

  • Add jenkins ConnectionDef (BasicAuth, scope metadata, fullName scope ID field) to the plugin registry.
  • Implement Jenkins scope add flow: parse --jobs or browse remote scopes and PUT selected job scopes.
  • Update CLI documentation and tests to include Jenkins plugin usage/availability.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
cmd/connection_types.go Adds Jenkins ConnectionDef to the registry (BasicAuth + scope metadata).
cmd/configure_scopes.go Implements Jenkins job discovery/selection and scopeJenkinsHandler.
cmd/configure_scope_add.go Adds --jobs flag and includes it in “flag mode” detection.
internal/devlake/types.go Adds JenkinsJobScope payload type for PUT /scopes.
cmd/connection_types_test.go Extends/introduces tests for Jenkins registry and auth behaviors.
docs/configure-connection.md Documents Jenkins in connection flags and required auth.
docs/configure-scope.md Documents Jenkins scopes and the --jobs flag/flow.
README.md Updates high-level docs and supported plugins list to include Jenkins.

Comment thread cmd/configure_scopes.go
Comment on lines +266 to +280
// resolveJenkinsJobs determines Jenkins jobs from flags or interactive remote-scope browsing.
func resolveJenkinsJobs(client *devlake.Client, connID int, opts *ScopeOpts) ([]string, error) {
if opts.Jobs != "" {
var jobs []string
for _, j := range strings.Split(opts.Jobs, ",") {
j = strings.TrimSpace(j)
if j != "" {
jobs = append(jobs, j)
}
}
if len(jobs) == 0 {
return nil, fmt.Errorf("no Jenkins jobs provided via --jobs")
}
return jobs, nil
}

Copilot AI Mar 5, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New resolveJenkinsJobs() parsing/validation logic (splitting, trimming, empty-input error) isn’t covered by unit tests, while resolveRepos has dedicated tests in cmd/configure_scopes_test.go. Consider adding similar table-driven tests for the --jobs flag path (e.g., trimming, ignoring empty entries, and erroring when only commas/spaces are provided).

Copilot uses AI. Check for mistakes.
Comment thread cmd/connection_types.go
Plugin: "jenkins",
DisplayName: "Jenkins",
Available: true,
Endpoint: "",

Copilot AI Mar 5, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Jenkins ConnectionDef sets Endpoint to an empty string, but connection creation/testing doesn’t validate or prompt for a required endpoint. If the user doesn’t pass --endpoint, BuildCreateRequest/BuildTestRequest will send an empty Endpoint, which will likely fail (or create a broken connection). Suggest enforcing endpoint for plugins with no default (e.g., error or prompt when def.Endpoint=="" and params.Endpoint=="") before testing/creating the connection.

Suggested change
Endpoint: "",
Endpoint: "http://localhost:8080/",

Copilot uses AI. Check for mistakes.
Comment thread cmd/connection_types.go Outdated
Comment on lines +246 to +247
RequiredScopes: []string{"Jenkins API token or password"},
ScopeHint: "Jenkins API token or password",

Copilot AI Mar 5, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For Jenkins (BasicAuth), RequiredScopes/ScopeHint currently contain credential text, but scopeHintSuffix renders it as “Ensure your PAT has these scopes…”, which is misleading for Jenkins users. Consider adjusting Jenkins’ ScopeHint/RequiredScopes wording to be credential-oriented (or making scopeHintSuffix text auth-method-aware) so error messages remain accurate.

Suggested change
RequiredScopes: []string{"Jenkins API token or password"},
ScopeHint: "Jenkins API token or password",
RequiredScopes: []string{},
ScopeHint: "",

Copilot uses AI. Check for mistakes.
Comment thread cmd/configure_scopes.go
Comment on lines +323 to +346
// listJenkinsRemoteJobs walks the Jenkins remote-scope tree and returns all job scopes.
func listJenkinsRemoteJobs(client *devlake.Client, connID int) ([]devlake.RemoteScopeChild, error) {
var (
allJobs []devlake.RemoteScopeChild
groupQueue = []string{""}
)
for len(groupQueue) > 0 {
groupID := groupQueue[0]
groupQueue = groupQueue[1:]

pageToken := ""
for {
resp, err := client.ListRemoteScopes("jenkins", connID, groupID, pageToken)
if err != nil {
return nil, fmt.Errorf("listing remote scopes: %w", err)
}
for _, child := range resp.Children {
switch child.Type {
case "group":
groupQueue = append(groupQueue, child.ID)
case "scope":
allJobs = append(allJobs, child)
}
}

Copilot AI Mar 5, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

listJenkinsRemoteJobs walks the entire remote-scope tree and accumulates all jobs before prompting. For large Jenkins instances this can be slow/heavy and there’s no visited-set to prevent repeated group traversal if the API ever returns duplicate groups. Consider tracking visited group IDs and/or adding a max-depth/limit or incremental selection to reduce API calls and memory usage.

Copilot uses AI. Check for mistakes.
Comment thread cmd/connection_types_test.go Outdated
if def.ScopeFunc == nil {
t.Errorf("jenkins ScopeFunc should be set")
}
if len(def.ScopeFlags) == 0 || def.ScopeFlags[0].Name != "jobs" {

Copilot AI Mar 5, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TestConnectionRegistry_Jenkins asserts ScopeFlags[0].Name == "jobs", which is order-dependent and will break if more scope flags are added/reordered. Prefer asserting that the slice contains a flag named "jobs" (and possibly that it’s associated with Jenkins) rather than relying on index 0.

Suggested change
if len(def.ScopeFlags) == 0 || def.ScopeFlags[0].Name != "jobs" {
foundJobs := false
for _, f := range def.ScopeFlags {
if f.Name == "jobs" {
foundJobs = true
break
}
}
if !foundJobs {

Copilot uses AI. Check for mistakes.
@ewega

ewega commented Mar 5, 2026

Copy link
Copy Markdown
Contributor

@codex[agent] Please address these code review comments:

  1. Clear misleading RequiredScopes/ScopeHint — Jenkins uses BasicAuth (username + API token), not OAuth/PAT scopes. Set RequiredScopes: []string{} and ScopeHint: "" so the scopeHintSuffix renderer doesn't show "Ensure your PAT has these scopes" for Jenkins connections.

  2. Add tests for resolveJenkinsJobs — The resolveJenkinsJobs() function has parsing/validation logic (splitting, trimming, empty-input error) that isn't covered by tests. Add table-driven tests in cmd/configure_scopes_test.go analogous to the existing resolveRepos tests.

  3. Fix index-based test assertion — In TestConnectionRegistry_Jenkins, change def.ScopeFlags[0].Name == "jobs" to loop through the slice looking for a flag named "jobs", so the test doesn't break if more scope flags are added later.

@Codex

Codex AI commented Mar 5, 2026

Copy link
Copy Markdown
Contributor Author

@codex[agent] Please address these code review comments:

  1. Clear misleading RequiredScopes/ScopeHint — Jenkins uses BasicAuth (username + API token), not OAuth/PAT scopes. Set `RequiredScopes:...

Updated Jenkins to leave RequiredScopes/ScopeHint empty, added table-driven resolveJenkinsJobs flag parsing tests, and made the Jenkins scope-flag assertion order-agnostic. (8fc89b6)

@Codex Codex AI changed the title Add Jenkins plugin with BasicAuth and job scoping Addressing PR comments Mar 5, 2026
@ewega ewega requested a review from Copilot March 5, 2026 12:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Comment thread docs/configure-connection.md Outdated
| `--org` | *(required for Copilot)* | GitHub organization slug |
| `--enterprise` | | GitHub enterprise slug (for enterprise-level Copilot metrics) |
| `--name` | `Plugin - org` | Connection display name |
| `--endpoint` | `https://api.github.com/` | API endpoint (use for GitHub Enterprise Server; required for Jenkins) |

Copilot AI Mar 5, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docs say --endpoint is required for Jenkins, but the --endpoint flag row still shows a global default of https://api.github.com/ (which isn’t true for Jenkins, since the Jenkins ConnectionDef has no default endpoint). Consider making this row explicitly plugin-dependent (or calling out that Jenkins has no default and must supply a URL), and ensure the Jenkins example command in this doc includes --endpoint so the guidance is consistent.

Suggested change
| `--endpoint` | `https://api.github.com/` | API endpoint (use for GitHub Enterprise Server; required for Jenkins) |
| `--endpoint` | `https://api.github.com/` (GitHub/Copilot) | API endpoint (override for GitHub Enterprise Server; Jenkins has no default and must supply a URL) |

Copilot uses AI. Check for mistakes.
Comment on lines +136 to +142
// TestAvailablePluginsScopeHints verifies that all available plugins have non-empty
// RequiredScopes and ScopeHint fields so users always see what PAT scopes are needed.
func TestAvailablePluginsScopeHints(t *testing.T) {
for _, def := range AvailableConnections() {
if def.AuthMethod == "BasicAuth" {
continue
}

Copilot AI Mar 5, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test’s comment says it verifies all available plugins have non-empty RequiredScopes/ScopeHint, but the test now skips BasicAuth plugins. Update the comment to match the actual behavior (e.g., that it applies to AccessToken-auth plugins only) to avoid future confusion.

Copilot uses AI. Check for mistakes.
@ewega

ewega commented Mar 5, 2026

Copy link
Copy Markdown
Contributor

@codex[agent] Please fix these consistency issues:

  1. CRITICAL: Revert CRLF line-ending changes — Your commits changed every line in connection_types_test.go (822 changed lines) and internal/devlake/types.go (427 changed lines) by converting CRLF to LF. This inflates the diff and guarantees merge conflicts with other PRs. Only keep your actual code additions — revert the line-ending-only changes. You can do this by checking out the original files from main and re-applying only your additions.

  2. Use emoji literals, not Unicode escapes — Replace all Unicode escape sequences with literal emoji characters. The entire codebase uses literal emoji. Change:

    • \U0001f50e🔎
    • \U0001f4dd📝
    • \u2705
    • Any other Unicode escapes → their literal emoji equivalents
  3. Add Jira-compatible skip logic in TestAvailablePluginsScopeHints — Change your if def.AuthMethod == "BasicAuth" { continue } to if len(def.RequiredScopes) == 0 { continue }. This is more generic and will work when Jira (which also has empty RequiredScopes but uses AccessToken) is added.

@Codex

Codex AI commented Mar 5, 2026

Copy link
Copy Markdown
Contributor Author

@codex[agent] Please fix these consistency issues:

  1. CRITICAL: Revert CRLF line-ending changes — Your commits changed every line in connection_types_test.go (822 changed lines) and `internal...

Restored CRLF line endings on the two files, replaced Unicode escapes with literal emoji, clarified Jenkins endpoint docs/example, and updated the ScopeHint test to skip plugins with empty RequiredScopes. (ae0665f)

@ewega ewega force-pushed the codex/add-jenkins-support branch from ae0665f to 69b5feb Compare March 5, 2026 14:03
@ewega ewega merged commit 8efbe06 into main Mar 5, 2026
10 checks passed
@ewega ewega deleted the codex/add-jenkins-support branch March 25, 2026 17:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Jenkins plugin support

3 participants