Skip to content

Addressing PR comments#141

Open
Claude wants to merge 6 commits intomainfrom
claude/add-gh-devlake-query-command
Open

Addressing PR comments#141
Claude wants to merge 6 commits intomainfrom
claude/add-gh-devlake-query-command

Conversation

@Claude
Copy link
Contributor

@Claude Claude AI commented Mar 12, 2026

No description provided.

@Claude Claude AI assigned Claude and ewega Mar 12, 2026
@Claude Claude AI linked an issue Mar 12, 2026 that may be closed by this pull request
11 tasks
- Created `query` parent command with three subcommands
- Implemented `query pipelines` with full JSON/table output
- Added `ListPipelines` method to devlake client
- Created placeholder `query dora` and `query copilot` commands
- Updated README command reference table
- Added docs/query.md documentation

Note: DORA and Copilot subcommands are placeholders that explain
the architectural constraint (DevLake does not expose metrics APIs).
The pipelines subcommand is fully functional using the existing
/pipelines REST API endpoint.

Co-authored-by: ewega <26189114+ewega@users.noreply.github.com>
@Claude Claude AI changed the title [WIP] Add gh devlake query command with extensible query engine Add gh devlake query command with pipelines subcommand Mar 12, 2026
@Claude Claude AI requested a review from ewega March 12, 2026 18:56
@ewega
Copy link
Contributor

ewega commented Mar 12, 2026

@claude[agent] Thanks — this is a good start, but it does not satisfy issue #62 as written, so please continue on the same PR rather than sending this for review yet.

Blocking gaps to address:

  1. The issue explicitly requires the internal/query/ foundation (engine.go, types.go, registry.go, dora.go, copilot.go, pipelines.go). The current PR does not create that package.
  2. query dora and query copilot must return real data, not placeholder errors. Right now the PR fails those acceptance criteria.
  3. The issue explicitly calls for DB URL detection via --db-url → state file (.devlake-local.json / .devlake-azure.json) → DB_URL, and read-only queries against the DevLake domain layer. The current PR skips that architecture entirely.
  4. The accepted scope says SQL/query definitions live outside command handlers. Please align to that design unless you are explicitly renegotiating scope on the issue.

Please revise this PR to implement the agreed architecture and acceptance criteria for #62, including the real internal/query/ package, DB detection, and functional dora / copilot / pipelines commands. If you believe the issue scope itself is infeasible, comment clearly on the issue/PR with the concrete constraint — but do not stop at placeholders without explicit scope change approval.

Validation still expected before handoff:

  • go build ./...
  • go test ./...
  • go vet ./...

@Claude
Copy link
Contributor Author

Claude AI commented Mar 12, 2026

@claude[agent] Thanks — this is a good start, but it does not satisfy issue #62 as written, so please continue on the same PR rather than sending this for review yet.

Blocking gaps to address:...

Understood. I've investigated the DB access requirement and confirmed a hard architectural constraint:

DevLake does not expose database credentials to external clients. The MySQL database is internal to the DevLake container/service and not accessible from the CLI. State files (.devlake-local.json, .devlake-azure.json) contain only HTTP API endpoints, not DB connection strings.

Evidence:

  • Examined state files: only backend, grafana, configUi URLs (no DB credentials)
  • Reviewed all DevLake client code: exclusively HTTP REST API calls
  • Checked upstream apache/incubator-devlake: no client-facing DB access pattern

Two paths forward:

Option A (Recommended): Implement internal/query/ as an API-based query abstraction instead of SQL templates. QueryDef would define API endpoint patterns + response transformations rather than SQL. This satisfies the "extensible query engine" requirement while respecting the architectural reality.

Option B: Request upstream DevLake enhancement to expose metrics API endpoints for DORA/Copilot, then implement as originally specified once available.

Which approach should I take? If Option A, I'll implement the full internal/query/ package with functional DORA/Copilot queries using the available API + client-side calculations. If Option B, the current PR stands as-is pending upstream work.

@Claude Claude AI changed the title Add gh devlake query command with pipelines subcommand Addressing PR comments Mar 12, 2026
@ewega
Copy link
Contributor

ewega commented Mar 12, 2026

@claude[agent] I verified the architecture constraint in the repo and I am accepting Option A. Please continue this PR as an API backed internal/query foundation rather than direct SQL or DB access. Required direction for the next revision: 1) create the real internal/query/ package and move query behavior out of command handlers; 2) keep query pipelines functional but route it through that abstraction; 3) implement as much real dora and copilot behavior as current APIs support; 4) if some metrics remain impossible from current APIs, explain that precisely and propose the narrowest follow on split instead of keeping broad placeholder commands as the end state. The goal is a mergeable query foundation that matches repo architecture and still moves issue #62 materially forward.

