Skip to content

Add configure connection delete subcommand#25

Merged
ewega merged 3 commits into
mainfrom
copilot/add-configure-connection-delete
Feb 19, 2026
Merged

Add configure connection delete subcommand#25
ewega merged 3 commits into
mainfrom
copilot/add-configure-connection-delete

Conversation

Copilot AI commented Feb 19, 2026

Copy link
Copy Markdown
Contributor
  • Add DeleteConnection(plugin string, connID int) error method to internal/devlake/client.go
  • Create cmd/configure_connection_delete.go with delete subcommand under configureConnectionsCmd
    • Flag mode: --plugin + --id required (use cmd.Flags().Changed to detect partial invocations and fail fast)
    • Interactive mode: list all connections, prompt select, confirm with scope-loss warning
    • After deletion, remove from state file
    • Graceful error (404 special-cased) if connection not found
    • \n prefix before stderr warning on state file update failure
  • Create cmd/configure_connection_delete_test.go with tests including partial flag mode cases
  • Update README.md with new command documentation
  • Build and all tests pass
  • All review comments addressed
Original prompt

This section details on the original issue you should resolve

<issue_title>Add configure connection delete command</issue_title>
<issue_description>## Summary
Add a configure connection delete subcommand to remove connections that are broken, test connections, or no longer needed.

Design (Option A: under configure connection)

gh devlake configure connection delete # interactive gh devlake configure connection delete --plugin gh-copilot --id 3 # flag-based

Interactive flow:

  1. List all connections across plugins
  2. User selects one to delete
  3. Confirmation prompt with warning about losing scopes
  4. DELETE /plugins/{plugin}/connections/{id}
  5. Update state file

Changes

  • Add delete subcommand under configure connection
  • Interactive: list connections, prompt select, confirm
  • Flag-based: --plugin + --id required
  • Call DELETE /plugins/{plugin}/connections/{id}
  • Remove from state file after successful deletion
  • Add DeleteConnection method to devlake.Client

Acceptance Criteria

  • Interactive mode lists connections and prompts for confirmation
  • Flag mode works with --plugin + --id
  • State file updated after deletion
  • Confirmation prompt warns about scope deletion
  • Graceful error if connection ID doesn't exist
    </issue_description>

<agent_instructions>v0.3.4 just shipped — commands are now singular (connection, scope, project), --plugin flag exists, and configure connection list is a subcommand of configureConnectionsCmd. Work off the latest main.

Key implementation points:

  1. Create a new file cmd/configure_connection_delete.go with a delete subcommand under configureConnectionsCmd
  2. Add DeleteConnection(plugin string, connID int) method to internal/devlake/client.go using DELETE /plugins/{plugin}/connections/{id}
  3. Interactive mode: use configure connection list logic to show connections, prompt user to select one, then confirm with a warning about losing scopes
  4. Flag mode: --plugin + --id required
  5. After successful deletion, remove the connection from the state file using existing state helpers
  6. Graceful error if connection ID doesn't exist
  7. Follow terminal output rules in .github/instructions/terminal-output.instructions.md
  8. Update README.md with the new command documentation
  9. Add tests in configure_connection_delete_test.go
  10. Run go build ./... and go test ./... to verify</agent_instructions>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Co-authored-by: ewega <26189114+ewega@users.noreply.github.com>
Copilot AI changed the title [WIP] Add configure connection delete command Add configure connection delete subcommand Feb 19, 2026
Copilot AI requested a review from ewega February 19, 2026 17:15
@ewega ewega requested a review from Copilot February 19, 2026 17:27

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 a new gh devlake configure connection delete subcommand to remove DevLake plugin connections (interactive selection or --plugin/--id flags), wiring it through a new DevLake API client delete method and documenting the command.

Changes:

  • Add Client.DeleteConnection(plugin, id) that calls DELETE /plugins/{plugin}/connections/{id}.
  • Introduce configure connection delete command with interactive selection + confirmation and state-file update.
  • Add basic command registration/validation tests and README documentation.

Reviewed changes

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

File Description
internal/devlake/client.go Adds DeleteConnection API call for deleting connections by plugin + ID.
cmd/configure_connection_delete.go Implements the new configure connection delete subcommand (interactive + flag mode), confirmation flow, and state update.
cmd/configure_connection_delete_test.go Adds tests for subcommand registration and unknown-plugin validation.
README.md Documents the new delete command usage and behavior.

Comment thread cmd/configure_connection_delete.go Outdated
plugin := connDeletePlugin
connID := connDeleteID

