Skip to content

Comments

refactor(robot): introduce listselect model for project prompts#708

Open
qcserestipy wants to merge 3 commits intogoharbor:mainfrom
qcserestipy:refactor/robot_listselect
Open

refactor(robot): introduce listselect model for project prompts#708
qcserestipy wants to merge 3 commits intogoharbor:mainfrom
qcserestipy:refactor/robot_listselect

Conversation

@qcserestipy
Copy link
Collaborator

@qcserestipy qcserestipy commented Feb 16, 2026

refactor(robot): introduce reusable listselect model for project prompts

Replaces the inline project selection logic in robot create/update with a reusable multi-select list model based on Bubble Tea.

Changes

  • Add listselect model with toggle (space) and confirm (enter) support
  • Add GetProjectNamesFromUser and GetProjectIDsFromUser to prompt package
  • Refactor system robot create/update to use new model

robot_create

- add prompts for project names and IDs
- move functions to prompt package
- make list select reusable
- refactor system robot create and update to use new model

Signed-off-by: Patrick Eschenbach <patrickeschenbach96@gmail.com>
Signed-off-by: Patrick Eschenbach <patrickeschenbach96@gmail.com>
@codecov
Copy link

codecov bot commented Feb 16, 2026

Codecov Report

❌ Patch coverage is 0% with 168 lines in your changes missing coverage. Please review.
✅ Project coverage is 7.17%. Comparing base (60ad0bd) to head (577d468).
⚠️ Report is 95 commits behind head on main.

Files with missing lines Patch % Lines
pkg/views/base/listselect/model.go 0.00% 65 Missing ⚠️
pkg/prompt/prompt.go 0.00% 64 Missing ⚠️
pkg/views/project/listselect/view.go 0.00% 35 Missing ⚠️
cmd/harbor/root/robot/create.go 0.00% 2 Missing ⚠️
cmd/harbor/root/robot/update.go 0.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff            @@
##             main    #708      +/-   ##
=========================================
- Coverage   10.99%   7.17%   -3.82%     
=========================================
  Files         173     262      +89     
  Lines        8671   13032    +4361     
=========================================
- Hits          953     935      -18     
- Misses       7612   11989    +4377     
- Partials      106     108       +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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 refactors the project selection logic in robot account creation and updating by introducing a reusable multi-select list model based on Bubble Tea. The refactoring extracts inline project selection code into a dedicated listselect component that can be reused throughout the application.

Changes:

  • Introduces a new reusable listselect model in pkg/views/base/listselect/ that provides multi-select functionality with toggle (space) and confirm (enter) support
  • Adds wrapper functions in pkg/views/project/listselect/ for project-specific multi-select operations
  • Extends the prompt package with GetProjectNamesFromUser, GetProjectIDsFromUser, and PromptForMoreProjects functions
  • Refactors robot create and update commands to use the new reusable prompt functions instead of inline huh.MultiSelect
  • Cleans up unused code in multiselect model (removes high performance rendering flag and unused max helper function)
  • Adds demo/ directory to .gitignore

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
pkg/views/base/listselect/model.go New reusable Bubble Tea multi-select list model with toggle and confirm functionality
pkg/views/project/listselect/view.go Project-specific wrappers for the listselect model, providing name and ID selection functions
pkg/prompt/prompt.go Added GetProjectNamesFromUser, GetProjectIDsFromUser, and PromptForMoreProjects helper functions
cmd/harbor/root/robot/create.go Removed inline project selection code, now uses prompt.GetProjectNamesFromUser and prompt.PromptForMoreProjects
cmd/harbor/root/robot/update.go Updated to use prompt.GetProjectNamesFromUser and prompt.PromptForMoreProjects
pkg/views/base/multiselect/model.go Cleanup: removed unused useHighPerformanceRenderer constant and max helper function (now using Go built-in)
.gitignore Added demo/ directory to ignore list

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@NucleoFusion
Copy link
Contributor

NucleoFusion commented Feb 17, 2026

@qcserestipy How can I check / review this pr? When I run create it gives me a

Error: failed to get permissions: forbidden: only admins(system and project) can access permissions

Do I need elevated permissions in demo.goharbor.io?

EDIT: Nvm it was some weird issue, had to create a new user

- run tea with altscreen
- return error instead of os.Exit
- print warning if list selection is empty

Signed-off-by: Patrick Eschenbach <patrickeschenbach96@gmail.com>
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

Copilot reviewed 6 out of 7 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package project
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

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

This file declares 'package project' when it's in the 'pkg/views/project/listselect/' directory. While technically valid in Go, having the same package name ('project') in both 'pkg/views/project/select/' and 'pkg/views/project/listselect/' creates confusion. Consider using 'package listselect' to match the directory name and improve code clarity, similar to how 'pkg/views/project/list/' uses 'package list'.

Suggested change
package project
package listselect

Copilot uses AI. Check for mistakes.
Comment on lines +110 to +114
for idx := range m.Selected {
if i, ok := m.List.Items()[idx].(Item); ok {
m.Choices = append(m.Choices, string(i))
}
}
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

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

Iterating over a map in Go produces non-deterministic order. This means the items in m.Choices will be added in random order, which could lead to inconsistent behavior across runs. Consider sorting the indices before iterating, or maintain the selection order in a separate slice to preserve the order in which users selected items.

Copilot uses AI. Check for mistakes.
Comment on lines +28 to +86
func ProjectsList(projects []*models.Project) ([]string, error) {
items := make([]list.Item, len(projects))
for i, p := range projects {
items[i] = listselect.Item(p.Name)
}

m := listselect.NewModel(items, "Project")

p, err := tea.NewProgram(m, tea.WithAltScreen()).Run()

if err != nil {
return nil, fmt.Errorf("error running selection program: %w", err)
}

if model, ok := p.(listselect.Model); ok {
if model.Aborted {
return nil, ErrUserAborted
}
if len(model.Choices) == 0 {
return nil, errors.New("no project selected")
}
return model.Choices, nil
}

return nil, errors.New("unexpected program result")
}

func ProjectsListWithId(projects []*models.Project) ([]int64, error) {
items := make([]list.Item, len(projects))
itemsMap := make(map[string]int64)

for i, p := range projects {
items[i] = listselect.Item(p.Name)
itemsMap[p.Name] = int64(p.ProjectID)
}

m := listselect.NewModel(items, "Project")

p, err := tea.NewProgram(m, tea.WithAltScreen()).Run()
if err != nil {
return nil, fmt.Errorf("error running selection program: %w", err)
}

if model, ok := p.(listselect.Model); ok {
if model.Aborted {
return nil, ErrUserAborted
}
if len(model.Choices) == 0 {
return nil, errors.New("no project selected")
}
var selectedIDs []int64
for _, choice := range model.Choices {
selectedIDs = append(selectedIDs, itemsMap[choice])
}
return selectedIDs, nil
}

return nil, errors.New("unexpected program result")
}
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

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

The functions ProjectsList and ProjectsListWithId share nearly identical logic, differing only in how they process the selected items (returning names vs IDs). Consider refactoring to reduce duplication by creating a single generic function that accepts a transformation function to convert model.Choices to the desired output type.

Copilot uses AI. Check for mistakes.
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.

2 participants