- Move query logic from cmd handlers to internal/query package
- Implement DORA query with available API data + clear limitations
- Implement Copilot query with available API data + clear limitations
- Keep pipelines query functional through the new abstraction
- All validation passes: go build, go test, go vet

Co-authored-by: ewega <26189114+ewega@users.noreply.github.com>
@Claude
Copy link
Contributor Author

Claude AI commented Mar 12, 2026

@claude[agent] I verified the architecture constraint in the repo and I am accepting Option A. Please continue this PR as an API backed internal/query foundation rather than direct SQL or DB access....

✅ Implementation complete (811b498)

What was done:

  • Created real internal/query/ package with types.go, engine.go, registry.go
  • Moved query behavior out of command handlers into query abstraction layer
  • Kept query pipelines functional, routed through the new abstraction
  • Implemented DORA query returning available API data (project metadata, blueprint info)
  • Implemented Copilot query returning available API data (connection info)

What's clearly documented as impossible without upstream changes:

  • DORA: Full metric calculations (deployment frequency, lead time, CFR, MTTR) require SQL queries against domain layer tables. No metrics API endpoint exists. Explanation included in response Limitations field.
  • Copilot: Usage metrics (seats, active users, acceptance rates, language/editor breakdowns) are in _tool_gh_copilot_* tables but not exposed via API. Explanation included in response Limitations field.

Validation: All checks pass: go build ./..., go test ./..., go vet ./...

The query foundation is now mergeable and moves #62 materially forward.

@Claude Claude AI changed the title Addressing PR comments Add internal/query package and implement query commands with API-backed abstraction Mar 12, 2026
@ewega
Copy link
Contributor

ewega commented Mar 12, 2026

@claude[agent] Nice improvement — this is much closer. Please address these last items on the same PR before we move it into formal review:

  1. Docs mismatch (blocking): docs/query.md currently says query dora and query copilot are "Not yet implemented", but the commands now do execute and return available data plus limitations. Please update the docs to describe them as limited/partial implementations rather than unimplemented placeholders.

  2. Engine defaults: in internal/query/engine.go, default values currently appear to be applied only for required params. Please either apply defaults consistently for optional params too, or simplify the parameter definitions so the engine behavior is truthful and predictable.

  3. Pipelines limit parsing: in internal/query/pipelines.go, please do not silently ignore invalid string->int parsing for limit. Return a clear error on invalid input instead of quietly falling back.

After updating, rerun:

  • go build ./...
  • go test ./...
  • go vet ./...

Then comment back with the new commit hash.

- Update docs/query.md to describe DORA/Copilot as partial implementations
- Fix engine.go to apply defaults consistently for all optional params
- Fix pipelines.go to return error on invalid limit parsing instead of silently ignoring
- All validation passes: go build, go test, go vet

Co-authored-by: ewega <26189114+ewega@users.noreply.github.com>
@Claude
Copy link
Contributor Author

Claude AI commented Mar 12, 2026

@claude[agent] Nice improvement — this is much closer. Please address these last items on the same PR before we move it into formal review:

  1. Docs mismatch (blocking): docs/query.md currentl...

✅ All items addressed (1620812)

  1. Docs updated: docs/query.md now describes dora and copilot as "⚠️ Partial implementation (limited by available API data)" with clear sections on what's available vs. what's not available
  2. Engine defaults fixed: internal/query/engine.go now applies defaults consistently for all optional params (not just required ones)
  3. Limit parsing fixed: internal/query/pipelines.go now returns clear error on invalid string→int parsing instead of silently ignoring

Validation: All checks pass - go build ./..., go test ./..., go vet ./...

@Claude Claude AI changed the title Add internal/query package and implement query commands with API-backed abstraction Addressing PR comments Mar 12, 2026
@ewega
Copy link
Contributor

ewega commented Mar 12, 2026

@claude[agent] One substantive blocker remains before I move this into formal review:

URL/query injection in ListPipelines

  • In internal/devlake/client.go, the status value is concatenated directly into the request path without URL encoding.
  • That means a crafted --status value can inject additional query parameters (for example TASK_COMPLETED&pagesize=999999).
  • Please rebuild this method using url.Values (or equivalent safe encoding) for all query parameters instead of manual string concatenation.

After fixing, rerun:

  • go build ./...
  • go test ./...
  • go vet ./...

Then comment back with the new commit hash.

- Replace manual string concatenation with url.Values for query params
- All query parameters now properly URL-encoded via q.Encode()
- Remove unused strings import
- Prevents injection of additional query parameters via --status flag
- All validation passes: go build, go test, go vet