if plugin == "" || connID == 0 {

Copilot AI Feb 19, 2026

Copy link

Choose a reason for hiding this comment

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

The interactive fallback currently triggers when either --plugin or --id is missing (if plugin == "" || connID == 0). This means a partially-specified flag invocation (e.g. --id without --plugin) unexpectedly drops into interactive mode instead of failing fast, which contradicts the documented/required “flag mode requires both” behavior. Consider treating it as flag mode when either flag is set (use cmd.Flags().Changed(...)) and return an error unless both values are provided; reserve interactive mode only when neither flag is set.

Suggested change
if plugin == "" || connID == 0 {
pluginFlagSet := cmd.Flags().Changed("plugin")
idFlagSet := cmd.Flags().Changed("id")
if pluginFlagSet || idFlagSet {
// Flag mode: if either flag is set, require both to be provided and valid
if !pluginFlagSet || !idFlagSet || plugin == "" || connID == 0 {
return fmt.Errorf("both --plugin and --id must be provided together")
}
} else {

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in c4307f4. Flag-mode detection now uses cmd.Flags().Changed("plugin") / cmd.Flags().Changed("id") early (before discovery), so partial invocations fail immediately with "both --plugin and --id must be provided together". Interactive mode is only entered when neither flag is set.

Comment thread cmd/configure_connection_delete.go Outdated
updated = append(updated, c)
}
if err := devlake.UpdateConnections(statePath, state, updated); err != nil {
fmt.Fprintf(os.Stderr, "⚠️ Could not update state file: %v\n", err)

Copilot AI Feb 19, 2026

Copy link

Choose a reason for hiding this comment

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

Per .github/instructions/terminal-output.instructions.md, emoji-prefixed lines should start with a leading blank line (\n) so they read as distinct steps. This warning is emitted without a preceding blank line, so it can jam against the previous output; prepend a newline (or add an extra fmt.Println() before it) to keep spacing consistent.

Suggested change
fmt.Fprintf(os.Stderr, "⚠️ Could not update state file: %v\n", err)
fmt.Fprintf(os.Stderr, "\n⚠️ Could not update state file: %v\n", err)

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in c4307f4 — prepended \n to the stderr warning: fmt.Fprintf(os.Stderr, "\n⚠️ Could not update state file: %v\n", err).

Comment on lines +139 to +142
body, _ := io.ReadAll(resp.Body)
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent {
return fmt.Errorf("delete connection returned %d: %s", resp.StatusCode, body)
}

Copilot AI Feb 19, 2026

Copy link

Choose a reason for hiding this comment

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

DeleteConnection returns a generic status/body error for all non-200/204 responses. The UX requirement mentions a graceful error when the connection ID doesn’t exist; consider special-casing 404 to return a clearer “not found” error (including plugin + id), and consider including the HTTP method/path in the generic error (e.g. DELETE /plugins/{plugin}/connections/{id}) to make troubleshooting easier.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in c4307f4. DeleteConnection now special-cases 404 with "connection not found: plugin=%s id=%d" and includes the full path in the generic error: "DELETE /plugins/%s/connections/%d returned %d: %s".

if !strings.Contains(err.Error(), "unknown plugin") {
t.Errorf("unexpected error message: %v", err)
}
}

Copilot AI Feb 19, 2026

Copy link

Choose a reason for hiding this comment

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

The tests cover subcommand registration and unknown-plugin validation, but don’t exercise the new command’s required flag-mode behavior (e.g. erroring when only one of --plugin / --id is provided, and accepting when both are provided). Adding a test for these cases will help prevent regressions in the interactive-vs-flag resolution logic.

Suggested change
}
}
func TestRunDeleteConnection_FlagsPluginOnly(t *testing.T) {
origPlugin := connDeletePlugin
origID := connDeleteID
t.Cleanup(func() {
connDeletePlugin = origPlugin
connDeleteID = origID
})
connDeletePlugin = ""
connDeleteID = 0
cmd := &cobra.Command{RunE: runDeleteConnection}
cmd.Flags().StringVar(&connDeletePlugin, "plugin", "", "")
cmd.Flags().IntVar(&connDeleteID, "id", 0, "")
_ = cmd.Flags().Set("plugin", "gitlab")
err := runDeleteConnection(cmd, nil)
if err == nil {
t.Fatal("expected error when only --plugin is provided, got nil")
}
}
func TestRunDeleteConnection_FlagsIDOnly(t *testing.T) {
origPlugin := connDeletePlugin
origID := connDeleteID
t.Cleanup(func() {
connDeletePlugin = origPlugin
connDeleteID = origID
})
connDeletePlugin = ""
connDeleteID = 0
cmd := &cobra.Command{RunE: runDeleteConnection}
cmd.Flags().StringVar(&connDeletePlugin, "plugin", "", "")
cmd.Flags().IntVar(&connDeleteID, "id", 0, "")
_ = cmd.Flags().Set("id", "1")
err := runDeleteConnection(cmd, nil)
if err == nil {
t.Fatal("expected error when only --id is provided, got nil")
}
}

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added in c4307f4: TestRunDeleteConnection_PluginOnlyNoID and TestRunDeleteConnection_IDOnlyNoPlugin both verify that partial flag invocations return "both --plugin and --id must be provided together".

@ewega

ewega commented Feb 19, 2026

Copy link
Copy Markdown
Contributor

@copilot apply changes based on the comments in this thread

…tests

Co-authored-by: ewega <26189114+ewega@users.noreply.github.com>
@ewega ewega marked this pull request as ready for review February 19, 2026 18:04
@ewega ewega merged commit c901269 into main Feb 19, 2026
@ewega ewega deleted the copilot/add-configure-connection-delete branch March 2, 2026 12:51
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 configure connection delete command

3 participants