Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@ go.work

/harbor
dist/
demo/
/dagger.gen.go
/internal/*
78 changes: 3 additions & 75 deletions cmd/harbor/root/robot/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import (
"os"

"github.com/atotto/clipboard"
"github.com/charmbracelet/huh"
"github.com/goharbor/go-client/pkg/sdk/v2.0/models"
"github.com/goharbor/harbor-cli/pkg/api"
config "github.com/goharbor/harbor-cli/pkg/config/robot"
Expand Down Expand Up @@ -209,7 +208,7 @@ func getSystemPermissions(all bool, permissions *[]models.Permission) error {
}

func getProjectPermissions(opts *create.CreateView, projectPermissionsMap map[string][]models.Permission) error {
permissionMode, err := promptPermissionMode()
permissionMode, err := prompt.PromptPermissionMode()
if err != nil {
return fmt.Errorf("error selecting permission mode: %v", err)
}
Expand All @@ -228,7 +227,7 @@ func getProjectPermissions(opts *create.CreateView, projectPermissionsMap map[st
}

func handleMultipleProjectsPermissions(projectPermissionsMap map[string][]models.Permission) error {
selectedProjects, err := getMultipleProjectsFromUser()
selectedProjects, err := prompt.GetProjectNamesFromUser()
if err != nil {
return fmt.Errorf("error selecting projects: %v", err)
}
Expand Down Expand Up @@ -263,7 +262,7 @@ func handlePerProjectPermissions(opts *create.CreateView, projectPermissionsMap
return fmt.Errorf("failed to get permissions: %v", utils.ParseHarborErrorMsg(err))
}

moreProjects, err := promptMoreProjects()
moreProjects, err := prompt.PromptForMoreProjects()
if err != nil {
return fmt.Errorf("error asking for more projects: %v", err)
}
Expand Down Expand Up @@ -379,74 +378,3 @@ func exportSecretToFile(name, secret, creationTime string, expiresAt int64) {

fmt.Printf("Secret saved to %s\n", filename)
}

func getMultipleProjectsFromUser() ([]string, error) {
allProjects, err := api.ListAllProjects()
if err != nil {
return nil, fmt.Errorf("failed to list projects: %v", err)
}

var selectedProjects []string
var projectOptions []huh.Option[string]

for _, p := range allProjects.Payload {
projectOptions = append(projectOptions, huh.NewOption(p.Name, p.Name))
}

err = huh.NewForm(
huh.NewGroup(
huh.NewNote().
Title("Multiple Project Selection").
Description("Select the projects to assign the same permissions to this robot account."),
huh.NewMultiSelect[string]().
Title("Select projects").
Options(projectOptions...).
Value(&selectedProjects),
),
).WithTheme(huh.ThemeCharm()).WithWidth(80).Run()

return selectedProjects, err
}

func promptMoreProjects() (bool, error) {
var addMore bool
err := huh.NewForm(
huh.NewGroup(
huh.NewNote().
Title("Project Selection").
Description("You can add permissions for multiple projects to this robot account."),
huh.NewSelect[bool]().
Title("Do you want to select (more) projects?").
Description("Select 'Yes' to add (another) project, 'No' to continue with current selection.").
Options(
huh.NewOption("No", false),
huh.NewOption("Yes", true),
).
Value(&addMore),
),
).WithTheme(huh.ThemeCharm()).WithWidth(60).WithHeight(10).Run()

return addMore, err
}

func promptPermissionMode() (string, error) {
var permissionMode string
err := huh.NewForm(
huh.NewGroup(
huh.NewNote().
Title("Permission Mode").
Description("Select how you want to assign permissions to projects:"),
huh.NewSelect[string]().
Title("Permission Mode").
Description("Choose 'List' to select multiple projects with common permissions, or 'Per Project' for individual project permissions.").
Options(
huh.NewOption("No project permissions (system-level only)", "none"),
huh.NewOption("Per Project", "per_project"),
huh.NewOption("List", "list"),
).
Value(&permissionMode),
),
).WithTheme(huh.ThemeCharm()).WithWidth(60).WithHeight(10).Run()

return permissionMode, err
}
4 changes: 2 additions & 2 deletions cmd/harbor/root/robot/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ func handleMultipleProjectsPermissionsForUpdate(projectPermissionsMap map[string
}
}

selectedProjects, err := getMultipleProjectsFromUser()
selectedProjects, err := prompt.GetProjectNamesFromUser()
if err != nil {
return fmt.Errorf("error selecting projects: %v", err)
}
Expand Down Expand Up @@ -496,7 +496,7 @@ func handlePerProjectPermissionsForUpdate(projectPermissionsMap map[string][]mod

projectPermissionsMap[projectName] = validProjectPerms

moreProjects, err := promptMoreProjects()
moreProjects, err := prompt.PromptForMoreProjects()
if err != nil {
return fmt.Errorf("error asking for more projects: %v", err)
}
Expand Down
111 changes: 111 additions & 0 deletions pkg/prompt/prompt.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ import (
"fmt"
"strconv"

"github.com/charmbracelet/huh"
"github.com/goharbor/harbor-cli/pkg/utils"
"github.com/goharbor/harbor-cli/pkg/views/base/selection"
list "github.com/goharbor/harbor-cli/pkg/views/context/switch"

"github.com/goharbor/go-client/pkg/sdk/v2.0/models"
Expand All @@ -30,6 +32,7 @@ import (
instview "github.com/goharbor/harbor-cli/pkg/views/instance/select"
lview "github.com/goharbor/harbor-cli/pkg/views/label/select"
mview "github.com/goharbor/harbor-cli/pkg/views/member/select"
plistselect "github.com/goharbor/harbor-cli/pkg/views/project/listselect"
pview "github.com/goharbor/harbor-cli/pkg/views/project/select"
qview "github.com/goharbor/harbor-cli/pkg/views/quota/select"
rview "github.com/goharbor/harbor-cli/pkg/views/registry/select"
Expand Down Expand Up @@ -92,6 +95,43 @@ func GetProjectIDFromUser() (int64, error) {
return res.id, res.err
}

func GetProjectIDsFromUser() ([]int64, error) {
type result struct {
ids []int64
err error
}
resultChan := make(chan result)

go func() {
response, err := api.ListAllProjects()
if err != nil {
resultChan <- result{nil, err}
return
}

if len(response.Payload) == 0 {
resultChan <- result{nil, errors.New("no projects found")}
return
}

ids, err := plistselect.ProjectsListWithId(response.Payload)
if err != nil {
if err == plistselect.ErrUserAborted {
resultChan <- result{nil, errors.New("user aborted project selection")}
} else {
resultChan <- result{nil, fmt.Errorf("error during project selection: %w", err)}
}
return
}

resultChan <- result{ids, nil}
}()

res := <-resultChan

return res.ids, res.err
}

func GetProjectNameFromUser() (string, error) {
type result struct {
name string
Expand Down Expand Up @@ -128,6 +168,62 @@ func GetProjectNameFromUser() (string, error) {
return res.name, res.err
}

func GetProjectNamesFromUser() ([]string, error) {
type result struct {
names []string
err error
}
resultChan := make(chan result)

go func() {
response, err := api.ListAllProjects()
if err != nil {
resultChan <- result{nil, err}
return
}

if len(response.Payload) == 0 {
resultChan <- result{nil, errors.New("no projects found")}
return
}

names, err := plistselect.ProjectsList(response.Payload)
if err != nil {
if err == plistselect.ErrUserAborted {
resultChan <- result{nil, errors.New("user aborted project selection")}
} else {
resultChan <- result{nil, fmt.Errorf("error during project selection: %w", err)}
}
return
}

resultChan <- result{names, nil}
}()

res := <-resultChan
return res.names, res.err
}

func PromptForMoreProjects() (bool, error) {
var addMore bool
err := huh.NewForm(
huh.NewGroup(
huh.NewNote().
Title("Project Selection").
Description("You can add permissions for multiple projects to this robot account."),
huh.NewSelect[bool]().
Title("Would you like to add another project?").
Description("Select 'Yes' to add a project, 'No' to continue with your current selection.").
Options(
huh.NewOption("No", false),
huh.NewOption("Yes", true),
).
Value(&addMore),
),
).WithTheme(huh.ThemeCharm()).WithWidth(60).WithHeight(10).Run()
return addMore, err
}

// GetRoleNameFromUser prompts the user to select a role and returns it.
func GetRoleNameFromUser() int64 {
roleChan := make(chan int64)
Expand Down Expand Up @@ -424,3 +520,18 @@ func GetRoleIDFromUser() int64 {

return <-roleID
}

func PromptPermissionMode() (string, error) {
options := []string{
"none",
"per_project",
"list",
}
permissionMode, err := selection.SelectFromOptionsListString(
options,
"Permission Mode", "Choose 'list' to select multiple projects with common \npermissions, or 'per_project' for individual project \npermissions or 'none' for no project permissions (system-level only).")
if err != nil {
return "", err
}
return permissionMode, err
}
Loading
Loading