feat(configure): add project add/list/delete CRUD subcommands#74
Conversation
Co-authored-by: ewega <26189114+ewega@users.noreply.github.com>
project add/list/delete CRUD subcommands
There was a problem hiding this comment.
Pull request overview
This PR restructures configure project from a single command into a parent with three subcommands (add, list, delete), matching the CRUD pattern established by configure connection. The core creation logic is unchanged — it is simply moved to the new add subcommand.
Changes:
- Added
listanddeletesubcommands (configure_project_list.go,configure_project_delete.go) plusaddwrapper (configure_project_add.go) - Stripped the
RunEand flags from the parentconfigure_projects.go; addedListProjects()andDeleteProject()to the DevLake client - Updated all command reference strings (
configure_scopes.go,configure_full.go,README.md) to useconfigure project add
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
internal/devlake/types.go |
Adds ProjectListResponse struct for GET /projects |
internal/devlake/client.go |
Adds ListProjects() (via doGet) and DeleteProject() (raw HTTP, matching DeleteConnection) |
cmd/configure_projects.go |
Parent command stripped of RunE and flags; registers three subcommands |
cmd/configure_project_add.go |
Thin add subcommand wrapping the existing runConfigureProjects logic |
cmd/configure_project_list.go |
list subcommand rendering a tabwriter table; supports --json |
cmd/configure_project_delete.go |
delete subcommand with interactive project picker and confirmation prompt |
cmd/configure_project_test.go |
Structural tests for subcommand registration, NoRunE, and flag presence |
cmd/configure_scopes.go |
Help text reference updated to configure project add |
cmd/configure_full.go |
Long description example updated to configure project add |
README.md |
Command reference table expanded with add, list, delete entries |
cmd/configure_project_delete.go
Outdated
| var projectDeleteName string | ||
|
|
||
| func newProjectDeleteCmd() *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: "delete", | ||
| Short: "Delete a DevLake project", | ||
| Long: `Deletes a DevLake project by name. | ||
|
|
||
| If --name is not specified, prompts interactively. | ||
|
|
||
| ⚠️ Deleting a project also removes its blueprint and sync schedule. | ||
|
|
||
| Examples: | ||
| gh devlake configure project delete | ||
| gh devlake configure project delete --name my-project`, | ||
| RunE: runProjectDelete, | ||
| } | ||
| cmd.Flags().StringVar(&projectDeleteName, "name", "", "Name of the project to delete") |
There was a problem hiding this comment.
The projectDeleteName package-level variable is inconsistent with the opts struct pattern used by the sibling newProjectAddCmd() in configure_project_add.go, which declares var opts ProjectOpts locally inside the constructor. Using a package-level variable for flag state means the flag value persists between test runs and could cause subtle issues in test suites that call newProjectDeleteCmd() multiple times. The --name flag should be bound to a locally-scoped variable (e.g. declared inside newProjectDeleteCmd()), consistent with the opts pattern established in configure_project_add.go and configure_scopes.go (line 36).
There was a problem hiding this comment.
Fixed in cea4de5 — projectDeleteName is now declared locally inside newProjectDeleteCmd() and passed to runProjectDelete as a parameter, consistent with the opts pattern used by the sibling commands.
cmd/configure_project_delete.go
Outdated
| if name == "" { | ||
| return fmt.Errorf("--name is required (or omit for interactive mode)") | ||
| } | ||
|
|
There was a problem hiding this comment.
The if name == "" guard at lines 71-73 is unreachable dead code. By the time execution reaches it, name is guaranteed to be non-empty: if --name was provided, it was copied from the non-empty projectDeleteName at line 43; if the interactive path was taken, the code already returned early at line 66 when chosen == "", so name was assigned from chosen (which is non-empty). This guard can never trigger and should be removed.
| if name == "" { | |
| return fmt.Errorf("--name is required (or omit for interactive mode)") | |
| } |
There was a problem hiding this comment.
Removed in cea4de5 — the dead if name == "" guard is gone.
|
@copilot Three documentation gaps to fix:
|
…docs Co-authored-by: ewega <26189114+ewega@users.noreply.github.com>
All three done in cea4de5:
|
ListProjects()andDeleteProject()tointernal/devlake/client.goProjectListResponsetointernal/devlake/types.gocmd/configure_project_add.go(delegates to existingrunConfigureProjects)cmd/configure_project_list.go(table + JSON output)cmd/configure_project_delete.go(flag mode + interactive picker + confirmation)projectDeleteNameto locally-scoped var insidenewProjectDeleteCmd()(opts pattern)if name == ""guard (dead code)cmd/configure_projects.go(remove RunE/flags, register add/list/delete subcommands)cmd/configure_full.goandcmd/configure_scopes.gohelp text referencesconfigure_project_test.go)docs/configure-project.mdto document all three subcommands (add/list/delete).github/skills/devlake-dev-architecture/SKILL.mdcommand treeAGENTS.mdwith command tree showing project add/list/deletego build ./...,go test ./..., andgo vet ./...all passOriginal prompt
This section details on the original issue you should resolve
<issue_title>
configure project add/list/delete: Add CRUD subcommands for project management</issue_title><issue_description>## Problem
configure projectis currently a single command that creates a project. There are no subcommands for listing existing projects or deleting them. This is inconsistent with the CRUD pattern established byconfigure connection(which hasadd,list,update,delete,test) and the restructuring planned forconfigure scopein #55.Users who want to inspect what projects exist, or remove a stale project, must use the DevLake API directly (or the Config UI).
Current command tree
Desired command tree
Dependencies
Blocked by:
configure connection add: Extract connection creation intoaddsubcommand #54 (connection add) — sets the extract-to-addpattern this issue followsBlocks: nothing directly
Parallel with: #55 (scope CRUD) — independent files, can be done in parallel
Scope of changes
1. Create
cmd/configure_project_add.goMove the current
runConfigureProjects()fromconfigure_projects.gointo a newaddsubcommand:2. Create
cmd/configure_project_list.go// gh devlake configure project listImplementation:
client.ListProjects()→GET /projectsName | Description | Blueprint ID | Blueprint Status3. Create
cmd/configure_project_delete.goImplementation:
--nameclient.DeleteProject(name)→DELETE /projects/{name}4. Update
configure_projects.goRunEfrom the parent project commandadd)add,list,deleteas subcommands5. Add client methods to
internal/devlake/client.goNote: The exact response shape of
GET /projectsshould be verified against the DevLake API. The upstream code is atbackend/server/api/project/project.goin theapache/incubator-devlakerepo.6. Verify orchestrators are unaffected
collectAndFinalizeProject()inhelpers.gocallsfinalizeProject()directly — it does not invoke the Cobra command. The restructuring should not affect orchestrators.Acceptance criteria
gh devlake configure projectprints help showingadd,list,deletegh devlake configure project addcreates a project (same as current behavior)gh devlake configure project add --project-name my-teamworks in flag modegh devlake configure project listshows a table of all projectsgh devlake configure project delete --name my-projectdeletes a projectconfigureAllPhases,collectAndFinalizeProject) continue to workgo build ./...andgo test ./...passReferences
cmd/configure_projects.go— current project command (to be restructured)cmd/helpers.go—collectAndFinalizeProject(),finalizeProject()(should be unaffected)internal/devlake/client.go—CreateProject(),GetProject()exist; needListProjects(),DeleteProject()GET /projects, `DELETE /...configure project add/list/delete: Add CRUD subcommands for project management #56💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.