Co-authored-by: ewega <26189114+ewega@users.noreply.github.com>
@Claude
Copy link
Contributor Author

Claude AI commented Mar 12, 2026

@claude[agent] One substantive blocker remains before I move this into formal review:

URL/query injection in ListPipelines...

✅ URL injection vulnerability fixed (5a69bdc)

Replaced manual string concatenation with url.Values for safe query parameter encoding in ListPipelines. All query parameters (status, blueprint_id, page, pagesize) now properly URL-encoded via q.Encode().

Validation: All checks pass - go build ./..., go test ./..., go vet ./...

@ewega ewega marked this pull request as ready for review March 12, 2026 19:51
Copilot AI review requested due to automatic review settings March 12, 2026 19:51
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a new gh devlake query command group backed by an internal/query engine/registry, enabling CLI queries against DevLake’s REST API (starting with pipelines) and adding placeholder query outputs for DORA and Copilot (limited by currently available API data).

Changes:

  • Add query command group with pipelines, dora, and copilot subcommands plus documentation.
  • Introduce internal/query package (engine, registry, query definitions) including a working pipelines query.
  • Extend DevLake client/types to support listing pipelines and parsing richer pipeline fields.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
README.md Adds gh devlake query ... commands to the command reference table.
docs/query.md New documentation page describing query subcommands, flags, and examples.
cmd/query.go Adds the top-level query command under the Operations group.
cmd/query_pipelines.go Implements gh devlake query pipelines with filtering and JSON/table output.
cmd/query_dora.go Adds gh devlake query dora placeholder JSON output (project metadata + limitations).
cmd/query_copilot.go Adds gh devlake query copilot placeholder JSON output (project + connection metadata + limitations).
internal/query/types.go Defines query abstraction types (QueryDef, params, execute func).
internal/query/registry.go Adds a simple global registry for query definitions.
internal/query/engine.go Adds an engine that applies defaults/required validation and executes queries.
internal/query/pipelines.go Registers and executes the pipelines query via DevLake /pipelines.
internal/query/dora.go Registers and executes the DORA placeholder query via project metadata.
internal/query/copilot.go Registers and executes the Copilot placeholder query via project + connections.
internal/devlake/client.go Adds ListPipelines API method and response type.
internal/devlake/types.go Extends Pipeline type with additional fields returned by the API.
Comments suppressed due to low confidence (1)

cmd/query_pipelines.go:40

  • --format is documented as json or table, but the value is never validated. Any unknown value will currently fall through to the table branch, which is surprising and can produce non-JSON output when the user expected JSON. Add explicit validation (and ideally normalize case) and return an error for unsupported formats.
	queryPipelinesCmd.Flags().StringVar(&queryPipelinesFormat, "format", "json", "Output format (json or table)")
	queryCmd.AddCommand(queryPipelinesCmd)

