Skip to content

configure scope add/list/delete: Add CRUD subcommands for scope management #55

@ewega

Description

@ewega

Problem

configure scope is currently a single command with one action: add scopes to a connection. There are no subcommands for listing or removing scopes. This is inconsistent with configure connection, which has list, delete, update, and test subcommands. Users who have added scopes cannot inspect or remove them without using the DevLake API directly.

Additionally, the current scope command auto-detects connection IDs from state files, which can lead to ambiguity when multiple connections exist for the same plugin. Connection ID should be an explicit, mandatory input for standalone scope commands.

Current command tree

configure scope              ← RunE = add scopes (only action)

Desired command tree

configure scope              ← no RunE, prints help
  ├── add                    ← current scope logic (interactive or flags)
  ├── list                   ← list scopes on a connection
  └── delete                 ← remove a scope from a connection

Dependencies

Blocked by:

Blocks:

Parallel with: #56 (project CRUD) — independent files, can be done in parallel

Design decisions

Connection ID is mandatory

A scope is always tied to a specific plugin connection. For standalone scope subcommands:

  • Flag mode: --plugin and --connection-id are required
  • Interactive mode: show a connection picker (list all connections across plugins, let user choose)

The orchestrators (configureAllPhases, scopeAllConnections) continue to resolve connection IDs internally since they just created the connections.

Plugin-specific flags stay on add

The scope add command retains all current flags. Plugin-specific flags (--repos for GitHub, --enterprise for Copilot) are addressed separately in #58.

Scope of changes

1. Create cmd/configure_scope_add.go

Move runConfigureScopes() from configure_scopes.go into a new add subcommand:

func newScopeAddCmd() *cobra.Command {
    var opts ScopeOpts
    cmd := &cobra.Command{
        Use:   "add",
        Short: "Add scopes (repos, orgs) to an existing connection",
        RunE: func(cmd *cobra.Command, args []string) error {
            return runScopeAdd(cmd, args, &opts)
        },
    }
    // Move all current scope flags here
    cmd.Flags().StringVar(&opts.Plugin, "plugin", "", ...)
    cmd.Flags().IntVar(&opts.ConnectionID, "connection-id", 0, ...)
    // ...
    return cmd
}

2. Create cmd/configure_scope_list.go

// gh devlake configure scope list --plugin github --connection-id 1
// gh devlake configure scope list   (interactive: pick a connection)

Implementation:

  • In flag mode, require --plugin + --connection-id
  • In interactive mode, use pickConnection() (same helper used by delete/update/test in connection commands)
  • Call existing client.ListScopes(plugin, connID) — this API already exists
  • Render results as a table: Scope ID | Name | Full Name

3. Create cmd/configure_scope_delete.go

// gh devlake configure scope delete --plugin github --connection-id 1 --scope-id 12345
// gh devlake configure scope delete   (interactive: pick connection, then pick scope)

Implementation:

  • In flag mode, require --plugin + --connection-id + --scope-id
  • In interactive mode: pick connection → list scopes → pick scope to delete
  • Add new client method: client.DeleteScope(plugin, connID, scopeID)
  • DevLake API: DELETE /plugins/{plugin}/connections/{connId}/scopes/{scopeId}
  • Confirm before deletion (warn about blueprint impact)

4. Update configure_scopes.go

  • Remove RunE from the parent scope command
  • Remove flag registrations (they move to add)
  • Register add, list, delete as subcommands

5. Add DeleteScope to internal/devlake/client.go

func (c *Client) DeleteScope(plugin string, connID int, scopeID string) error {
    url := fmt.Sprintf("%s/plugins/%s/connections/%d/scopes/%s", c.BaseURL, plugin, connID, scopeID)
    req, _ := http.NewRequest(http.MethodDelete, url, nil)
    resp, err := c.HTTPClient.Do(req)
    // ... error handling ...
}

6. Verify orchestrators are unaffected

scopeAllConnections() in helpers.go calls scopeGitHub() and scopeCopilot() directly — it does not invoke the Cobra command. The restructuring should not affect it.

Acceptance criteria

  • gh devlake configure scope prints help showing add, list, delete
  • gh devlake configure scope add --plugin github --connection-id 1 --org my-org --repos org/repo1 works
  • gh devlake configure scope add enters interactive mode (pick plugin, resolve connection)
  • gh devlake configure scope list --plugin github --connection-id 1 shows a table of scopes
  • gh devlake configure scope list (interactive) lets user pick a connection, then shows scopes
  • gh devlake configure scope delete --plugin github --connection-id 1 --scope-id 12345 deletes a scope
  • Interactive delete picks connection → lists scopes → confirms deletion
  • --connection-id and --plugin are required in flag mode for all subcommands
  • Orchestrators (configureAllPhases, scopeAllConnections) continue to work
  • go build ./... and go test ./... pass
  • README updated

References

  • cmd/configure_scopes.go — current scope command (to be restructured)
  • cmd/configure_connection_delete.go — pattern for interactive picker + flag mode duality
  • cmd/configure_connection_list.go — pattern for tabular output
  • internal/devlake/client.goListScopes() already exists; need DeleteScope()
  • DevLake API: DELETE /plugins/{plugin}/connections/{connId}/scopes/{scopeId}
  • cmd/helpers.goscopeAllConnections() (should be unaffected)

Metadata

Metadata

Assignees

Labels

enhancementNew feature or requestrefactorCode restructure, no behavior change

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions