diff --git a/cmd/component/action/update.go b/cmd/component/action/update.go new file mode 100644 index 0000000..c9cf2f9 --- /dev/null +++ b/cmd/component/action/update.go @@ -0,0 +1,110 @@ +package action + +import ( + "fmt" + + "bunnyshell.com/cli/cmd/environment/action" + "bunnyshell.com/cli/pkg/api/common" + "bunnyshell.com/cli/pkg/api/component" + "bunnyshell.com/cli/pkg/api/environment" + "bunnyshell.com/cli/pkg/build" + "bunnyshell.com/cli/pkg/config" + githelper "bunnyshell.com/cli/pkg/helper/git" + "bunnyshell.com/cli/pkg/lib" + "bunnyshell.com/cli/pkg/util" + "github.com/spf13/cobra" +) + +type EditComponentData struct { + common.ItemOptions + + K8SIntegration string + + GitTarget string + + WithDeploy bool +} + +var commandExample = fmt.Sprintf(`This command updates the Git details for a component + +You can update both the repository and the Git branch / ref: +%[1]s%[2]s components update --id dMVwZO5jGN --git-target https://github.com/my-fork/my-repo@my-main + +You can update only the Git branch / ref: +%[1]s%[2]s components update --id dMVwZO5jGN --git-target @fix/bug-1234 +.`, "\t", build.Name) + +func init() { + settings := config.GetSettings() + options := config.GetOptions() + + editComponentsData := &EditComponentData{ + ItemOptions: *common.NewItemOptions(""), + } + + command := &cobra.Command{ + Use: "update", + + Example: commandExample, + + ValidArgsFunction: cobra.NoFileCompletions, + + RunE: func(cmd *cobra.Command, args []string) error { + editComponentsData.ID = settings.Profile.Context.ServiceComponent + + gitRepository, gitRef, err := githelper.ParseGitSec(editComponentsData.GitTarget) + if err != nil { + return lib.FormatCommandError(cmd, fmt.Errorf("invalid git spec for %s: %w", editComponentsData.GitTarget, err)) + } + + model, err := component.Get(&editComponentsData.ItemOptions) + if err != nil { + return lib.FormatCommandError(cmd, err) + } + + if settings.IsStylish() { + cmd.Printf(`Updating component "%s" (%s)%s`, editComponentsData.ID, model.GetName(), "\n\n") + } + + editOptions := environment.NewEditComponentOptions() + editOptions.ID = model.GetEnvironment() + editOptions.Component = model.GetName() + editOptions.TargetRepository = gitRepository + editOptions.TargetBranch = gitRef + + _, err = environment.EditComponents(editOptions) + if err != nil { + return lib.FormatCommandError(cmd, err) + } + + if settings.IsStylish() { + cmd.Printf("Successfully updated component...%s", "\n\n") + } + + if !editComponentsData.WithDeploy { + return lib.FormatCommandData(cmd, model) + } + + deployOptions := &editOptions.DeployOptions + deployOptions.ID = model.GetEnvironment() + + if err = action.HandleDeploy(cmd, deployOptions, "updated", editComponentsData.K8SIntegration); err != nil { + return err + } + + return lib.FormatCommandData(cmd, model) + }, + } + + flags := command.Flags() + + flags.StringVar(&editComponentsData.GitTarget, "git-target", editComponentsData.GitTarget, "Target git spec (e.g. https://github.com/fork/templates@main)") + util.MarkFlagRequiredWithHelp(flags.Lookup("git-target"), "The target git spec (e.g. https://github.com/fork/templates@main)") + + flags.AddFlag(options.ServiceComponent.GetRequiredFlag("id")) + + flags.BoolVar(&editComponentsData.WithDeploy, "deploy", editComponentsData.WithDeploy, "Deploy the environment after update") + flags.StringVar(&editComponentsData.K8SIntegration, "k8s", editComponentsData.K8SIntegration, "Set Kubernetes integration for the environment (if not set)") + + mainCmd.AddCommand(command) +} diff --git a/cmd/environment/action/create.go b/cmd/environment/action/create.go index 9dd6b80..e85bca0 100644 --- a/cmd/environment/action/create.go +++ b/cmd/environment/action/create.go @@ -99,7 +99,7 @@ func init() { deployOptions := &createOptions.DeployOptions deployOptions.ID = model.GetId() - return handleDeploy(cmd, deployOptions, "created", createOptions.GetKubernetesIntegration()) + return HandleDeploy(cmd, deployOptions, "created", createOptions.GetKubernetesIntegration()) }, } diff --git a/cmd/environment/action/deploy.go b/cmd/environment/action/deploy.go index 30e31fb..640ce15 100644 --- a/cmd/environment/action/deploy.go +++ b/cmd/environment/action/deploy.go @@ -29,7 +29,7 @@ func init() { RunE: func(cmd *cobra.Command, args []string) error { deployOptions.ID = settings.Profile.Context.Environment - return handleDeploy(cmd, deployOptions, "", deployData.K8SIntegration) + return HandleDeploy(cmd, deployOptions, "", deployData.K8SIntegration) }, } diff --git a/cmd/environment/action/root.go b/cmd/environment/action/root.go index 1691d5a..1888b23 100644 --- a/cmd/environment/action/root.go +++ b/cmd/environment/action/root.go @@ -40,7 +40,7 @@ func validateActionOptions(actionOptions *common.ActionOptions) error { return fmt.Errorf("%w when following pipelines", lib.ErrNotStylish) } -func handleDeploy(cmd *cobra.Command, deployOptions *environment.DeployOptions, action string, kubernetesIntegration string) error { +func HandleDeploy(cmd *cobra.Command, deployOptions *environment.DeployOptions, action string, kubernetesIntegration string) error { if err := ensureKubernetesIntegration(deployOptions, kubernetesIntegration); err != nil { return err } @@ -107,6 +107,7 @@ func ensureKubernetesIntegration(deployOptions *environment.DeployOptions, kuber } editSettingsOptions := environment.NewEditSettingsOptions(deployOptions.ID) + editSettingsOptions.UpdateEditSettingsForType(model.GetType()) editSettingsOptions.EnvironmentEditSettings.KubernetesIntegration.Set(&kubernetesIntegration) diff --git a/cmd/environment/action/update.build_settings.go b/cmd/environment/action/update.build_settings.go new file mode 100644 index 0000000..a841490 --- /dev/null +++ b/cmd/environment/action/update.build_settings.go @@ -0,0 +1,103 @@ +package action + +import ( + "bunnyshell.com/cli/pkg/api/build_settings" + "bunnyshell.com/cli/pkg/api/environment" + "bunnyshell.com/cli/pkg/config" + "bunnyshell.com/cli/pkg/config/enum" + "bunnyshell.com/cli/pkg/lib" + "bunnyshell.com/cli/pkg/util" + "bunnyshell.com/sdk" + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +func init() { + options := config.GetOptions() + settings := config.GetSettings() + + useClusterProjectSettings := enum.BoolFalse + useRegistryProjectSettings := enum.BoolFalse + + editBuildSettingsOptions := environment.NewEditBuildSettingsOptions("") + + command := &cobra.Command{ + Use: "update-build-settings", + + ValidArgsFunction: cobra.NoFileCompletions, + + RunE: func(cmd *cobra.Command, args []string) error { + editBuildSettingsOptions.ID = settings.Profile.Context.Environment + + parseEditBuildSettingsOptions(cmd.Flags(), editBuildSettingsOptions, useClusterProjectSettings, useRegistryProjectSettings) + + _, err := environment.EditBuildSettings(editBuildSettingsOptions) + if err != nil { + return lib.FormatCommandError(cmd, err) + } + + model, err := build_settings.CheckBuildSettingsValidation[sdk.EnvironmentItem]( + environment.Get, + &editBuildSettingsOptions.EditOptions, + settings.IsStylish(), + ) + if err != nil { + return lib.FormatCommandError(cmd, err) + } + + return lib.FormatCommandData(cmd, model) + }, + } + + flags := command.Flags() + + flags.AddFlag(options.Environment.GetFlag("id", util.FlagRequired)) + + useClusterProjectSettingsFlag := enum.BoolFlag( + &useClusterProjectSettings, + "use-project-k8s", + "Use the project build cluster settings", + ) + flags.AddFlag(useClusterProjectSettingsFlag) + useClusterProjectSettingsFlag.NoOptDefVal = "true" + + useRegistryProjectSettingsFlag := enum.BoolFlag( + &useRegistryProjectSettings, + "use-project-registry", + "Use the project build registry settings", + ) + flags.AddFlag(useRegistryProjectSettingsFlag) + useRegistryProjectSettingsFlag.NoOptDefVal = "true" + + editBuildSettingsOptions.UpdateFlagSet(flags) + + // use-project-settings excludes the other build settings flags for the cluster + command.MarkFlagsMutuallyExclusive("use-project-k8s", "use-managed-k8s") + command.MarkFlagsMutuallyExclusive("use-project-k8s", "k8s") + command.MarkFlagsMutuallyExclusive("use-project-k8s", "cpu") + command.MarkFlagsMutuallyExclusive("use-project-k8s", "memory") + + command.MarkFlagsMutuallyExclusive("use-project-registry", "use-managed-registry") + command.MarkFlagsMutuallyExclusive("use-project-registry", "registry") + + mainCmd.AddCommand(command) +} + +func parseEditBuildSettingsOptions( + flags *pflag.FlagSet, + editBuildSettingsOptions *environment.EditBuildSettingsOptions, + useClusterProjectSettings enum.Bool, + useRegistryProjectSettings enum.Bool, +) { + if useClusterProjectSettings == enum.BoolTrue { + editBuildSettingsOptions.EditData.UseManagedCluster = enum.BoolFalse + editBuildSettingsOptions.SetKubernetesIntegration("") + editBuildSettingsOptions.Cpu = sdk.NullableString{} + editBuildSettingsOptions.Memory = sdk.NullableInt32{} + } + + if useRegistryProjectSettings == enum.BoolTrue { + editBuildSettingsOptions.EditData.UseManagedRegistry = enum.BoolFalse + editBuildSettingsOptions.SetRegistryIntegration("") + } +} diff --git a/cmd/environment/action/update.components.go b/cmd/environment/action/update.components.go index f42d68e..37a540b 100644 --- a/cmd/environment/action/update.components.go +++ b/cmd/environment/action/update.components.go @@ -2,13 +2,13 @@ package action import ( "fmt" - "net/url" "strings" "bunnyshell.com/cli/pkg/api/component/git" "bunnyshell.com/cli/pkg/api/environment" "bunnyshell.com/cli/pkg/build" "bunnyshell.com/cli/pkg/config" + githelper "bunnyshell.com/cli/pkg/helper/git" "bunnyshell.com/cli/pkg/lib" "bunnyshell.com/cli/pkg/util" "bunnyshell.com/sdk" @@ -18,10 +18,10 @@ import ( type EditComponentsSource struct { // filters Component string - Source string + GitSource string // updates - Target string + GitTarget string // deployment K8SIntegration string @@ -30,13 +30,13 @@ type EditComponentsSource struct { var commandExample = fmt.Sprintf(`This command updates the Git details for components in an environment. You can update the Git details for a specific component in an environment by using the --component-name flag: -%[1]s%[2]s env update-components --component-name my-component --target https://github.com/my-fork/my-repo@my-main +%[1]s%[2]s env update-components --component-name my-component --git-target https://github.com/my-fork/my-repo@my-main You can update all components matching a specific repository: -%[1]s%[2]s env update-components --source https://github.com/original/repo --target git@github.com/my-fork/my-repo +%[1]s%[2]s env update-components --git-source https://github.com/original/repo --git-target git@github.com/my-fork/my-repo You can update all components matching a specific branch: -%[1]s%[2]s env update-components --source @main --target @feature-branch`, "\t", build.Name) +%[1]s%[2]s env update-components --git-source @main --git-target @feature-branch`, "\t", build.Name) func init() { options := config.GetOptions() @@ -86,7 +86,7 @@ func init() { deployOptions := &editOptions.DeployOptions deployOptions.ID = model.GetId() - if err = handleDeploy(cmd, deployOptions, "updated", editSource.K8SIntegration); err != nil { + if err = HandleDeploy(cmd, deployOptions, "updated", editSource.K8SIntegration); err != nil { return err } @@ -100,14 +100,14 @@ func init() { editOptions.UpdateFlagSet(flags) - flags.StringVar(&editSource.Target, "target", editSource.Target, "Target git spec (e.g. https://github.com/fork/templates@main)") + flags.StringVar(&editSource.GitTarget, "git-target", editSource.GitTarget, "Target git spec (e.g. https://github.com/fork/templates@main)") - targetFlag := flags.Lookup("target") + targetFlag := flags.Lookup("git-target") util.MarkFlagRequiredWithHelp(targetFlag, "Update components git repository and branch. Example: https://github.com/my-fork/my-repo@my-branch") - flags.StringVar(&editSource.Source, "source", editSource.Source, "Filter by git spec (e.g. https://github.com/bunnyshell/templates@main)") + flags.StringVar(&editSource.GitSource, "git-source", editSource.GitSource, "Filter by git spec (e.g. https://github.com/bunnyshell/templates@main)") flags.StringVar(&editSource.Component, "component-name", editSource.Component, "Filter by component name") - command.MarkFlagsMutuallyExclusive("source", "component-name") + command.MarkFlagsMutuallyExclusive("git-source", "component-name") mainCmd.AddCommand(command) } @@ -150,10 +150,10 @@ func componentToString(matched []sdk.ComponentGitCollection) string { } func fillWithGitSpec(editSource *EditComponentsSource, editOptions *environment.EditComponentOptions) error { - if editSource.Target != "" { - target, branch, err := parseGitSec(editSource.Target) + if editSource.GitTarget != "" { + target, branch, err := githelper.ParseGitSec(editSource.GitTarget) if err != nil { - return fmt.Errorf("invalid git spec for %s: %w", editSource.Target, err) + return fmt.Errorf("invalid git spec for %s: %w", editSource.GitTarget, err) } editOptions.TargetRepository = target @@ -166,13 +166,13 @@ func fillWithGitSpec(editSource *EditComponentsSource, editOptions *environment. return nil } - if editSource.Source == "" { + if editSource.GitSource == "" { return nil } - target, branch, err := parseGitSec(editSource.Source) + target, branch, err := githelper.ParseGitSec(editSource.GitSource) if err != nil { - return fmt.Errorf("invalid git spec for %s: %w", editSource.Source, err) + return fmt.Errorf("invalid git spec for %s: %w", editSource.GitSource, err) } editOptions.SourceRepository = target @@ -180,23 +180,3 @@ func fillWithGitSpec(editSource *EditComponentsSource, editOptions *environment. return nil } - -func parseGitSec(spec string) (string, string, error) { - if spec[0] == '@' { - return "", spec[1:], nil - } - - info, err := url.Parse(spec) - if err != nil { - return "", "", err - } - - if !strings.Contains(info.Path, "@") { - return spec, "", nil - } - - chunks := strings.SplitN(info.Path, "@", 2) - info.Path = chunks[0] - - return info.String(), chunks[1], nil -} diff --git a/cmd/environment/action/update.configuration.go b/cmd/environment/action/update.configuration.go index 0ebcb36..0579851 100644 --- a/cmd/environment/action/update.configuration.go +++ b/cmd/environment/action/update.configuration.go @@ -77,7 +77,7 @@ func init() { deployOptions := &editConfigurationOptions.DeployOptions deployOptions.ID = model.GetId() - return handleDeploy(cmd, deployOptions, "updated", editConfigurationOptions.K8SIntegration) + return HandleDeploy(cmd, deployOptions, "updated", editConfigurationOptions.K8SIntegration) }, } diff --git a/cmd/environment/action/update.settings.go b/cmd/environment/action/update.settings.go index 48743d2..9dda4f4 100644 --- a/cmd/environment/action/update.settings.go +++ b/cmd/environment/action/update.settings.go @@ -22,6 +22,12 @@ func init() { RunE: func(cmd *cobra.Command, args []string) error { editSettingsOptions.ID = settings.Profile.Context.Environment + environmentModel, err := environment.Get(&editSettingsOptions.ItemOptions) + if err != nil { + return lib.FormatCommandError(cmd, err) + } + editSettingsOptions.UpdateEditSettingsForType(environmentModel.GetType()) + model, err := environment.EditSettings(editSettingsOptions) if err != nil { return lib.FormatCommandError(cmd, err) diff --git a/cmd/project/action/create.go b/cmd/project/action/create.go new file mode 100644 index 0000000..620a2c3 --- /dev/null +++ b/cmd/project/action/create.go @@ -0,0 +1,67 @@ +package action + +import ( + "errors" + + "bunnyshell.com/cli/pkg/api" + "bunnyshell.com/cli/pkg/api/project" + "bunnyshell.com/cli/pkg/config" + "bunnyshell.com/cli/pkg/lib" + "bunnyshell.com/cli/pkg/util" + "github.com/spf13/cobra" +) + +func init() { + options := config.GetOptions() + settings := config.GetSettings() + + createOptions := project.NewCreateOptions() + + command := &cobra.Command{ + Use: "create", + + ValidArgsFunction: cobra.NoFileCompletions, + + RunE: func(cmd *cobra.Command, args []string) error { + createOptions.Organization = settings.Profile.Context.Organization + + model, err := project.Create(createOptions) + if err != nil { + var apiError api.Error + + if errors.As(err, &apiError) { + return handleCreateErrors(cmd, apiError, createOptions) + } + + return lib.FormatCommandError(cmd, err) + } + + return lib.FormatCommandData(cmd, model) + }, + } + + flags := command.Flags() + + flags.AddFlag(options.Organization.AddFlagWithExtraHelp( + "organization", + "Organization for the project", + "Organizations contain projects with their build settings and project variables", + util.FlagRequired, + )) + + createOptions.UpdateFlagSet(flags) + + mainCmd.AddCommand(command) +} + +func handleCreateErrors(cmd *cobra.Command, apiError api.Error, createOptions *project.CreateOptions) error { + if len(apiError.Violations) == 0 { + return apiError + } + + for _, violation := range apiError.Violations { + cmd.Printf("Problem with creation: %s\n", violation.GetMessage()) + } + + return lib.ErrGeneric +} diff --git a/cmd/project/action/delete.go b/cmd/project/action/delete.go new file mode 100644 index 0000000..ff1004f --- /dev/null +++ b/cmd/project/action/delete.go @@ -0,0 +1,40 @@ +package action + +import ( + "bunnyshell.com/cli/pkg/api/project" + "bunnyshell.com/cli/pkg/config" + "bunnyshell.com/cli/pkg/lib" + "github.com/spf13/cobra" +) + +func init() { + options := config.GetOptions() + settings := config.GetSettings() + + deleteOptions := project.NewDeleteOptions() + + command := &cobra.Command{ + Use: "delete", + + ValidArgsFunction: cobra.NoFileCompletions, + + RunE: func(cmd *cobra.Command, args []string) error { + deleteOptions.ID = settings.Profile.Context.Project + + err := project.Delete(deleteOptions) + if err != nil { + return lib.FormatCommandError(cmd, err) + } + + cmd.Printf("\nProject %s successfully deleted\n", deleteOptions.ID) + + return nil + }, + } + + flags := command.Flags() + + flags.AddFlag(options.Project.GetRequiredFlag("id")) + + mainCmd.AddCommand(command) +} diff --git a/cmd/project/action/root.go b/cmd/project/action/root.go new file mode 100644 index 0000000..8329147 --- /dev/null +++ b/cmd/project/action/root.go @@ -0,0 +1,11 @@ +package action + +import ( + "github.com/spf13/cobra" +) + +var mainCmd = &cobra.Command{} + +func GetMainCommand() *cobra.Command { + return mainCmd +} diff --git a/cmd/project/action/update.build_settings.go b/cmd/project/action/update.build_settings.go new file mode 100644 index 0000000..69694aa --- /dev/null +++ b/cmd/project/action/update.build_settings.go @@ -0,0 +1,52 @@ +package action + +import ( + "bunnyshell.com/cli/pkg/api/build_settings" + "bunnyshell.com/cli/pkg/api/project" + "bunnyshell.com/cli/pkg/config" + "bunnyshell.com/cli/pkg/lib" + "bunnyshell.com/cli/pkg/util" + "bunnyshell.com/sdk" + "github.com/spf13/cobra" +) + +func init() { + options := config.GetOptions() + settings := config.GetSettings() + + editBuildSettingsOptions := project.NewEditBuildSettingsOptions("") + + command := &cobra.Command{ + Use: "update-build-settings", + + ValidArgsFunction: cobra.NoFileCompletions, + + RunE: func(cmd *cobra.Command, args []string) error { + editBuildSettingsOptions.ID = settings.Profile.Context.Project + + _, err := project.EditBuildSettings(editBuildSettingsOptions) + if err != nil { + return lib.FormatCommandError(cmd, err) + } + + model, err := build_settings.CheckBuildSettingsValidation[sdk.ProjectItem]( + project.Get, + &editBuildSettingsOptions.EditOptions, + settings.IsStylish(), + ) + if err != nil { + return lib.FormatCommandError(cmd, err) + } + + return lib.FormatCommandData(cmd, model) + }, + } + + flags := command.Flags() + + flags.AddFlag(options.Project.GetFlag("id", util.FlagRequired)) + + editBuildSettingsOptions.UpdateFlagSet(flags) + + mainCmd.AddCommand(command) +} diff --git a/cmd/project/action/update.settings.go b/cmd/project/action/update.settings.go new file mode 100644 index 0000000..54e2fe0 --- /dev/null +++ b/cmd/project/action/update.settings.go @@ -0,0 +1,41 @@ +package action + +import ( + "bunnyshell.com/cli/pkg/api/project" + "bunnyshell.com/cli/pkg/config" + "bunnyshell.com/cli/pkg/lib" + "bunnyshell.com/cli/pkg/util" + "github.com/spf13/cobra" +) + +func init() { + options := config.GetOptions() + settings := config.GetSettings() + + editSettingsOptions := project.NewEditSettingsOptions("") + + command := &cobra.Command{ + Use: "update-settings", + + ValidArgsFunction: cobra.NoFileCompletions, + + RunE: func(cmd *cobra.Command, args []string) error { + editSettingsOptions.ID = settings.Profile.Context.Project + + model, err := project.EditSettings(editSettingsOptions) + if err != nil { + return lib.FormatCommandError(cmd, err) + } + + return lib.FormatCommandData(cmd, model) + }, + } + + flags := command.Flags() + + flags.AddFlag(options.Project.GetFlag("id", util.FlagRequired)) + + editSettingsOptions.UpdateFlagSet(flags) + + mainCmd.AddCommand(command) +} diff --git a/cmd/project/root.go b/cmd/project/root.go index f718d8a..f07b9bf 100644 --- a/cmd/project/root.go +++ b/cmd/project/root.go @@ -1,7 +1,9 @@ package project import ( + "bunnyshell.com/cli/cmd/project/action" "bunnyshell.com/cli/pkg/config" + "bunnyshell.com/cli/pkg/util" "github.com/spf13/cobra" ) @@ -11,10 +13,21 @@ var mainCmd = &cobra.Command{ Short: "Projects", Long: "Bunnyshell Projects", + + ValidArgsFunction: cobra.NoFileCompletions, } func init() { config.MainManager.CommandWithAPI(mainCmd) + + util.AddGroupedCommands( + mainCmd, + cobra.Group{ + ID: "actions", + Title: "Commands for Projects:", + }, + action.GetMainCommand().Commands(), + ) } func GetMainCommand() *cobra.Command { diff --git a/cmd/project_variable/action/create.go b/cmd/project_variable/action/create.go new file mode 100644 index 0000000..7fe8887 --- /dev/null +++ b/cmd/project_variable/action/create.go @@ -0,0 +1,46 @@ +package action + +import ( + "bunnyshell.com/cli/pkg/api/project_variable" + "bunnyshell.com/cli/pkg/config" + "bunnyshell.com/cli/pkg/lib" + "bunnyshell.com/cli/pkg/util" + "github.com/spf13/cobra" +) + +func init() { + options := config.GetOptions() + settings := config.GetSettings() + + createOptions := project_variable.NewCreateOptions() + + command := &cobra.Command{ + Use: "create", + + ValidArgsFunction: cobra.NoFileCompletions, + + RunE: func(cmd *cobra.Command, args []string) error { + createOptions.Project = settings.Profile.Context.Project + + model, err := project_variable.Create(createOptions) + if err != nil { + return lib.FormatCommandError(cmd, err) + } + + return lib.FormatCommandData(cmd, model) + }, + } + + flags := command.Flags() + + flags.AddFlag(options.Project.AddFlagWithExtraHelp( + "project", + "Project for the variable", + "Projects contain multiple variables and build settings", + util.FlagRequired, + )) + + createOptions.UpdateFlagSet(flags) + + mainCmd.AddCommand(command) +} diff --git a/cmd/project_variable/action/delete.go b/cmd/project_variable/action/delete.go new file mode 100644 index 0000000..5634346 --- /dev/null +++ b/cmd/project_variable/action/delete.go @@ -0,0 +1,34 @@ +package action + +import ( + "bunnyshell.com/cli/pkg/api/project_variable" + "bunnyshell.com/cli/pkg/lib" + "github.com/spf13/cobra" +) + +func init() { + deleteOptions := project_variable.NewDeleteOptions() + + command := &cobra.Command{ + Use: "delete", + + ValidArgsFunction: cobra.NoFileCompletions, + + RunE: func(cmd *cobra.Command, args []string) error { + err := project_variable.Delete(deleteOptions) + if err != nil { + return lib.FormatCommandError(cmd, err) + } + + cmd.Printf("\nProject %s successfully deleted\n", deleteOptions.ID) + + return nil + }, + } + + flags := command.Flags() + + flags.AddFlag(GetIDOption(&deleteOptions.ID).GetRequiredFlag("id")) + + mainCmd.AddCommand(command) +} diff --git a/cmd/project_variable/action/edit.go b/cmd/project_variable/action/edit.go new file mode 100644 index 0000000..fbd542b --- /dev/null +++ b/cmd/project_variable/action/edit.go @@ -0,0 +1,39 @@ +package action + +import ( + "bunnyshell.com/cli/pkg/api/project_variable" + "bunnyshell.com/cli/pkg/lib" + "github.com/spf13/cobra" +) + +func init() { + editOptions := project_variable.NewEditOptions("") + + command := &cobra.Command{ + Use: "edit", + + ValidArgsFunction: cobra.NoFileCompletions, + + RunE: func(cmd *cobra.Command, args []string) error { + flags := cmd.Flags() + if flags.Changed("value") { + editOptions.ProjectVariableEditAction.SetValue(flags.Lookup("value").Value.String()) + } + + model, err := project_variable.Edit(editOptions) + if err != nil { + return lib.FormatCommandError(cmd, err) + } + + return lib.FormatCommandData(cmd, model) + }, + } + + flags := command.Flags() + + flags.AddFlag(GetIDOption(&editOptions.ID).GetRequiredFlag("id")) + + editOptions.UpdateFlagSet(flags) + + mainCmd.AddCommand(command) +} diff --git a/cmd/project_variable/action/root.go b/cmd/project_variable/action/root.go new file mode 100644 index 0000000..11a09a5 --- /dev/null +++ b/cmd/project_variable/action/root.go @@ -0,0 +1,28 @@ +package action + +import ( + "fmt" + + "bunnyshell.com/cli/pkg/build" + "bunnyshell.com/cli/pkg/config/option" + "github.com/spf13/cobra" +) + +var mainCmd = &cobra.Command{} + +func GetMainCommand() *cobra.Command { + return mainCmd +} + +func GetIDOption(value *string) *option.String { + help := fmt.Sprintf( + `Find available project variables with "%s variables list"`, + build.Name, + ) + + idOption := option.NewStringOption(value) + + idOption.AddFlagWithExtraHelp("id", "Project Variable Id", help) + + return idOption +} diff --git a/cmd/project_variable/list.go b/cmd/project_variable/list.go new file mode 100644 index 0000000..723b4af --- /dev/null +++ b/cmd/project_variable/list.go @@ -0,0 +1,39 @@ +package project_variable + +import ( + "bunnyshell.com/cli/pkg/api/project_variable" + "bunnyshell.com/cli/pkg/config" + "bunnyshell.com/cli/pkg/lib" + "github.com/spf13/cobra" +) + +func init() { + options := config.GetOptions() + settings := config.GetSettings() + + listOptions := project_variable.NewListOptions() + + command := &cobra.Command{ + Use: "list", + + ValidArgsFunction: cobra.NoFileCompletions, + + RunE: func(cmd *cobra.Command, args []string) error { + listOptions.Organization = settings.Profile.Context.Organization + listOptions.Project = settings.Profile.Context.Project + + return lib.ShowCollection(cmd, listOptions, func() (lib.ModelWithPagination, error) { + return project_variable.List(listOptions) + }) + }, + } + + flags := command.Flags() + + flags.AddFlag(options.Organization.GetFlag("organization")) + flags.AddFlag(options.Project.GetFlag("project")) + + listOptions.UpdateFlagSet(flags) + + mainCmd.AddCommand(command) +} diff --git a/cmd/project_variable/root.go b/cmd/project_variable/root.go new file mode 100644 index 0000000..49980da --- /dev/null +++ b/cmd/project_variable/root.go @@ -0,0 +1,33 @@ +package project_variable + +import ( + "bunnyshell.com/cli/cmd/project_variable/action" + "bunnyshell.com/cli/pkg/config" + "bunnyshell.com/cli/pkg/util" + "github.com/spf13/cobra" +) + +var mainCmd = &cobra.Command{ + Use: "project-variables", + Aliases: []string{"pvar"}, + + Short: "Project Variables", + Long: "Bunnyshell Project Variables", +} + +func init() { + config.MainManager.CommandWithAPI(mainCmd) + + util.AddGroupedCommands( + mainCmd, + cobra.Group{ + ID: "actions", + Title: "Commands for Project Variables:", + }, + action.GetMainCommand().Commands(), + ) +} + +func GetMainCommand() *cobra.Command { + return mainCmd +} diff --git a/cmd/project_variable/show.go b/cmd/project_variable/show.go new file mode 100644 index 0000000..c5ea4ba --- /dev/null +++ b/cmd/project_variable/show.go @@ -0,0 +1,33 @@ +package project_variable + +import ( + "bunnyshell.com/cli/cmd/project_variable/action" + "bunnyshell.com/cli/pkg/api/project_variable" + "bunnyshell.com/cli/pkg/lib" + "github.com/spf13/cobra" +) + +func init() { + itemOptions := project_variable.NewItemOptions("") + + command := &cobra.Command{ + Use: "show", + + ValidArgsFunction: cobra.NoFileCompletions, + + RunE: func(cmd *cobra.Command, args []string) error { + model, err := project_variable.Get(itemOptions) + if err != nil { + return lib.FormatCommandError(cmd, err) + } + + return lib.FormatCommandData(cmd, model) + }, + } + + flags := command.Flags() + + flags.AddFlag(action.GetIDOption(&itemOptions.ID).GetRequiredFlag("id")) + + mainCmd.AddCommand(command) +} diff --git a/cmd/registry_integration/list.go b/cmd/registry_integration/list.go new file mode 100644 index 0000000..cb552ba --- /dev/null +++ b/cmd/registry_integration/list.go @@ -0,0 +1,38 @@ +package registry_integration + +import ( + "bunnyshell.com/cli/pkg/api/registry_integration" + "bunnyshell.com/cli/pkg/config" + "bunnyshell.com/cli/pkg/lib" + "github.com/spf13/cobra" +) + +func init() { + options := config.GetOptions() + settings := config.GetSettings() + + listOptions := registry_integration.NewListOptions() + + command := &cobra.Command{ + Use: "list", + GroupID: mainGroup.ID, + + ValidArgsFunction: cobra.NoFileCompletions, + + RunE: func(cmd *cobra.Command, args []string) error { + listOptions.Organization = settings.Profile.Context.Organization + + return lib.ShowCollection(cmd, listOptions, func() (lib.ModelWithPagination, error) { + return registry_integration.List(listOptions) + }) + }, + } + + flags := command.Flags() + + flags.AddFlag(options.Organization.GetFlag("organization")) + + listOptions.UpdateFlagSet(flags) + + mainCmd.AddCommand(command) +} diff --git a/cmd/registry_integration/root.go b/cmd/registry_integration/root.go new file mode 100644 index 0000000..4200735 --- /dev/null +++ b/cmd/registry_integration/root.go @@ -0,0 +1,29 @@ +package registry_integration + +import ( + "bunnyshell.com/cli/pkg/config" + "github.com/spf13/cobra" +) + +var mainCmd = &cobra.Command{ + Use: "container-registries", + Aliases: []string{"creg"}, + + Short: "Container Registry Integrations", + Long: "Bunnyshell Container Registry Integrations", +} + +var mainGroup = cobra.Group{ + ID: "container-registries", + Title: "Commands for Container Registries:", +} + +func init() { + config.MainManager.CommandWithAPI(mainCmd) + + mainCmd.AddGroup(&mainGroup) +} + +func GetMainCommand() *cobra.Command { + return mainCmd +} diff --git a/cmd/registry_integration/show.go b/cmd/registry_integration/show.go new file mode 100644 index 0000000..679d54b --- /dev/null +++ b/cmd/registry_integration/show.go @@ -0,0 +1,50 @@ +package registry_integration + +import ( + "fmt" + + "bunnyshell.com/cli/pkg/api/registry_integration" + "bunnyshell.com/cli/pkg/build" + "bunnyshell.com/cli/pkg/config/option" + "bunnyshell.com/cli/pkg/lib" + "github.com/spf13/cobra" +) + +func init() { + itemOptions := registry_integration.NewItemOptions("") + + command := &cobra.Command{ + Use: "show", + GroupID: mainGroup.ID, + + ValidArgsFunction: cobra.NoFileCompletions, + + RunE: func(cmd *cobra.Command, args []string) error { + model, err := registry_integration.Get(itemOptions) + if err != nil { + return lib.FormatCommandError(cmd, err) + } + + return lib.FormatCommandData(cmd, model) + }, + } + + flags := command.Flags() + + flags.AddFlag(getIDOption(&itemOptions.ID).GetRequiredFlag("id")) + + mainCmd.AddCommand(command) +} + +func getIDOption(value *string) *option.String { + help := fmt.Sprintf( + `Find available Container Registries Integrations with "%s container-registries list"`, + build.Name, + ) + + idOption := option.NewStringOption(value) + + idOption.AddFlagWithExtraHelp("id", "Container Registry Integration Id", help) + + return idOption +} diff --git a/cmd/root.go b/cmd/root.go index ffd4273..f028149 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -13,6 +13,8 @@ import ( "bunnyshell.com/cli/cmd/organization" "bunnyshell.com/cli/cmd/pipeline" "bunnyshell.com/cli/cmd/project" + "bunnyshell.com/cli/cmd/project_variable" + "bunnyshell.com/cli/cmd/registry_integration" "bunnyshell.com/cli/cmd/template" "bunnyshell.com/cli/cmd/utils" "bunnyshell.com/cli/cmd/variable" @@ -90,6 +92,8 @@ func init() { event.GetMainCommand(), organization.GetMainCommand(), project.GetMainCommand(), + project_variable.GetMainCommand(), + registry_integration.GetMainCommand(), variable.GetMainCommand(), k8sIntegration.GetMainCommand(), pipeline.GetMainCommand(), diff --git a/cmd/variable/action/create.go b/cmd/variable/action/create.go new file mode 100644 index 0000000..0a9adca --- /dev/null +++ b/cmd/variable/action/create.go @@ -0,0 +1,46 @@ +package action + +import ( + "bunnyshell.com/cli/pkg/api/variable" + "bunnyshell.com/cli/pkg/config" + "bunnyshell.com/cli/pkg/lib" + "bunnyshell.com/cli/pkg/util" + "github.com/spf13/cobra" +) + +func init() { + options := config.GetOptions() + settings := config.GetSettings() + + createOptions := variable.NewCreateOptions() + + command := &cobra.Command{ + Use: "create", + + ValidArgsFunction: cobra.NoFileCompletions, + + RunE: func(cmd *cobra.Command, args []string) error { + createOptions.Environment = settings.Profile.Context.Environment + + model, err := variable.Create(createOptions) + if err != nil { + return lib.FormatCommandError(cmd, err) + } + + return lib.FormatCommandData(cmd, model) + }, + } + + flags := command.Flags() + + flags.AddFlag(options.Environment.AddFlagWithExtraHelp( + "environment", + "Environment for the variable", + "Environments contain multiple variables", + util.FlagRequired, + )) + + createOptions.UpdateFlagSet(flags) + + mainCmd.AddCommand(command) +} diff --git a/cmd/variable/action/delete.go b/cmd/variable/action/delete.go new file mode 100644 index 0000000..0880374 --- /dev/null +++ b/cmd/variable/action/delete.go @@ -0,0 +1,34 @@ +package action + +import ( + "bunnyshell.com/cli/pkg/api/variable" + "bunnyshell.com/cli/pkg/lib" + "github.com/spf13/cobra" +) + +func init() { + deleteOptions := variable.NewDeleteOptions() + + command := &cobra.Command{ + Use: "delete", + + ValidArgsFunction: cobra.NoFileCompletions, + + RunE: func(cmd *cobra.Command, args []string) error { + err := variable.Delete(deleteOptions) + if err != nil { + return lib.FormatCommandError(cmd, err) + } + + cmd.Printf("\nEnvironment variable %s successfully deleted\n", deleteOptions.ID) + + return nil + }, + } + + flags := command.Flags() + + flags.AddFlag(GetIDOption(&deleteOptions.ID).GetRequiredFlag("id")) + + mainCmd.AddCommand(command) +} diff --git a/cmd/variable/action/edit.go b/cmd/variable/action/edit.go new file mode 100644 index 0000000..40aef9c --- /dev/null +++ b/cmd/variable/action/edit.go @@ -0,0 +1,38 @@ +package action + +import ( + "bunnyshell.com/cli/pkg/api/variable" + "bunnyshell.com/cli/pkg/lib" + "github.com/spf13/cobra" +) + +func init() { + editOptions := variable.NewEditOptions("") + + command := &cobra.Command{ + Use: "edit", + + ValidArgsFunction: cobra.NoFileCompletions, + + RunE: func(cmd *cobra.Command, args []string) error { + flags := cmd.Flags() + if flags.Changed("value") { + editOptions.EnvironmentVariableEditAction.SetValue(flags.Lookup("value").Value.String()) + } + + model, err := variable.Edit(editOptions) + if err != nil { + return lib.FormatCommandError(cmd, err) + } + + return lib.FormatCommandData(cmd, model) + }, + } + + flags := command.Flags() + + flags.AddFlag(GetIDOption(&editOptions.ID).GetRequiredFlag("id")) + editOptions.UpdateFlagSet(flags) + + mainCmd.AddCommand(command) +} diff --git a/cmd/variable/action/root.go b/cmd/variable/action/root.go new file mode 100644 index 0000000..69f7f6f --- /dev/null +++ b/cmd/variable/action/root.go @@ -0,0 +1,28 @@ +package action + +import ( + "fmt" + + "bunnyshell.com/cli/pkg/build" + "bunnyshell.com/cli/pkg/config/option" + "github.com/spf13/cobra" +) + +var mainCmd = &cobra.Command{} + +func GetMainCommand() *cobra.Command { + return mainCmd +} + +func GetIDOption(value *string) *option.String { + help := fmt.Sprintf( + `Find available environment variables with "%s variables list"`, + build.Name, + ) + + idOption := option.NewStringOption(value) + + idOption.AddFlagWithExtraHelp("id", "Environment Variable Id", help) + + return idOption +} diff --git a/cmd/variable/edit.go b/cmd/variable/edit.go deleted file mode 100644 index 0183d6a..0000000 --- a/cmd/variable/edit.go +++ /dev/null @@ -1,68 +0,0 @@ -package variable - -import ( - "bunnyshell.com/cli/pkg/config/option" - "bunnyshell.com/cli/pkg/lib" - "bunnyshell.com/sdk" - "github.com/spf13/cobra" -) - -func init() { - var ( - variableID string - value string - ) - - actionGroup := &cobra.Group{ - ID: "actions", - Title: "Commands for Environment Variable Actions:", - } - - command := &cobra.Command{ - Use: "edit", - GroupID: actionGroup.ID, - - ValidArgsFunction: cobra.NoFileCompletions, - - RunE: func(cmd *cobra.Command, args []string) error { - ctx, cancel := lib.GetContext() - defer cancel() - - request := lib.GetAPI().EnvironmentVariableAPI.EnvironmentVariableEdit( - ctx, - variableID, - ).EnvironmentVariableEdit( - *toVariableEdit(value), - ) - - model, resp, err := request.Execute() - - return lib.FormatRequestResult(cmd, model, resp, err) - }, - } - - flags := command.Flags() - - flags.AddFlag(getIDOption(&variableID).GetRequiredFlag("id")) - flags.AddFlag(getEditValueOption(&value).GetRequiredFlag("value")) - - mainCmd.AddGroup(actionGroup) - mainCmd.AddCommand(command) -} - -func getEditValueOption(value *string) *option.String { - help := "Update the value of an environment variable. A deployment will be required for the updates to take effect." - - idOption := option.NewStringOption(value) - - idOption.AddFlagWithExtraHelp("value", "Environment Variable Value", help) - - return idOption -} - -func toVariableEdit(value string) *sdk.EnvironmentVariableEdit { - edit := sdk.NewEnvironmentVariableEdit() - edit.SetValue(value) - - return edit -} diff --git a/cmd/variable/list.go b/cmd/variable/list.go index ae74f17..58888dd 100644 --- a/cmd/variable/list.go +++ b/cmd/variable/list.go @@ -14,8 +14,7 @@ func init() { listOptions := variable.NewListOptions() command := &cobra.Command{ - Use: "list", - GroupID: mainGroup.ID, + Use: "list", ValidArgsFunction: cobra.NoFileCompletions, diff --git a/cmd/variable/root.go b/cmd/variable/root.go index 60dd9f4..8e3626b 100644 --- a/cmd/variable/root.go +++ b/cmd/variable/root.go @@ -1,19 +1,12 @@ package variable import ( - "fmt" - - "bunnyshell.com/cli/pkg/build" + "bunnyshell.com/cli/cmd/variable/action" "bunnyshell.com/cli/pkg/config" - "bunnyshell.com/cli/pkg/config/option" + "bunnyshell.com/cli/pkg/util" "github.com/spf13/cobra" ) -var mainGroup = cobra.Group{ - ID: "variables", - Title: "Commands for Environment Variables:", -} - var mainCmd = &cobra.Command{ Use: "variables", Aliases: []string{"var"}, @@ -25,22 +18,16 @@ var mainCmd = &cobra.Command{ func init() { config.MainManager.CommandWithAPI(mainCmd) - mainCmd.AddGroup(&mainGroup) + util.AddGroupedCommands( + mainCmd, + cobra.Group{ + ID: "actions", + Title: "Commands for Environment Variables:", + }, + action.GetMainCommand().Commands(), + ) } func GetMainCommand() *cobra.Command { return mainCmd } - -func getIDOption(value *string) *option.String { - help := fmt.Sprintf( - `Find available variables with "%s variables list"`, - build.Name, - ) - - idOption := option.NewStringOption(value) - - idOption.AddFlagWithExtraHelp("id", "Environment Variable Id", help) - - return idOption -} diff --git a/cmd/variable/show.go b/cmd/variable/show.go index 6abbb89..a47b981 100644 --- a/cmd/variable/show.go +++ b/cmd/variable/show.go @@ -1,6 +1,7 @@ package variable import ( + "bunnyshell.com/cli/cmd/variable/action" "bunnyshell.com/cli/pkg/api/variable" "bunnyshell.com/cli/pkg/lib" "github.com/spf13/cobra" @@ -10,8 +11,7 @@ func init() { itemOptions := variable.NewItemOptions("") command := &cobra.Command{ - Use: "show", - GroupID: mainGroup.ID, + Use: "show", ValidArgsFunction: cobra.NoFileCompletions, @@ -27,7 +27,7 @@ func init() { flags := command.Flags() - flags.AddFlag(getIDOption(&itemOptions.ID).GetRequiredFlag("id")) + flags.AddFlag(action.GetIDOption(&itemOptions.ID).GetRequiredFlag("id")) mainCmd.AddCommand(command) } diff --git a/go.mod b/go.mod index e82d5e3..bbf53b8 100644 --- a/go.mod +++ b/go.mod @@ -6,15 +6,15 @@ replace github.com/imdario/mergo => github.com/imdario/mergo v0.3.16 require ( bunnyshell.com/dev v0.5.5 - bunnyshell.com/sdk v0.15.1 + bunnyshell.com/sdk v0.15.2 github.com/AlecAivazis/survey/v2 v2.3.7 + github.com/avast/retry-go/v4 v4.5.1 github.com/briandowns/spinner v1.23.0 github.com/fatih/color v1.15.0 github.com/spf13/cobra v1.7.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.17.0 github.com/thediveo/enumflag/v2 v2.0.4 - golang.org/x/exp v0.0.0-20231006140011-7918f672742d gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.28.2 k8s.io/apimachinery v0.28.2 @@ -84,6 +84,7 @@ require ( go.starlark.net v0.0.0-20231013162135-47c85baa7a64 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.14.0 // indirect + golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect golang.org/x/net v0.17.0 // indirect golang.org/x/oauth2 v0.13.0 // indirect golang.org/x/sync v0.4.0 // indirect diff --git a/go.sum b/go.sum index a1be1b8..f7c2b28 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ bunnyshell.com/dev v0.5.5 h1:dqFr6ZTPWvfqtgQGDPzHDfG0hNvNMZDqXAKS2+ekFzY= bunnyshell.com/dev v0.5.5/go.mod h1:KlqPdOh60vqAfnuGUw9AKc0RVhXpNzWg46QKJSP/jog= bunnyshell.com/sdk v0.15.1 h1:X2AUE2r7Eww4SgxY2+ZlDbWKnJrdSBiB5HzBenvSwv0= bunnyshell.com/sdk v0.15.1/go.mod h1:RfgfUzZ4WHZGCkToUfu2/hoQS6XsQc8IdPTVAlpS138= +bunnyshell.com/sdk v0.15.2 h1:hgMAHtlsXfxlsUFs8obzVfvUphf13CyPtDxbrfNDeag= +bunnyshell.com/sdk v0.15.2/go.mod h1:RfgfUzZ4WHZGCkToUfu2/hoQS6XsQc8IdPTVAlpS138= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -52,6 +54,8 @@ github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63n github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/avast/retry-go/v4 v4.5.1 h1:AxIx0HGi4VZ3I02jr78j5lZ3M6x1E0Ivxa6b0pUUh7o= +github.com/avast/retry-go/v4 v4.5.1/go.mod h1:/sipNsvNB3RRuT5iNcb6h73nw3IBmXJ/H3XrCQYSOpc= github.com/briandowns/spinner v1.23.0 h1:alDF2guRWqa/FOZZYWjlMIx2L6H0wyewPxo/CH4Pt2A= github.com/briandowns/spinner v1.23.0/go.mod h1:rPG4gmXeN3wQV/TsAY4w8lPdIM6RX3yqeBQJSrbXjuE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= diff --git a/pkg/api/build_settings/edit_options.go b/pkg/api/build_settings/edit_options.go new file mode 100644 index 0000000..7d75f28 --- /dev/null +++ b/pkg/api/build_settings/edit_options.go @@ -0,0 +1,111 @@ +package build_settings + +import ( + "bunnyshell.com/cli/pkg/api/common" + "bunnyshell.com/cli/pkg/config/enum" + "github.com/spf13/pflag" +) + +const ( + StatusSuccess string = "success" + StatusValidating string = "validating" + StatusError string = "error" +) + +type ActionWithBuildSettings interface { + SetUseManagedRegistry(bool) + + SetRegistryIntegration(string) + + SetUseManagedCluster(bool) + + SetKubernetesIntegration(string) + + SetMemory(int32) + + SetCpu(string) +} + +type EditOptions struct { + common.ItemOptions + + EditData + + // Seconds to wait for the build settings to be validated + ValidationTimeout int32 +} + +type EditData struct { + UseManagedRegistry enum.Bool + RegistryIntegration string + + UseManagedCluster enum.Bool + BuildK8sIntegration string + Memory int32 + Cpu string +} + +func NewEditOptions(entityId string) *EditOptions { + return &EditOptions{ + ItemOptions: *common.NewItemOptions(entityId), + + EditData: EditData{}, + + ValidationTimeout: 180, + } +} + +func (eso *EditOptions) UpdateFlagSet(flags *pflag.FlagSet) { + data := &eso.EditData + + useManagedRegistryFlag := enum.BoolFlag( + &data.UseManagedRegistry, + "use-managed-registry", + "Use the managed Container Registry for the built images", + ) + flags.AddFlag(useManagedRegistryFlag) + useManagedRegistryFlag.NoOptDefVal = "true" + + flags.StringVar(&data.RegistryIntegration, "registry", data.RegistryIntegration, "Set the Container Registry integration to push the built images") + + useManagedClusterFlag := enum.BoolFlag( + &data.UseManagedCluster, + "use-managed-k8s", + "Use the managed Kubernetes integration cluster for the image builds", + ) + flags.AddFlag(useManagedClusterFlag) + useManagedClusterFlag.NoOptDefVal = "true" + + flags.StringVar(&data.BuildK8sIntegration, "k8s", data.BuildK8sIntegration, "Set the Kubernetes integration cluster to be used for the image builds") + + flags.Int32Var(&data.Memory, "memory", data.Memory, "Set the maximum memory to be used by a build job (in Mi)") + flags.StringVar(&data.Cpu, "cpu", data.Cpu, "Set the maximum amount of CPU to be used by a build job (ie. 0.2)") + + flags.Int32Var(&eso.ValidationTimeout, "validation-timeout", eso.ValidationTimeout, "Seconds to wait for the build settings to be validated") +} + +func ApplyEditOptionsToAction(action ActionWithBuildSettings, options *EditData) { + if options.UseManagedRegistry != enum.BoolNone { + action.SetUseManagedRegistry(options.UseManagedRegistry == enum.BoolTrue) + } + + if options.RegistryIntegration != "" { + action.SetRegistryIntegration(options.RegistryIntegration) + } + + if options.UseManagedCluster != enum.BoolNone { + action.SetUseManagedCluster(options.UseManagedCluster == enum.BoolTrue) + } + + if options.BuildK8sIntegration != "" { + action.SetKubernetesIntegration(options.BuildK8sIntegration) + } + + if options.Cpu != "" { + action.SetCpu(options.Cpu) + } + + if options.Memory > 0 { + action.SetMemory(options.Memory) + } +} diff --git a/pkg/api/build_settings/validation.go b/pkg/api/build_settings/validation.go new file mode 100644 index 0000000..f5d70ff --- /dev/null +++ b/pkg/api/build_settings/validation.go @@ -0,0 +1,70 @@ +package build_settings + +import ( + "context" + "errors" + "fmt" + "time" + + "bunnyshell.com/cli/pkg/api/common" + "bunnyshell.com/cli/pkg/net" + "bunnyshell.com/cli/pkg/util" + "bunnyshell.com/sdk" + "github.com/avast/retry-go/v4" +) + +type ModelWithBuildSettings[T any] interface { + HasBuildSettings() bool + GetBuildSettings() sdk.BuildSettingsItem + *T +} + +type ModelFetcher[T any, PT ModelWithBuildSettings[T]] func(*common.ItemOptions) (PT, error) + +func CheckBuildSettingsValidation[T any, PT ModelWithBuildSettings[T]](fetcher ModelFetcher[T, PT], options *EditOptions, showSpinner bool) (PT, error) { + if showSpinner { + resume := net.PauseSpinner() + defer resume() + + spinner := util.MakeSpinner("Validating the build settings...") + + spinner.Start() + defer spinner.Stop() + } + + itemOptions := common.NewItemOptions(options.ID) + + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(options.ValidationTimeout)*time.Second) + defer cancel() + + model, err := retry.DoWithData( + func() (PT, error) { + model, err := fetcher(itemOptions) + if err != nil { + return nil, err + } + + buildSettings := model.GetBuildSettings() + + // Return the model if the build settings are not set at all or they are already validated + if !model.HasBuildSettings() || buildSettings.GetLastStatus() == StatusSuccess { + return model, nil + } + + if buildSettings.GetLastStatus() == StatusError { + return nil, retry.Unrecoverable(fmt.Errorf("the new build settings validation failed: %s", buildSettings.GetLastError())) + } + + return nil, errors.New("the new build settings validation is in progress: %s") + }, + retry.Context(ctx), + retry.Attempts(0), + retry.Delay(5*time.Second), + ) + + if err == context.DeadlineExceeded { + return nil, fmt.Errorf("the new build settings validation timed out after %d seconds", options.ValidationTimeout) + } + + return model, err +} diff --git a/pkg/api/component/action_edit.go b/pkg/api/component/action_edit.go new file mode 100644 index 0000000..89a55d8 --- /dev/null +++ b/pkg/api/component/action_edit.go @@ -0,0 +1,42 @@ +package component + +import ( + "bunnyshell.com/cli/pkg/api/common" + "bunnyshell.com/cli/pkg/api/environment" + "github.com/spf13/pflag" +) + +type EditComponentOptions struct { + common.ItemOptions + + environment.DeployOptions + + EditComponentData + + WithDeploy bool +} + +type EditComponentData struct { + K8SIntegration string + + TargetRepository string + TargetBranch string +} + +func NewEditComponentOptions(component string) *EditComponentOptions { + return &EditComponentOptions{ + ItemOptions: *common.NewItemOptions(component), + + DeployOptions: *environment.NewDeployOptions(""), + } +} + +func (eo *EditComponentOptions) UpdateFlagSet(flags *pflag.FlagSet) { + data := eo.EditComponentData + + flags.BoolVar(&eo.WithDeploy, "deploy", eo.WithDeploy, "Deploy the environment after update") + + flags.StringVar(&data.K8SIntegration, "k8s", data.K8SIntegration, "Set Kubernetes integration for the environment (if not set)") + + eo.DeployOptions.UpdateFlagSet(flags) +} diff --git a/pkg/api/environment/action_create.go b/pkg/api/environment/action_create.go index 2ec6ff0..db113f6 100644 --- a/pkg/api/environment/action_create.go +++ b/pkg/api/environment/action_create.go @@ -16,14 +16,16 @@ type CreateOptions struct { sdk.EnvironmentCreateAction WithDeploy bool - - Labels map[string]string } func NewCreateOptions() *CreateOptions { - environmentCreateAction := sdk.NewEnvironmentCreateAction("", "") + environmentCreateAction := sdk.NewEnvironmentCreateActionWithDefaults() environmentCreateAction.SetKubernetesIntegration("") environmentCreateAction.SetEphemeralKubernetesIntegration("") + environmentCreateAction.SetLabels(map[string]string{}) + environmentCreateAction.SetAutoDeployEphemeral(false) + environmentCreateAction.SetCreateEphemeralOnPrCreate(false) + environmentCreateAction.SetDestroyEphemeralOnPrClose(false) return &CreateOptions{ DeployOptions: *NewDeployOptions(""), @@ -41,7 +43,13 @@ func (co *CreateOptions) UpdateFlagSet(flags *pflag.FlagSet) { util.MarkFlagRequiredWithHelp(flags.Lookup("name"), "A unique name within the project for the new environment") - flags.StringToStringVar(&co.Labels, "label", co.Labels, "Set labels for the new environment (key=value)") + flags.StringToStringVar(co.Labels, "label", *co.Labels, "Set labels for the new environment (key=value)") + + ephemeralsK8sIntegration := co.EphemeralKubernetesIntegration.Get() + flags.BoolVar(co.CreateEphemeralOnPrCreate, "create-ephemeral-on-pr", *co.CreateEphemeralOnPrCreate, "Create ephemeral environments when pull requests are created") + flags.BoolVar(co.DestroyEphemeralOnPrClose, "destroy-ephemeral-on-pr-close", *co.DestroyEphemeralOnPrClose, "Destroys the created ephemerals when the pull request is closed (or merged)") + flags.BoolVar(co.AutoDeployEphemeral, "auto-deploy-ephemerals", *co.AutoDeployEphemeral, "Auto deploy the created ephemerals") + flags.StringVar(ephemeralsK8sIntegration, "ephemerals-k8s", *ephemeralsK8sIntegration, "The Kubernetes integration to be used for the ephemeral environments triggered by this environment") co.DeployOptions.UpdateFlagSet(flags) } @@ -63,8 +71,8 @@ func CreateRaw(options *CreateOptions) (*sdk.EnvironmentItem, *http.Response, er request := lib.GetAPIFromProfile(profile).EnvironmentAPI.EnvironmentCreate(ctx) - if len(options.Labels) > 0 { - options.EnvironmentCreateAction.SetLabels(options.Labels) + if len(*options.Labels) > 0 { + options.EnvironmentCreateAction.SetLabels(*options.Labels) } request = request.EnvironmentCreateAction(options.EnvironmentCreateAction) diff --git a/pkg/api/environment/action_edit_build_settings.go b/pkg/api/environment/action_edit_build_settings.go new file mode 100644 index 0000000..f64636a --- /dev/null +++ b/pkg/api/environment/action_edit_build_settings.go @@ -0,0 +1,48 @@ +package environment + +import ( + "net/http" + + "bunnyshell.com/cli/pkg/api" + "bunnyshell.com/cli/pkg/api/build_settings" + "bunnyshell.com/cli/pkg/lib" + "bunnyshell.com/sdk" +) + +type EditBuildSettingsOptions struct { + build_settings.EditOptions + + sdk.EnvironmentEditBuildSettingsAction +} + +func NewEditBuildSettingsOptions(project string) *EditBuildSettingsOptions { + return &EditBuildSettingsOptions{ + EditOptions: *build_settings.NewEditOptions(project), + + EnvironmentEditBuildSettingsAction: *sdk.NewEnvironmentEditBuildSettingsActionWithDefaults(), + } +} + +func EditBuildSettings(options *EditBuildSettingsOptions) (*sdk.EnvironmentItem, error) { + model, resp, err := EditBuildSettingsRaw(options) + if err != nil { + return nil, api.ParseError(resp, err) + } + + return model, nil +} + +func EditBuildSettingsRaw(options *EditBuildSettingsOptions) (*sdk.EnvironmentItem, *http.Response, error) { + profile := options.GetProfile() + + ctx, cancel := lib.GetContextFromProfile(profile) + defer cancel() + + build_settings.ApplyEditOptionsToAction(&options.EnvironmentEditBuildSettingsAction, &options.EditData) + + request := lib.GetAPIFromProfile(profile).EnvironmentAPI. + EnvironmentEditBuildSettings(ctx, options.ID). + EnvironmentEditBuildSettingsAction(options.EnvironmentEditBuildSettingsAction) + + return request.Execute() +} diff --git a/pkg/api/environment/action_edit_settings.go b/pkg/api/environment/action_edit_settings.go index 2d0c2de..f7aac65 100644 --- a/pkg/api/environment/action_edit_settings.go +++ b/pkg/api/environment/action_edit_settings.go @@ -1,6 +1,8 @@ package environment import ( + "errors" + "fmt" "net/http" "bunnyshell.com/cli/pkg/api" @@ -11,10 +13,15 @@ import ( "github.com/spf13/pflag" ) +var ( + errUnknownEnvironmentType = errors.New("unknown environment type") + errUndefinedEnvironmentType = errors.New("undefined environment type") +) + type EditSettingsOptions struct { common.ItemOptions - sdk.EnvironmentEditSettings + *sdk.EnvironmentEditSettings EditSettingsData } @@ -29,10 +36,15 @@ type EditSettingsData struct { LabelReplace bool K8SIntegration string + + EphemeralK8SIntegration string + CreateEphemeralOnPrCreate enum.Bool + DestroyEphemeralOnPrClose enum.Bool + AutoDeployEphemeral enum.Bool } func NewEditSettingsOptions(environment string) *EditSettingsOptions { - return &EditSettingsOptions{ + options := &EditSettingsOptions{ ItemOptions: *common.NewItemOptions(environment), EditSettingsData: EditSettingsData{ @@ -40,8 +52,25 @@ func NewEditSettingsOptions(environment string) *EditSettingsOptions { AutoUpdate: enum.BoolNone, }, - EnvironmentEditSettings: *sdk.NewEnvironmentEditSettings(), + EnvironmentEditSettings: sdk.NewEnvironmentEditSettingsWithDefaults(), + } + + return options +} + +func (eso *EditSettingsOptions) UpdateEditSettingsForType(environmentType string) error { + switch environmentType { + case "primary": + primaryEdit := sdk.PrimaryAsEnvironmentEditSettingsEdit(sdk.NewPrimaryWithDefaults()) + eso.Edit = &primaryEdit + case "ephemeral": + ephemeralEdit := sdk.EphemeralAsEnvironmentEditSettingsEdit(sdk.NewEphemeralWithDefaults()) + eso.Edit = &ephemeralEdit + default: + return fmt.Errorf("%w: %s", errUnknownEnvironmentType, environmentType) } + + return nil } func (eso *EditSettingsOptions) UpdateFlagSet(flags *pflag.FlagSet) { @@ -69,9 +98,39 @@ func (eso *EditSettingsOptions) UpdateFlagSet(flags *pflag.FlagSet) { flags.BoolVar(&data.LabelReplace, "label-replace", data.LabelReplace, "Set label strategy to replace (default: merge)") flags.StringVar(&data.K8SIntegration, "k8s", data.K8SIntegration, "Set Kubernetes integration for the environment (if not set)") + + ephCreateFlag := enum.BoolFlag( + &data.CreateEphemeralOnPrCreate, + "create-ephemeral-on-pr", + "Create ephemeral environments when pull requests are created (for 'primary' environments)", + ) + flags.AddFlag(ephCreateFlag) + ephCreateFlag.NoOptDefVal = "true" + + ephDestroyFlag := enum.BoolFlag( + &data.DestroyEphemeralOnPrClose, + "destroy-ephemeral-on-pr-close", + "Destroys the created ephemerals when the pull request is closed (or merged) (for 'primary' environments)", + ) + flags.AddFlag(ephDestroyFlag) + ephDestroyFlag.NoOptDefVal = "true" + + ephAutoDeployFlag := enum.BoolFlag( + &data.AutoDeployEphemeral, + "auto-deploy-ephemerals", + "Auto deploy the created ephemerals (for 'primary' environments)", + ) + flags.AddFlag(ephAutoDeployFlag) + ephAutoDeployFlag.NoOptDefVal = "true" + + flags.StringVar(&data.EphemeralK8SIntegration, "ephemerals-k8s", data.EphemeralK8SIntegration, "The Kubernetes integration to be used for the ephemeral environments triggered by this environment (for 'primary' environments)") } func EditSettings(options *EditSettingsOptions) (*sdk.EnvironmentItem, error) { + if options.Edit == nil { + return nil, fmt.Errorf("%w", errUndefinedEnvironmentType) + } + model, resp, err := EditSettingsRaw(options) if err != nil { return nil, api.ParseError(resp, err) @@ -127,7 +186,30 @@ func applyEditSettingsOptions( options.EnvironmentEditSettings.SetLabels(labelsEdit) } - request = request.EnvironmentEditSettings(options.EnvironmentEditSettings) + isPrimaryType := options.EnvironmentEditSettings.Edit.Primary != nil + if isPrimaryType { + applyPrimaryEditSettingsOptions(options) + } + + request = request.EnvironmentEditSettings(*options.EnvironmentEditSettings) return request } + +func applyPrimaryEditSettingsOptions(options *EditSettingsOptions) { + if options.EphemeralK8SIntegration != "" { + options.EnvironmentEditSettings.Edit.Primary.SetEphemeralKubernetesIntegration(options.EphemeralK8SIntegration) + } + + if options.CreateEphemeralOnPrCreate != enum.BoolNone { + options.EnvironmentEditSettings.Edit.Primary.SetCreateEphemeralOnPrCreate(options.CreateEphemeralOnPrCreate == enum.BoolTrue) + } + + if options.AutoDeployEphemeral != enum.BoolNone { + options.EnvironmentEditSettings.Edit.Primary.SetAutoDeployEphemeral(options.AutoDeployEphemeral == enum.BoolTrue) + } + + if options.DestroyEphemeralOnPrClose != enum.BoolNone { + options.EnvironmentEditSettings.Edit.Primary.SetDestroyEphemeralOnPrClose(options.DestroyEphemeralOnPrClose == enum.BoolTrue) + } +} diff --git a/pkg/api/project/action_create.go b/pkg/api/project/action_create.go new file mode 100644 index 0000000..66b71f2 --- /dev/null +++ b/pkg/api/project/action_create.go @@ -0,0 +1,57 @@ +package project + +import ( + "net/http" + + "bunnyshell.com/cli/pkg/api" + "bunnyshell.com/cli/pkg/api/common" + "bunnyshell.com/cli/pkg/lib" + "bunnyshell.com/cli/pkg/util" + "bunnyshell.com/sdk" + "github.com/spf13/pflag" +) + +type CreateOptions struct { + common.Options + + sdk.ProjectCreateAction +} + +func NewCreateOptions() *CreateOptions { + projectCreateOptions := sdk.NewProjectCreateAction("", "") + + projectCreateOptions.Labels = &map[string]string{} + + return &CreateOptions{ + ProjectCreateAction: *projectCreateOptions, + } +} + +func (co *CreateOptions) UpdateFlagSet(flags *pflag.FlagSet) { + flags.StringVar(&co.Name, "name", co.Name, "Unique name for the project") + util.MarkFlagRequiredWithHelp(flags.Lookup("name"), "A unique name within the organization for the new project") + + flags.StringToStringVar(co.Labels, "label", *co.Labels, "Set labels for the new project (key=value)") +} + +func Create(options *CreateOptions) (*sdk.ProjectItem, error) { + model, resp, err := CreateRaw(options) + if err != nil { + return nil, api.ParseError(resp, err) + } + + return model, nil +} + +func CreateRaw(options *CreateOptions) (*sdk.ProjectItem, *http.Response, error) { + profile := options.GetProfile() + + ctx, cancel := lib.GetContextFromProfile(profile) + defer cancel() + + request := lib.GetAPIFromProfile(profile). + ProjectAPI.ProjectCreate(ctx). + ProjectCreateAction(options.ProjectCreateAction) + + return request.Execute() +} diff --git a/pkg/api/project/action_delete.go b/pkg/api/project/action_delete.go new file mode 100644 index 0000000..d623ab2 --- /dev/null +++ b/pkg/api/project/action_delete.go @@ -0,0 +1,38 @@ +package project + +import ( + "net/http" + + "bunnyshell.com/cli/pkg/api" + "bunnyshell.com/cli/pkg/api/common" + "bunnyshell.com/cli/pkg/lib" +) + +type DeleteOptions struct { + common.ItemOptions +} + +func NewDeleteOptions() *DeleteOptions { + return &DeleteOptions{} +} + +func Delete(options *DeleteOptions) error { + resp, err := DeleteRaw(options) + if err != nil { + return api.ParseError(resp, err) + } + + return nil +} + +func DeleteRaw(options *DeleteOptions) (*http.Response, error) { + profile := options.GetProfile() + + ctx, cancel := lib.GetContextFromProfile(profile) + defer cancel() + + request := lib.GetAPIFromProfile(profile). + ProjectAPI.ProjectDelete(ctx, options.ID) + + return request.Execute() +} diff --git a/pkg/api/project/action_edit_build_settings.go b/pkg/api/project/action_edit_build_settings.go new file mode 100644 index 0000000..3a7ced1 --- /dev/null +++ b/pkg/api/project/action_edit_build_settings.go @@ -0,0 +1,48 @@ +package project + +import ( + "net/http" + + "bunnyshell.com/cli/pkg/api" + "bunnyshell.com/cli/pkg/api/build_settings" + "bunnyshell.com/cli/pkg/lib" + "bunnyshell.com/sdk" +) + +type EditBuildSettingsOptions struct { + build_settings.EditOptions + + sdk.ProjectEditBuildSettingsAction +} + +func NewEditBuildSettingsOptions(project string) *EditBuildSettingsOptions { + return &EditBuildSettingsOptions{ + EditOptions: *build_settings.NewEditOptions(project), + + ProjectEditBuildSettingsAction: *sdk.NewProjectEditBuildSettingsActionWithDefaults(), + } +} + +func EditBuildSettings(options *EditBuildSettingsOptions) (*sdk.ProjectItem, error) { + model, resp, err := EditBuildSettingsRaw(options) + if err != nil { + return nil, api.ParseError(resp, err) + } + + return model, nil +} + +func EditBuildSettingsRaw(options *EditBuildSettingsOptions) (*sdk.ProjectItem, *http.Response, error) { + profile := options.GetProfile() + + ctx, cancel := lib.GetContextFromProfile(profile) + defer cancel() + + build_settings.ApplyEditOptionsToAction(&options.ProjectEditBuildSettingsAction, &options.EditData) + + request := lib.GetAPIFromProfile(profile).ProjectAPI. + ProjectEditBuildSettings(ctx, options.ID). + ProjectEditBuildSettingsAction(options.ProjectEditBuildSettingsAction) + + return request.Execute() +} diff --git a/pkg/api/project/action_edit_settings.go b/pkg/api/project/action_edit_settings.go new file mode 100644 index 0000000..76742dd --- /dev/null +++ b/pkg/api/project/action_edit_settings.go @@ -0,0 +1,94 @@ +package project + +import ( + "net/http" + + "bunnyshell.com/cli/pkg/api" + "bunnyshell.com/cli/pkg/api/common" + "bunnyshell.com/cli/pkg/lib" + "bunnyshell.com/sdk" + "github.com/spf13/pflag" +) + +type EditSettingsOptions struct { + common.ItemOptions + + sdk.ProjectEditSettingsAction + + EditSettingsData +} + +type EditSettingsData struct { + Name string + + Labels map[string]string + LabelReplace bool +} + +func NewEditSettingsOptions(project string) *EditSettingsOptions { + return &EditSettingsOptions{ + ItemOptions: *common.NewItemOptions(project), + + EditSettingsData: EditSettingsData{}, + + ProjectEditSettingsAction: *sdk.NewProjectEditSettingsActionWithDefaults(), + } +} + +func (eso *EditSettingsOptions) UpdateFlagSet(flags *pflag.FlagSet) { + data := &eso.EditSettingsData + + flags.StringVar(&data.Name, "name", data.Name, "Update project name") + + flags.StringToStringVar(&data.Labels, "label", data.Labels, "Set labels for the project (key=value)") + flags.BoolVar(&data.LabelReplace, "label-replace", data.LabelReplace, "Set label strategy to replace (default: merge)") +} + +func EditSettings(options *EditSettingsOptions) (*sdk.ProjectItem, error) { + model, resp, err := EditSettingsRaw(options) + if err != nil { + return nil, api.ParseError(resp, err) + } + + return model, nil +} + +func EditSettingsRaw(options *EditSettingsOptions) (*sdk.ProjectItem, *http.Response, error) { + profile := options.GetProfile() + + ctx, cancel := lib.GetContextFromProfile(profile) + defer cancel() + + request := lib.GetAPIFromProfile(profile).ProjectAPI.ProjectEditSettings(ctx, options.ID) + + return applyEditSettingsOptions(request, options).Execute() +} + +func applyEditSettingsOptions( + request sdk.ApiProjectEditSettingsRequest, + options *EditSettingsOptions, +) sdk.ApiProjectEditSettingsRequest { + if options.EditSettingsData.Name != "" { + options.ProjectEditSettingsAction.SetName(options.EditSettingsData.Name) + } + + if options.EditSettingsData.Labels != nil { + labelsEdit := *sdk.NewEdit() + if options.EditSettingsData.LabelReplace { + labelsEdit.SetStrategy("replace") + } + + labelsEdit.SetValues(options.EditSettingsData.Labels) + + options.ProjectEditSettingsAction.SetLabels(labelsEdit) + } else if options.EditSettingsData.LabelReplace { + labelsEdit := *sdk.NewEdit() + labelsEdit.SetStrategy("replace") + + options.ProjectEditSettingsAction.SetLabels(labelsEdit) + } + + request = request.ProjectEditSettingsAction(options.ProjectEditSettingsAction) + + return request +} diff --git a/pkg/api/project_variable/action_create.go b/pkg/api/project_variable/action_create.go new file mode 100644 index 0000000..b0573b3 --- /dev/null +++ b/pkg/api/project_variable/action_create.go @@ -0,0 +1,77 @@ +package project_variable + +import ( + "net/http" + + "bunnyshell.com/cli/pkg/api" + "bunnyshell.com/cli/pkg/api/common" + "bunnyshell.com/cli/pkg/config/enum" + "bunnyshell.com/cli/pkg/lib" + "bunnyshell.com/cli/pkg/util" + "bunnyshell.com/sdk" + "github.com/spf13/pflag" +) + +type CreateOptions struct { + common.Options + + sdk.ProjectVariableCreateAction + + Value string + IsSecret enum.Bool +} + +func NewCreateOptions() *CreateOptions { + projectVariableCreateOptions := sdk.NewProjectVariableCreateAction("", "", "") + + return &CreateOptions{ + ProjectVariableCreateAction: *projectVariableCreateOptions, + + IsSecret: enum.BoolNone, + } +} + +func (co *CreateOptions) UpdateFlagSet(flags *pflag.FlagSet) { + flags.StringVar(&co.Name, "name", co.Name, "Unique name for the project variable") + util.MarkFlagRequiredWithHelp(flags.Lookup("name"), "A unique name within the project for the new project variable") + + flags.StringVar(&co.Value, "value", co.Value, "The value of the project variable") + util.MarkFlagRequiredWithHelp(flags.Lookup("value"), "A value for this project variable") + util.MarkFlag(flags.Lookup("value"), util.FlagAllowBlank) + + isSecretFlag := enum.BoolFlag( + &co.IsSecret, + "secret", + "Whether the project variable is secret or not", + ) + flags.AddFlag(isSecretFlag) + isSecretFlag.NoOptDefVal = "true" +} + +func Create(options *CreateOptions) (*sdk.ProjectVariableItem, error) { + options.ProjectVariableCreateAction.SetValue(options.Value) + + if options.IsSecret == enum.BoolTrue { + options.ProjectVariableCreateAction.SetIsSecret(true) + } + + model, resp, err := CreateRaw(options) + if err != nil { + return nil, api.ParseError(resp, err) + } + + return model, nil +} + +func CreateRaw(options *CreateOptions) (*sdk.ProjectVariableItem, *http.Response, error) { + profile := options.GetProfile() + + ctx, cancel := lib.GetContextFromProfile(profile) + defer cancel() + + request := lib.GetAPIFromProfile(profile). + ProjectVariableAPI.ProjectVariableCreate(ctx). + ProjectVariableCreateAction(options.ProjectVariableCreateAction) + + return request.Execute() +} diff --git a/pkg/api/project_variable/action_delete.go b/pkg/api/project_variable/action_delete.go new file mode 100644 index 0000000..e48b53e --- /dev/null +++ b/pkg/api/project_variable/action_delete.go @@ -0,0 +1,38 @@ +package project_variable + +import ( + "net/http" + + "bunnyshell.com/cli/pkg/api" + "bunnyshell.com/cli/pkg/api/common" + "bunnyshell.com/cli/pkg/lib" +) + +type DeleteOptions struct { + common.ItemOptions +} + +func NewDeleteOptions() *DeleteOptions { + return &DeleteOptions{} +} + +func Delete(options *DeleteOptions) error { + resp, err := DeleteRaw(options) + if err != nil { + return api.ParseError(resp, err) + } + + return nil +} + +func DeleteRaw(options *DeleteOptions) (*http.Response, error) { + profile := options.GetProfile() + + ctx, cancel := lib.GetContextFromProfile(profile) + defer cancel() + + request := lib.GetAPIFromProfile(profile). + ProjectVariableAPI.ProjectVariableDelete(ctx, options.ID) + + return request.Execute() +} diff --git a/pkg/api/project_variable/action_edit.go b/pkg/api/project_variable/action_edit.go new file mode 100644 index 0000000..27a6bf0 --- /dev/null +++ b/pkg/api/project_variable/action_edit.go @@ -0,0 +1,82 @@ +package project_variable + +import ( + "net/http" + + "bunnyshell.com/cli/pkg/api" + "bunnyshell.com/cli/pkg/api/common" + "bunnyshell.com/cli/pkg/config/enum" + "bunnyshell.com/cli/pkg/lib" + "bunnyshell.com/sdk" + "github.com/spf13/pflag" +) + +type EditOptions struct { + common.ItemOptions + + sdk.ProjectVariableEditAction + + EditData +} + +type EditData struct { + Value string + IsSecret enum.Bool +} + +func NewEditOptions(id string) *EditOptions { + return &EditOptions{ + ItemOptions: *common.NewItemOptions(id), + + EditData: EditData{}, + + ProjectVariableEditAction: *sdk.NewProjectVariableEditActionWithDefaults(), + } +} + +func (eso *EditOptions) UpdateFlagSet(flags *pflag.FlagSet) { + data := &eso.EditData + + flags.StringVar(&data.Value, "value", data.Value, "Update the project variable value") + + isSecretFlag := enum.BoolFlag( + &eso.EditData.IsSecret, + "secret", + "Whether the project variable is secret or not", + ) + flags.AddFlag(isSecretFlag) + isSecretFlag.NoOptDefVal = "true" +} + +func Edit(options *EditOptions) (*sdk.ProjectVariableItem, error) { + model, resp, err := EditRaw(options) + if err != nil { + return nil, api.ParseError(resp, err) + } + + return model, nil +} + +func EditRaw(options *EditOptions) (*sdk.ProjectVariableItem, *http.Response, error) { + profile := options.GetProfile() + + ctx, cancel := lib.GetContextFromProfile(profile) + defer cancel() + + request := lib.GetAPIFromProfile(profile).ProjectVariableAPI.ProjectVariableEdit(ctx, options.ID) + + return applyEditOptions(request, options).Execute() +} + +func applyEditOptions( + request sdk.ApiProjectVariableEditRequest, + options *EditOptions, +) sdk.ApiProjectVariableEditRequest { + if options.EditData.IsSecret != enum.BoolNone { + options.ProjectVariableEditAction.SetIsSecret(options.EditData.IsSecret == enum.BoolTrue) + } + + request = request.ProjectVariableEditAction(options.ProjectVariableEditAction) + + return request +} diff --git a/pkg/api/project_variable/item.go b/pkg/api/project_variable/item.go new file mode 100644 index 0000000..11b78dc --- /dev/null +++ b/pkg/api/project_variable/item.go @@ -0,0 +1,34 @@ +package project_variable + +import ( + "net/http" + + "bunnyshell.com/cli/pkg/api" + "bunnyshell.com/cli/pkg/api/common" + "bunnyshell.com/cli/pkg/lib" + "bunnyshell.com/sdk" +) + +func NewItemOptions(id string) *common.ItemOptions { + return common.NewItemOptions(id) +} + +func Get(options *common.ItemOptions) (*sdk.ProjectVariableItem, error) { + model, resp, err := GetRaw(options) + if err != nil { + return nil, api.ParseError(resp, err) + } + + return model, nil +} + +func GetRaw(options *common.ItemOptions) (*sdk.ProjectVariableItem, *http.Response, error) { + profile := options.GetProfile() + + ctx, cancel := lib.GetContextFromProfile(profile) + defer cancel() + + request := lib.GetAPIFromProfile(profile).ProjectVariableAPI.ProjectVariableView(ctx, options.ID) + + return request.Execute() +} diff --git a/pkg/api/project_variable/list.go b/pkg/api/project_variable/list.go new file mode 100644 index 0000000..7b5672a --- /dev/null +++ b/pkg/api/project_variable/list.go @@ -0,0 +1,76 @@ +package project_variable + +import ( + "net/http" + + "bunnyshell.com/cli/pkg/api" + "bunnyshell.com/cli/pkg/api/common" + "bunnyshell.com/cli/pkg/lib" + "bunnyshell.com/sdk" + "github.com/spf13/pflag" +) + +type ListOptions struct { + common.ListOptions + + Organization string + Project string + + Name string +} + +func NewListOptions() *ListOptions { + return &ListOptions{ + ListOptions: *common.NewListOptions(), + } +} + +func (lo *ListOptions) UpdateFlagSet(flags *pflag.FlagSet) { + flags.StringVar(&lo.Name, "name", lo.Name, "Filter by Name") + + lo.ListOptions.UpdateFlagSet(flags) +} + +func List(options *ListOptions) (*sdk.PaginatedProjectVariableCollection, error) { + model, resp, err := ListRaw(options) + if err != nil { + return nil, api.ParseError(resp, err) + } + + return model, nil +} + +func ListRaw(options *ListOptions) (*sdk.PaginatedProjectVariableCollection, *http.Response, error) { + profile := options.GetProfile() + + ctx, cancel := lib.GetContextFromProfile(profile) + defer cancel() + + request := lib.GetAPIFromProfile(profile).ProjectVariableAPI.ProjectVariableList(ctx) + + return applyOptions(request, options).Execute() +} + +func applyOptions(request sdk.ApiProjectVariableListRequest, options *ListOptions) sdk.ApiProjectVariableListRequest { + if options == nil { + return request + } + + if options.Page > 1 { + request = request.Page(options.Page) + } + + if options.Organization != "" { + request = request.Organization(options.Organization) + } + + if options.Project != "" { + request = request.Project(options.Project) + } + + if options.Name != "" { + request = request.Name(options.Name) + } + + return request +} diff --git a/pkg/api/registry_integration/item.go b/pkg/api/registry_integration/item.go new file mode 100644 index 0000000..f464ed3 --- /dev/null +++ b/pkg/api/registry_integration/item.go @@ -0,0 +1,34 @@ +package registry_integration + +import ( + "net/http" + + "bunnyshell.com/cli/pkg/api" + "bunnyshell.com/cli/pkg/api/common" + "bunnyshell.com/cli/pkg/lib" + "bunnyshell.com/sdk" +) + +func NewItemOptions(id string) *common.ItemOptions { + return common.NewItemOptions(id) +} + +func Get(options *common.ItemOptions) (*sdk.RegistryIntegrationItem, error) { + model, resp, err := GetRaw(options) + if err != nil { + return nil, api.ParseError(resp, err) + } + + return model, nil +} + +func GetRaw(options *common.ItemOptions) (*sdk.RegistryIntegrationItem, *http.Response, error) { + profile := options.GetProfile() + + ctx, cancel := lib.GetContextFromProfile(profile) + defer cancel() + + request := lib.GetAPIFromProfile(profile).RegistryIntegrationAPI.RegistryIntegrationView(ctx, options.ID) + + return request.Execute() +} diff --git a/pkg/api/registry_integration/list.go b/pkg/api/registry_integration/list.go new file mode 100644 index 0000000..ebb8420 --- /dev/null +++ b/pkg/api/registry_integration/list.go @@ -0,0 +1,67 @@ +package registry_integration + +import ( + "net/http" + + "bunnyshell.com/cli/pkg/api" + "bunnyshell.com/cli/pkg/api/common" + "bunnyshell.com/cli/pkg/lib" + "bunnyshell.com/sdk" + "github.com/spf13/pflag" +) + +type ListOptions struct { + common.ListOptions + + Organization string + Environment string + + CloudProvider string + Status string +} + +func NewListOptions() *ListOptions { + return &ListOptions{ + ListOptions: *common.NewListOptions(), + } +} + +func (lo *ListOptions) UpdateFlagSet(flags *pflag.FlagSet) { + lo.ListOptions.UpdateFlagSet(flags) +} + +func List(options *ListOptions) (*sdk.PaginatedRegistryIntegrationCollection, error) { + model, resp, err := ListRaw(options) + if err != nil { + return nil, api.ParseError(resp, err) + } + + return model, nil +} + +func ListRaw(options *ListOptions) (*sdk.PaginatedRegistryIntegrationCollection, *http.Response, error) { + profile := options.GetProfile() + + ctx, cancel := lib.GetContextFromProfile(profile) + defer cancel() + + request := lib.GetAPIFromProfile(profile).RegistryIntegrationAPI.RegistryIntegrationList(ctx) + + return applyOptions(request, options).Execute() +} + +func applyOptions(request sdk.ApiRegistryIntegrationListRequest, options *ListOptions) sdk.ApiRegistryIntegrationListRequest { + if options == nil { + return request + } + + if options.Page > 1 { + request = request.Page(options.Page) + } + + if options.Organization != "" { + request = request.Organization(options.Organization) + } + + return request +} diff --git a/pkg/api/variable/action_create.go b/pkg/api/variable/action_create.go new file mode 100644 index 0000000..277d623 --- /dev/null +++ b/pkg/api/variable/action_create.go @@ -0,0 +1,75 @@ +package variable + +import ( + "net/http" + + "bunnyshell.com/cli/pkg/api" + "bunnyshell.com/cli/pkg/api/common" + "bunnyshell.com/cli/pkg/config/enum" + "bunnyshell.com/cli/pkg/lib" + "bunnyshell.com/cli/pkg/util" + "bunnyshell.com/sdk" + "github.com/spf13/pflag" +) + +type CreateOptions struct { + common.Options + + sdk.EnvironmentVariableCreateAction + + Value string + IsSecret enum.Bool +} + +func NewCreateOptions() *CreateOptions { + variableCreateActionCreateOptions := sdk.NewEnvironmentVariableCreateActionWithDefaults() + + return &CreateOptions{ + EnvironmentVariableCreateAction: *variableCreateActionCreateOptions, + } +} + +func (co *CreateOptions) UpdateFlagSet(flags *pflag.FlagSet) { + flags.StringVar(&co.Name, "name", co.Name, "Unique name for the environment variable") + util.MarkFlagRequiredWithHelp(flags.Lookup("name"), "A unique name within the environment for the new environment variable") + + flags.StringVar(&co.Value, "value", co.Value, "The value of the environment variable") + util.MarkFlagRequiredWithHelp(flags.Lookup("value"), "A value for this environment variable") + util.MarkFlag(flags.Lookup("value"), util.FlagAllowBlank) + + isSecretFlag := enum.BoolFlag( + &co.IsSecret, + "secret", + "Whether the environment variable is secret or not", + ) + flags.AddFlag(isSecretFlag) + isSecretFlag.NoOptDefVal = "true" +} + +func Create(options *CreateOptions) (*sdk.EnvironmentVariableItem, error) { + options.EnvironmentVariableCreateAction.SetValue(options.Value) + + if options.IsSecret == enum.BoolTrue { + options.EnvironmentVariableCreateAction.SetIsSecret(true) + } + + model, resp, err := CreateRaw(options) + if err != nil { + return nil, api.ParseError(resp, err) + } + + return model, nil +} + +func CreateRaw(options *CreateOptions) (*sdk.EnvironmentVariableItem, *http.Response, error) { + profile := options.GetProfile() + + ctx, cancel := lib.GetContextFromProfile(profile) + defer cancel() + + request := lib.GetAPIFromProfile(profile). + EnvironmentVariableAPI.EnvironmentVariableCreate(ctx). + EnvironmentVariableCreateAction(options.EnvironmentVariableCreateAction) + + return request.Execute() +} diff --git a/pkg/api/variable/action_delete.go b/pkg/api/variable/action_delete.go new file mode 100644 index 0000000..5c4f761 --- /dev/null +++ b/pkg/api/variable/action_delete.go @@ -0,0 +1,38 @@ +package variable + +import ( + "net/http" + + "bunnyshell.com/cli/pkg/api" + "bunnyshell.com/cli/pkg/api/common" + "bunnyshell.com/cli/pkg/lib" +) + +type DeleteOptions struct { + common.ItemOptions +} + +func NewDeleteOptions() *DeleteOptions { + return &DeleteOptions{} +} + +func Delete(options *DeleteOptions) error { + resp, err := DeleteRaw(options) + if err != nil { + return api.ParseError(resp, err) + } + + return nil +} + +func DeleteRaw(options *DeleteOptions) (*http.Response, error) { + profile := options.GetProfile() + + ctx, cancel := lib.GetContextFromProfile(profile) + defer cancel() + + request := lib.GetAPIFromProfile(profile). + EnvironmentVariableAPI.EnvironmentVariableDelete(ctx, options.ID) + + return request.Execute() +} diff --git a/pkg/api/variable/action_edit.go b/pkg/api/variable/action_edit.go new file mode 100644 index 0000000..ffa368e --- /dev/null +++ b/pkg/api/variable/action_edit.go @@ -0,0 +1,82 @@ +package variable + +import ( + "net/http" + + "bunnyshell.com/cli/pkg/api" + "bunnyshell.com/cli/pkg/api/common" + "bunnyshell.com/cli/pkg/config/enum" + "bunnyshell.com/cli/pkg/lib" + "bunnyshell.com/sdk" + "github.com/spf13/pflag" +) + +type EditOptions struct { + common.ItemOptions + + sdk.EnvironmentVariableEditAction + + EditData +} + +type EditData struct { + Value string + IsSecret enum.Bool +} + +func NewEditOptions(id string) *EditOptions { + return &EditOptions{ + ItemOptions: *common.NewItemOptions(id), + + EditData: EditData{}, + + EnvironmentVariableEditAction: *sdk.NewEnvironmentVariableEditActionWithDefaults(), + } +} + +func (eso *EditOptions) UpdateFlagSet(flags *pflag.FlagSet) { + data := &eso.EditData + + flags.StringVar(&data.Value, "value", data.Value, "Update the environment variable value") + + isSecretFlag := enum.BoolFlag( + &eso.EditData.IsSecret, + "secret", + "Whether the environment variable is secret or not", + ) + flags.AddFlag(isSecretFlag) + isSecretFlag.NoOptDefVal = "true" +} + +func Edit(options *EditOptions) (*sdk.EnvironmentVariableItem, error) { + model, resp, err := EditRaw(options) + if err != nil { + return nil, api.ParseError(resp, err) + } + + return model, nil +} + +func EditRaw(options *EditOptions) (*sdk.EnvironmentVariableItem, *http.Response, error) { + profile := options.GetProfile() + + ctx, cancel := lib.GetContextFromProfile(profile) + defer cancel() + + request := lib.GetAPIFromProfile(profile).EnvironmentVariableAPI.EnvironmentVariableEdit(ctx, options.ID) + + return applyEditOptions(request, options).Execute() +} + +func applyEditOptions( + request sdk.ApiEnvironmentVariableEditRequest, + options *EditOptions, +) sdk.ApiEnvironmentVariableEditRequest { + if options.EditData.IsSecret != enum.BoolNone { + options.EnvironmentVariableEditAction.SetIsSecret(options.EditData.IsSecret == enum.BoolTrue) + } + + request = request.EnvironmentVariableEditAction(options.EnvironmentVariableEditAction) + + return request +} diff --git a/pkg/formatter/stylish.go b/pkg/formatter/stylish.go index cbbe42e..ca653cb 100644 --- a/pkg/formatter/stylish.go +++ b/pkg/formatter/stylish.go @@ -30,6 +30,8 @@ func stylish(data interface{}) ([]byte, error) { tabulateEventCollection(writer, dataType) case *sdk.PaginatedEnvironmentVariableCollection: tabulateEnvironmentVariableCollection(writer, dataType) + case *sdk.PaginatedProjectVariableCollection: + tabulateProjectVariableCollection(writer, dataType) case *sdk.PaginatedKubernetesIntegrationCollection: tabulateKubernetesCollection(writer, dataType) case *sdk.PaginatedPipelineCollection: @@ -42,6 +44,8 @@ func stylish(data interface{}) ([]byte, error) { tabulateTemplateCollection(writer, dataType) case *sdk.PaginatedTemplatesRepositoryCollection: tabulateTemplatesRepositoryCollection(writer, dataType) + case *sdk.PaginatedRegistryIntegrationCollection: + tabulateRegistryIntegrationsCollection(writer, dataType) case []sdk.ComponentEndpointCollection: tabulateAggregateEndpoint(writer, dataType) case *sdk.OrganizationItem: @@ -56,8 +60,12 @@ func stylish(data interface{}) ([]byte, error) { tabulateEventItem(writer, dataType) case *sdk.EnvironmentVariableItem: tabulateEnvironmentVariableItem(writer, dataType) + case *sdk.ProjectVariableItem: + tabulateProjectVariableItem(writer, dataType) case *sdk.KubernetesIntegrationItem: tabulateKubernetesItem(writer, dataType) + case *sdk.RegistryIntegrationItem: + tabulateRegistryIntegrationItem(writer, dataType) case *sdk.PipelineItem: tabulatePipelineItem(writer, dataType) case *sdk.ComponentGitItem: @@ -118,6 +126,49 @@ func tabulateProjectItem(w *tabwriter.Writer, item *sdk.ProjectItem) { fmt.Fprintf(w, "%v\t %v\n", "OrganizationID", item.GetOrganization()) fmt.Fprintf(w, "%v\t %v\n", "Name", item.GetName()) fmt.Fprintf(w, "%v\t %v\n", "Environments", item.GetTotalEnvironments()) + + first := true + for key, value := range item.GetLabels() { + if first { + fmt.Fprintf(w, "%v\t %v\t %v\n", "Labels", key, value) + + first = false + } else { + fmt.Fprintf(w, "\t %v\t %v\n", key, value) + } + } + + if buildSettings, ok := item.GetBuildSettingsOk(); ok { + tabulateBuildSettings(w, buildSettings) + } +} + +func tabulateBuildSettings(w *tabwriter.Writer, item *sdk.BuildSettingsItem) { + buildCluster := "" + if useManagedCluster, ok := item.GetUseManagedClusterOk(); ok && !*useManagedCluster { + if k8sCluster, ok := item.GetKubernetesIntegrationOk(); ok && k8sCluster != nil { + buildCluster = *k8sCluster + } else { + buildCluster = "Project cluster" + } + } + + if buildCluster != "" { + fmt.Fprintf(w, "%v\t %v\n", "Build Cluster", buildCluster) + } + + registryIntegration := "" + if useManagedRegistry, ok := item.GetUseManagedRegistryOk(); ok && !*useManagedRegistry { + if registry, ok := item.GetRegistryIntegrationOk(); ok && registry != nil { + registryIntegration = *registry + } else { + registryIntegration = "Project registry" + } + } + + if registryIntegration != "" { + fmt.Fprintf(w, "%v\t %v\n", "Build Registry", registryIntegration) + } } func tabulateEnvironmentCollection(w *tabwriter.Writer, data *sdk.PaginatedEnvironmentCollection) { @@ -149,6 +200,15 @@ func tabulateEnvironmentItem(w *tabwriter.Writer, item *sdk.EnvironmentItem) { fmt.Fprintf(w, "\t %v\t %v\n", key, value) } } + + if buildSettings, ok := item.GetBuildSettingsOk(); ok { + if buildSettings == nil { + fmt.Fprintf(w, "%v\t %v\n", "Build Cluster", "Project cluster") + fmt.Fprintf(w, "%v\t %v\n", "Build Registry", "Project registry") + } else { + tabulateBuildSettings(w, buildSettings) + } + } } func tabulateComponentCollection(w *tabwriter.Writer, data *sdk.PaginatedComponentCollection) { @@ -187,6 +247,16 @@ func tabulateEnvironmentVariableCollection(w *tabwriter.Writer, data *sdk.Pagina } } +func tabulateProjectVariableCollection(w *tabwriter.Writer, data *sdk.PaginatedProjectVariableCollection) { + fmt.Fprintf(w, "%v\t %v\t %v\t %v\n", "ProjectVarID", "ProjectID", "OrganizationID", "Name") + + if data.Embedded != nil { + for _, item := range data.Embedded.Item { + fmt.Fprintf(w, "%v\t %v\t %v\t %v\n", item.GetId(), item.GetProject(), item.GetOrganization(), item.GetName()) + } + } +} + func tabulateEventCollection(w *tabwriter.Writer, data *sdk.PaginatedEventCollection) { fmt.Fprintf(w, "%v\t %v\t %v\t %v\t %v\n", "EventID", "EnvironmentID", "OrganizationID", "Type", "Status") @@ -208,6 +278,7 @@ func tabulateEventItem(w *tabwriter.Writer, item *sdk.EventItem) { } func tabulateEnvironmentVariableItem(w *tabwriter.Writer, item *sdk.EnvironmentVariableItem) { + fmt.Fprintf(w, "%v\t %v\n", "EnvironmentVariableID", item.GetId()) fmt.Fprintf(w, "%v\t %v\n", "EnvironmentID", item.GetEnvironment()) fmt.Fprintf(w, "%v\t %v\n", "OrganizationID", item.GetOrganization()) fmt.Fprintf(w, "%v\t %v\n", "Name", item.GetName()) @@ -215,6 +286,15 @@ func tabulateEnvironmentVariableItem(w *tabwriter.Writer, item *sdk.EnvironmentV fmt.Fprintf(w, "%v\t %v\n", "Secret", item.GetSecret()) } +func tabulateProjectVariableItem(w *tabwriter.Writer, item *sdk.ProjectVariableItem) { + fmt.Fprintf(w, "%v\t %v\n", "ProjectVariableID", item.GetId()) + fmt.Fprintf(w, "%v\t %v\n", "ProjectID", item.GetProject()) + fmt.Fprintf(w, "%v\t %v\n", "OrganizationID", item.GetOrganization()) + fmt.Fprintf(w, "%v\t %v\n", "Name", item.GetName()) + fmt.Fprintf(w, "%v\t %v\n", "Value", item.GetValue()) + fmt.Fprintf(w, "%v\t %v\n", "Secret", item.GetSecret()) +} + func tabulateGeneric(w *tabwriter.Writer, item *sdk.ProblemGeneric) { fmt.Fprintf(w, "%v\n", "ERROR") fmt.Fprintf(w, "%v\t %v\n", "Title", item.GetTitle()) diff --git a/pkg/formatter/stylish.registry_integration.go b/pkg/formatter/stylish.registry_integration.go new file mode 100644 index 0000000..c33a247 --- /dev/null +++ b/pkg/formatter/stylish.registry_integration.go @@ -0,0 +1,34 @@ +package formatter + +import ( + "fmt" + "text/tabwriter" + + "bunnyshell.com/sdk" +) + +func tabulateRegistryIntegrationsCollection(w *tabwriter.Writer, data *sdk.PaginatedRegistryIntegrationCollection) { + fmt.Fprintf(w, "%v\t %v\t %v\t %v\t %v\n", "IntegrationID", "OrganizationID", "Name", "Provider", "Status") + + if data.Embedded != nil { + for _, item := range data.Embedded.Item { + fmt.Fprintf( + w, + "%v\t %v\t %v\t %v\t %v\n", + item.GetId(), + item.GetOrganization(), + item.GetName(), + item.GetProviderName(), + item.GetStatus(), + ) + } + } +} + +func tabulateRegistryIntegrationItem(w *tabwriter.Writer, item *sdk.RegistryIntegrationItem) { + fmt.Fprintf(w, "%v\t %v\n", "IntegrationID", item.GetId()) + fmt.Fprintf(w, "%v\t %v\n", "OrganizationID", item.GetOrganization()) + fmt.Fprintf(w, "%v\t %v\n", "Name", item.GetName()) + fmt.Fprintf(w, "%v\t %v\n", "Provider", item.GetProviderName()) + fmt.Fprintf(w, "%v\t %v\n", "Status", item.GetStatus()) +} diff --git a/pkg/helper/git/git_spec.go b/pkg/helper/git/git_spec.go new file mode 100644 index 0000000..eb65033 --- /dev/null +++ b/pkg/helper/git/git_spec.go @@ -0,0 +1,33 @@ +package git + +import ( + "errors" + "net/url" + "strings" +) + +var errEmptySpec = errors.New("empty spec") + +func ParseGitSec(spec string) (string, string, error) { + if len(spec) == 0 { + return "", "", errEmptySpec + } + + if spec[0] == '@' { + return "", spec[1:], nil + } + + info, err := url.Parse(spec) + if err != nil { + return "", "", err + } + + if !strings.Contains(info.Path, "@") { + return spec, "", nil + } + + chunks := strings.SplitN(info.Path, "@", 2) + info.Path = chunks[0] + + return info.String(), chunks[1], nil +} diff --git a/pkg/interactive/cobra.go b/pkg/interactive/cobra.go index 7dfef43..e10c5d9 100644 --- a/pkg/interactive/cobra.go +++ b/pkg/interactive/cobra.go @@ -65,8 +65,14 @@ func setterValidator(flag *pflag.Flag) survey.Validator { func getQuestion(flag *pflag.Flag) func() error { message := fmt.Sprintf("Provide a value for flag '%s':", flag.Name) + + minimumLength := 1 + if util.GetFlagBoolAnnotation(flag, util.FlagAllowBlank) { + minimumLength = 0 + } + validator := All( - AssertMinimumLength(1), + AssertMinimumLength(minimumLength), setterValidator(flag), ) diff --git a/pkg/util/cobra.go b/pkg/util/cobra.go index 2456c6e..09adfbd 100644 --- a/pkg/util/cobra.go +++ b/pkg/util/cobra.go @@ -14,9 +14,10 @@ const ( ) const ( - FlagHidden BoolFlagType = "bns_annotation_hidden" - FlagRequired BoolFlagType = cobra.BashCompOneRequiredFlag - FlagDirname BoolFlagType = cobra.BashCompSubdirsInDir + FlagHidden BoolFlagType = "bns_annotation_hidden" + FlagRequired BoolFlagType = cobra.BashCompOneRequiredFlag + FlagDirname BoolFlagType = cobra.BashCompSubdirsInDir + FlagAllowBlank BoolFlagType = "bns_annotation_allow_blank" ) func HasHelp(flag *pflag.Flag) bool { @@ -80,3 +81,15 @@ func AppendFlagHelp(flag *pflag.Flag, helpTemplate string) *pflag.Flag { return flag } + +func GetFlagBoolAnnotation(flag *pflag.Flag, annotation BoolFlagType) bool { + if flag.Annotations == nil { + return false + } + + if flag.Annotations[string(annotation)] == nil { + return false + } + + return flag.Annotations[string(annotation)][0] == StrTrue +}