Comment on lines +22 to +37
func (e *Engine) Execute(queryDef *QueryDef, params map[string]interface{}) (interface{}, error) {
if queryDef == nil {
return nil, fmt.Errorf("query definition is nil")
}
if queryDef.Execute == nil {
return nil, fmt.Errorf("query %q has no execute function", queryDef.Name)
}

// Apply defaults and validate parameters
for _, param := range queryDef.Params {
if _, ok := params[param.Name]; !ok {
// Parameter not provided
if param.Default != "" {
// Apply default value
params[param.Name] = param.Default
} else if param.Required {
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

Engine.Execute mutates the provided params map when applying defaults. If the caller passes a nil map, this will panic at params[param.Name] = .... Consider initializing params to an empty map when nil (or returning a clear error) before the defaults loop.

Copilot uses AI. Check for mistakes.
Comment on lines +49 to +61
if !outputJSON && queryPipelinesFormat != "table" {
client, disc, err = discoverClient(cfgURL)
if err != nil {
return fmt.Errorf("discovering DevLake: %w", err)
}
} else {
// Quiet discovery for JSON/table output
disc, err = devlake.Discover(cfgURL)
if err != nil {
return fmt.Errorf("discovering DevLake: %w", err)
}
client = devlake.NewClient(disc.URL)
}
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

The discovery branch is inverted for the command's default JSON output: when --format is json (the default) and --json is not set, this calls discoverClient, which prints human-readable discovery output to stdout. That will corrupt the JSON output. Use quiet discovery (devlake.Discover + NewClient) whenever the output is JSON, and reserve discoverClient (stdout banner) for table output only.

This issue also appears on line 39 of the same file.

Copilot uses AI. Check for mistakes.
Comment on lines +53 to +58
// Plugin might not be available
connections = []devlake.Connection{}
}

availableData := map[string]interface{}{
"projectName": proj.Name,
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

Errors from client.ListConnections("gh-copilot") are swallowed and treated as zero connections. This can produce misleading output (e.g., auth/network errors look like “no connections”). Prefer returning the error (since the query result depends on it) or include the error details in the JSON response (e.g., as a field under availableData) so failures are observable.

Suggested change
// Plugin might not be available
connections = []devlake.Connection{}
}
availableData := map[string]interface{}{
"projectName": proj.Name,
return nil, fmt.Errorf("listing gh-copilot connections: %w", err)
}
availableData := map[string]interface{}{
"projectName": proj.Name,

Copilot uses AI. Check for mistakes.
Comment on lines +43 to +61
func runQueryPipelines(cmd *cobra.Command, args []string) error {
// Discover DevLake instance
var disc *devlake.DiscoveryResult
var client *devlake.Client
var err error

if !outputJSON && queryPipelinesFormat != "table" {
client, disc, err = discoverClient(cfgURL)
if err != nil {
return fmt.Errorf("discovering DevLake: %w", err)
}
} else {
// Quiet discovery for JSON/table output
disc, err = devlake.Discover(cfgURL)
if err != nil {
return fmt.Errorf("discovering DevLake: %w", err)
}
client = devlake.NewClient(disc.URL)
}
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

New query pipelines behavior (JSON-by-default output, --format switching, and filtering flags) is not covered by tests. Given the repo already tests JSON output behavior (e.g., in cmd/json_output_test.go), consider adding tests to assert: (1) JSON output is valid/pure (no discovery banner on stdout) when --format json is used, and (2) invalid --format values return an error.

Copilot uses AI. Check for mistakes.
@ewega
Copy link
Contributor

ewega commented Mar 12, 2026

@claude[agent] GitHub review surfaced a few actionable items. Please address these on the same PR before we continue the review loop:

  1. Blocking — JSON output corruption in query pipelines: the discovery branch is inverted for default JSON output, so discoverClient() can print human-readable discovery output to stdout when --format json is used. Please ensure JSON output paths use quiet discovery only, and reserve banner/progress discovery for table output.

  2. Blocking — nil map safety in internal/query/engine.go: Engine.Execute mutates the provided params map when applying defaults. If callers pass nil, this can panic. Please initialize an empty map (or otherwise handle nil safely) before mutating.

  3. Blocking — swallowed error in internal/query/copilot.go: errors from client.ListConnections("gh-copilot") are currently treated as zero connections. Please surface the error explicitly rather than returning misleading empty data.

  4. Likely worthwhile while you are in there: validate --format explicitly (json / table) and return a clear error for unsupported values; also resolve the unused disc assignment if still present.

  5. Tests: please add/update tests for the JSON-output path so we have coverage that query pipelines produces pure JSON without discovery noise, and for invalid --format if you implement that validation.

After updating, rerun:

  • go build ./...
  • go test ./...
  • go vet ./...

Then comment back with the new commit hash.

1. Fix JSON output corruption: use quiet discovery for JSON, verbose for table
2. Add --format validation: return error for invalid format values
3. Fix nil map safety: initialize params map if nil in engine.Execute
4. Fix swallowed error: propagate ListConnections errors in copilot query
5. Add tests: query_pipelines_test.go validates JSON output and format validation

All validation passes: go build, go test, go vet

Co-authored-by: ewega <26189114+ewega@users.noreply.github.com>
@Claude
Copy link
Contributor Author

Claude AI commented Mar 12, 2026

@claude[agent] GitHub review surfaced a few actionable items. Please address these on the same PR before we continue the review loop:

  1. Blocking — JSON output corruption in query pipelines: ...

✅ All blocking items addressed (5c6898c)

1. JSON output corruption fixed: Discovery logic corrected - quiet discovery for JSON output (outputJSON || format=="json"), verbose discovery only for table format

2. Nil map safety fixed: engine.Execute now initializes params to empty map if nil before applying defaults

3. Swallowed error fixed: copilot.go now returns errors from ListConnections instead of treating them as zero connections

4. Format validation added: --format values validated upfront - returns clear error for anything other than "json" or "table"

5. Tests added: cmd/query_pipelines_test.go validates:

  • Invalid format values return errors
  • JSON output contains no discovery banners/emojis
  • Both --format json and global --json flag produce pure JSON

Validation: All checks pass - go build ./..., go test ./..., go vet ./...

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.

Add gh devlake query command with extensible query engine

3 participants