diff --git a/pkg/cmd/channel/create/create.go b/pkg/cmd/channel/create/create.go index d9d6e1d5..2dbd9fd7 100644 --- a/pkg/cmd/channel/create/create.go +++ b/pkg/cmd/channel/create/create.go @@ -14,7 +14,6 @@ import ( "github.com/OctopusDeploy/cli/pkg/util/flag" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/channels" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/lifecycles" - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/projects" "github.com/spf13/cobra" ) @@ -136,17 +135,10 @@ func PromptMissing(opts *CreateOptions) error { return err } - var selectedProject *projects.Project - if opts.Project.Value == "" { - selectedProject, err = selectors.Project("Select the project in which the channel will be created", opts.Client, opts.Ask) - if err != nil { - return err - } - } else { - selectedProject, err = selectors.FindProject(opts.Client, opts.Project.Value) - if err != nil { - return err - } + selectedProject, err := selectors.ResolveProject(opts.Client, opts.Ask, true, + "Select the project in which the channel will be created", opts.Project.Value) + if err != nil { + return err } opts.Project.Value = selectedProject.Name diff --git a/pkg/cmd/channel/list/list.go b/pkg/cmd/channel/list/list.go index 7b3061ca..f8f73cb1 100644 --- a/pkg/cmd/channel/list/list.go +++ b/pkg/cmd/channel/list/list.go @@ -1,7 +1,6 @@ package list import ( - "errors" "strings" "github.com/MakeNowJust/heredoc/v2" @@ -11,7 +10,6 @@ import ( "github.com/OctopusDeploy/cli/pkg/output" "github.com/OctopusDeploy/cli/pkg/question/selectors" "github.com/OctopusDeploy/cli/pkg/util/flag" - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/projects" "github.com/spf13/cobra" ) @@ -83,30 +81,13 @@ func listRun(cmd *cobra.Command, f factory.Factory, flags *ListFlags) error { return err } - var selectedProject *projects.Project - if f.IsPromptEnabled() { - if projectNameOrID == "" { - selectedProject, err = selectors.Project("Select the project to list channels for", octopus, f.Ask) - if err != nil { - return err - } - } else { - selectedProject, err = selectors.FindProject(octopus, projectNameOrID) - if err != nil { - return err - } - if !constants.IsProgrammaticOutputFormat(outputFormat) { - cmd.Printf("Project %s\n", output.Cyan(selectedProject.Name)) - } - } - } else { - if projectNameOrID == "" { - return errors.New("project must be specified") - } - selectedProject, err = selectors.FindProject(octopus, projectNameOrID) - if err != nil { - return err - } + selectedProject, err := selectors.ResolveProject(octopus, f.Ask, f.IsPromptEnabled(), + "Select the project to list channels for", projectNameOrID) + if err != nil { + return err + } + if f.IsPromptEnabled() && projectNameOrID != "" && !constants.IsProgrammaticOutputFormat(outputFormat) { + cmd.Printf("Project %s\n", output.Cyan(selectedProject.Name)) } // Projects.GetChannels handles paging internally and returns the project-scoped list. diff --git a/pkg/cmd/release/create/create.go b/pkg/cmd/release/create/create.go index 9c79f4dc..a5292ad8 100644 --- a/pkg/cmd/release/create/create.go +++ b/pkg/cmd/release/create/create.go @@ -429,18 +429,12 @@ func AskQuestions(octopus *octopusApiClient.Client, stdout io.Writer, asker ques // we should emulate that so there is always a line where you can see what the item was when specified on the command line, // however if we support a "quiet mode" then we shouldn't emit those - var err error - var selectedProject *projects.Project - if options.ProjectName == "" { - selectedProject, err = selectors.Project("Select the project in which the release will be created", octopus, asker) - if err != nil { - return err - } - } else { // project name is already provided, fetch the object because it's needed for further questions - selectedProject, err = selectors.FindProject(octopus, options.ProjectName) - if err != nil { - return err - } + selectedProject, err := selectors.ResolveProject(octopus, asker, true, + "Select the project in which the release will be created", options.ProjectName) + if err != nil { + return err + } + if options.ProjectName != "" { // project name was already provided; echo it so the choice is always visible _, _ = fmt.Fprintf(stdout, "Project %s\n", output.Cyan(selectedProject.Name)) } options.ProjectName = selectedProject.Name diff --git a/pkg/cmd/release/delete/delete.go b/pkg/cmd/release/delete/delete.go index 59b2fee2..a387a270 100644 --- a/pkg/cmd/release/delete/delete.go +++ b/pkg/cmd/release/delete/delete.go @@ -86,23 +86,30 @@ func deleteRun(cmd *cobra.Command, f factory.Factory, flags *Flags, args []strin return err } - var selectedProject *projects.Project - var releasesToDelete []*releases.Release - - if f.IsPromptEnabled() { // this would be AskQuestions if it were bigger + // in automation mode we validate the flags up front, before making any API calls + if !f.IsPromptEnabled() { if projectNameOrID == "" { - selectedProject, err = selectors.Project("Select the project to delete a release in", octopus, f.Ask) - if err != nil { - return err - } - } else { // project name is already provided, fetch the object because it's needed for further questions - selectedProject, err = selectors.FindProject(octopus, projectNameOrID) - if err != nil { - return err - } - cmd.Printf("Project %s\n", output.Cyan(selectedProject.Name)) + return errors.New("project must be specified") + } + if len(versionsToDelete) == 0 { + return errors.New("at least one release version must be specified") } + } + + selectedProject, err := selectors.ResolveProject(octopus, f.Ask, f.IsPromptEnabled(), + "Select the project to delete a release in", projectNameOrID) + if err != nil { + return err + } + // echo the project when it came from the command line, so there is always a line + // showing what was selected + if f.IsPromptEnabled() && projectNameOrID != "" { + cmd.Printf("Project %s\n", output.Cyan(selectedProject.Name)) + } + + var releasesToDelete []*releases.Release + if f.IsPromptEnabled() { // this would be AskQuestions if it were bigger if len(versionsToDelete) == 0 { releasesToDelete, err = selectReleases(octopus, selectedProject, f.Ask) if err != nil { @@ -137,18 +144,6 @@ func deleteRun(cmd *cobra.Command, f factory.Factory, flags *Flags, args []strin } } else { // we don't have the executions API backing us and allowing NameOrID; we need to do the lookups ourselves - // validation - if projectNameOrID == "" { - return errors.New("project must be specified") - } - if len(versionsToDelete) == 0 { - return errors.New("at least one release version must be specified") - } - - selectedProject, err = selectors.FindProject(octopus, projectNameOrID) - if err != nil { - return err - } releasesToDelete, err = findReleases(octopus, selectedProject, versionsToDelete) if err != nil { return err diff --git a/pkg/cmd/release/deploy/deploy.go b/pkg/cmd/release/deploy/deploy.go index 7637a51a..0df6d614 100644 --- a/pkg/cmd/release/deploy/deploy.go +++ b/pkg/cmd/release/deploy/deploy.go @@ -392,17 +392,11 @@ func AskQuestions(octopus *octopusApiClient.Client, stdout io.Writer, asker ques var err error // select project - var selectedProject *projects.Project - if options.ProjectName == "" { - selectedProject, err = selectors.Project("Select project", octopus, asker) - if err != nil { - return err - } - } else { // project name is already provided, fetch the object because it's needed for further questions - selectedProject, err = selectors.FindProject(octopus, options.ProjectName) - if err != nil { - return err - } + selectedProject, err := selectors.ResolveProject(octopus, asker, true, "Select project", options.ProjectName) + if err != nil { + return err + } + if options.ProjectName != "" { // project name was already provided; echo it so the choice is always visible _, _ = fmt.Fprintf(stdout, "Project %s\n", output.Cyan(selectedProject.Name)) } options.ProjectName = selectedProject.Name diff --git a/pkg/cmd/release/list/list.go b/pkg/cmd/release/list/list.go index ce0d8785..babcb881 100644 --- a/pkg/cmd/release/list/list.go +++ b/pkg/cmd/release/list/list.go @@ -1,7 +1,6 @@ package list import ( - "errors" "time" "github.com/MakeNowJust/heredoc/v2" @@ -13,7 +12,6 @@ import ( "github.com/OctopusDeploy/cli/pkg/util" "github.com/OctopusDeploy/cli/pkg/util/flag" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/channels" - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/projects" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/releases" "github.com/spf13/cobra" ) @@ -79,30 +77,15 @@ func listRun(cmd *cobra.Command, f factory.Factory, flags *ListFlags) error { return err } - var selectedProject *projects.Project - if f.IsPromptEnabled() { // this would be AskQuestions if it were bigger - if projectNameOrID == "" { - selectedProject, err = selectors.Project("Select the project to list releases for", octopus, f.Ask) - if err != nil { - return err - } - } else { // project name is already provided, fetch the object because it's needed for further questions - selectedProject, err = selectors.FindProject(octopus, projectNameOrID) - if err != nil { - return err - } - if !constants.IsProgrammaticOutputFormat(outputFormat) { - cmd.Printf("Project %s\n", output.Cyan(selectedProject.Name)) - } - } - } else { // we don't have the executions API backing us and allowing NameOrID; we need to do the lookup ourselves - if projectNameOrID == "" { - return errors.New("project must be specified") - } - selectedProject, err = selectors.FindProject(octopus, projectNameOrID) - if err != nil { - return err - } + selectedProject, err := selectors.ResolveProject(octopus, f.Ask, f.IsPromptEnabled(), + "Select the project to list releases for", projectNameOrID) + if err != nil { + return err + } + // echo the project when it came from the command line, so there is always a line + // showing what was selected + if f.IsPromptEnabled() && projectNameOrID != "" && !constants.IsProgrammaticOutputFormat(outputFormat) { + cmd.Printf("Project %s\n", output.Cyan(selectedProject.Name)) } foundReleases, err := octopus.Projects.GetReleases(selectedProject) // does paging internally diff --git a/pkg/cmd/runbook/list/list.go b/pkg/cmd/runbook/list/list.go index a3fcc2b1..44af9c41 100644 --- a/pkg/cmd/runbook/list/list.go +++ b/pkg/cmd/runbook/list/list.go @@ -14,7 +14,6 @@ import ( "github.com/OctopusDeploy/cli/pkg/output" "github.com/OctopusDeploy/cli/pkg/question/selectors" "github.com/OctopusDeploy/cli/pkg/util/flag" - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/projects" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/resources" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/runbooks" "github.com/spf13/cobra" @@ -95,30 +94,15 @@ func listRun(cmd *cobra.Command, f factory.Factory, flags *ListFlags) error { return err } - var selectedProject *projects.Project - if f.IsPromptEnabled() { // this would be AskQuestions if it were bigger - if projectNameOrID == "" { - selectedProject, err = selectors.Project("Select the project to list runbooks for", octopus, f.Ask) - if err != nil { - return err - } - } else { // project name is already provided, fetch the object because it's needed for further questions - selectedProject, err = selectors.FindProject(octopus, projectNameOrID) - if err != nil { - return err - } - if !constants.IsProgrammaticOutputFormat(outputFormat) { - cmd.Printf("Project %s\n", output.Cyan(selectedProject.Name)) - } - } - } else { // we don't have the executions API backing us and allowing NameOrID; we need to do the lookup ourselves - if projectNameOrID == "" { - return errors.New("project must be specified") - } - selectedProject, err = selectors.FindProject(octopus, projectNameOrID) - if err != nil { - return err - } + selectedProject, err := selectors.ResolveProject(octopus, f.Ask, f.IsPromptEnabled(), + "Select the project to list runbooks for", projectNameOrID) + if err != nil { + return err + } + // echo the project when it came from the command line, so there is always a line + // showing what was selected + if f.IsPromptEnabled() && projectNameOrID != "" && !constants.IsProgrammaticOutputFormat(outputFormat) { + cmd.Printf("Project %s\n", output.Cyan(selectedProject.Name)) } var foundRunbooks *resources.Resources[*runbooks.Runbook] diff --git a/pkg/cmd/runbook/run/run.go b/pkg/cmd/runbook/run/run.go index a05f567a..c5b507b0 100644 --- a/pkg/cmd/runbook/run/run.go +++ b/pkg/cmd/runbook/run/run.go @@ -550,25 +550,7 @@ func runGitRunbook(cmd *cobra.Command, f factory.Factory, flags *RunFlags, octop } func selectProject(octopus *octopusApiClient.Client, f factory.Factory, projectName string) (*projects.Project, error) { - if projectName == "" { - if f.IsPromptEnabled() { - selectedProject, err := selectors.Project("Select project", octopus, f.Ask) - if err != nil { - return nil, err - } - return selectedProject, nil - } else { - // Project name not provided and not asking questions so error out - return nil, errors.New("project must be specified") - } - } else { // project name is already provided, fetch the object because it's needed for further questions - selectedProject, err := selectors.FindProject(octopus, projectName) - if err != nil { - return nil, err - } - - return selectedProject, nil - } + return selectors.ResolveProject(octopus, f.Ask, f.IsPromptEnabled(), "Select project", projectName) } // shouldAskAdvancedOptions determines if we should prompt the user to change advanced options. diff --git a/pkg/cmd/runbook/snapshot/list/list.go b/pkg/cmd/runbook/snapshot/list/list.go index ee2c111b..da6055aa 100644 --- a/pkg/cmd/runbook/snapshot/list/list.go +++ b/pkg/cmd/runbook/snapshot/list/list.go @@ -12,7 +12,6 @@ import ( "github.com/OctopusDeploy/cli/pkg/output" "github.com/OctopusDeploy/cli/pkg/question/selectors" "github.com/OctopusDeploy/cli/pkg/util/flag" - "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/projects" "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/runbooks" "github.com/spf13/cobra" ) @@ -82,24 +81,19 @@ func listRun(cmd *cobra.Command, f factory.Factory, flags *ListFlags) error { projectNameOrID := flags.Project.Value runbookNameOrID := flags.Runbook.Value - var selectedProject *projects.Project + selectedProject, err := selectors.ResolveProject(client, f.Ask, f.IsPromptEnabled(), + "Select the project to list runbook snapshots for", projectNameOrID) + if err != nil { + return err + } + // echo the project when it came from the command line, so there is always a line + // showing what was selected + if f.IsPromptEnabled() && projectNameOrID != "" && !constants.IsProgrammaticOutputFormat(outputFormat) { + cmd.Printf("Project %s\n", output.Cyan(selectedProject.Name)) + } + var selectedRunbook *runbooks.Runbook if f.IsPromptEnabled() { // this would be AskQuestions if it were bigger - if projectNameOrID == "" { - selectedProject, err = selectors.Project("Select the project to list runbook snapshots for", client, f.Ask) - if err != nil { - return err - } - } else { // project name is already provided, fetch the object because it's needed for further questions - selectedProject, err = selectors.FindProject(client, projectNameOrID) - if err != nil { - return err - } - if !constants.IsProgrammaticOutputFormat(outputFormat) { - cmd.Printf("Project %s\n", output.Cyan(selectedProject.Name)) - } - } - if runbookNameOrID == "" { selectedRunbook, err = selectors.Runbook("Select the runbook to list snapshots for", client, f.Ask, selectedProject.GetID()) if err != nil { @@ -115,14 +109,6 @@ func listRun(cmd *cobra.Command, f factory.Factory, flags *ListFlags) error { } } } else { // we don't have the executions API backing us and allowing NameOrID; we need to do the lookup ourselves - if projectNameOrID == "" { - return errors.New("project must be specified") - } - selectedProject, err = selectors.FindProject(client, projectNameOrID) - if err != nil { - return err - } - if runbookNameOrID == "" { return errors.New("runbook must be specified") }