├── .github └── workflows │ ├── audit.yaml │ └── release.yaml ├── .gitignore ├── .goreleaser.yaml ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── cmd ├── component │ ├── action │ │ ├── port_forward.go │ │ ├── root.go │ │ ├── ssh.go │ │ └── update.go │ ├── list.go │ ├── root.go │ ├── show.go │ └── variable │ │ ├── action │ │ ├── create.go │ │ ├── delete.go │ │ ├── edit.go │ │ └── root.go │ │ ├── list.go │ │ ├── root.go │ │ └── show.go ├── component_debug │ ├── down.go │ ├── root.go │ ├── ssh.go │ └── up.go ├── configure │ ├── init.go │ ├── profile │ │ ├── add.go │ │ ├── context.go │ │ ├── default.go │ │ ├── list.go │ │ ├── remove.go │ │ └── root.go │ ├── root.go │ └── show.go ├── environment │ ├── action │ │ ├── clone.go │ │ ├── create.go │ │ ├── definition.go │ │ ├── delete.go │ │ ├── deploy.go │ │ ├── endpoints.go │ │ ├── root.go │ │ ├── start.go │ │ ├── stop.go │ │ ├── update.build_settings.go │ │ ├── update.components.go │ │ ├── update.configuration.go │ │ └── update.settings.go │ ├── list.go │ ├── root.go │ └── show.go ├── event │ ├── list.go │ ├── root.go │ └── show.go ├── git │ ├── info.go │ ├── prepare.go │ └── root.go ├── k8sIntegration │ ├── list.go │ ├── root.go │ └── show.go ├── organization │ ├── list.go │ ├── root.go │ └── show.go ├── pipeline │ ├── list.go │ ├── monitor.go │ ├── root.go │ └── show.go ├── project │ ├── action │ │ ├── create.go │ │ ├── delete.go │ │ ├── root.go │ │ ├── update.build_settings.go │ │ └── update.settings.go │ ├── list.go │ ├── root.go │ └── show.go ├── project_variable │ ├── action │ │ ├── create.go │ │ ├── delete.go │ │ ├── edit.go │ │ └── root.go │ ├── list.go │ ├── root.go │ └── show.go ├── registry_integration │ ├── list.go │ ├── root.go │ └── show.go ├── remote_development │ ├── config │ │ ├── generate.go │ │ ├── root.go │ │ └── show.go │ ├── down.go │ ├── root.go │ └── up.go ├── root.go ├── secret │ ├── decrypt.go │ ├── decrypt_definition.go │ ├── definition.go │ ├── encrypt.go │ ├── encrypt_definition.go │ └── root.go ├── template │ ├── definition.go │ ├── generate.go │ ├── list.go │ ├── repository │ │ ├── list.go │ │ ├── root.go │ │ └── show.go │ ├── root.go │ ├── show.go │ └── validate.go ├── utils │ ├── port_forward.go │ ├── root.go │ └── ssh.go ├── variable │ ├── action │ │ ├── create.go │ │ ├── delete.go │ │ ├── edit.go │ │ ├── import.go │ │ └── root.go │ ├── list.go │ ├── root.go │ └── show.go ├── variable_group │ ├── action │ │ ├── create.go │ │ ├── delete.go │ │ ├── edit.go │ │ └── root.go │ ├── list.go │ ├── root.go │ └── show.go └── version │ └── version.go ├── config.sample.yaml ├── go.mod ├── go.sum ├── installer.sh ├── main.go └── pkg ├── api ├── build_settings │ ├── edit_options.go │ └── validation.go ├── common │ ├── action.go │ ├── item.go │ ├── list.go │ ├── options.go │ └── partial_action.go ├── component │ ├── action_edit.go │ ├── endpoint │ │ ├── aggregate.go │ │ ├── item.go │ │ └── list.go │ ├── git │ │ ├── aggregate.go │ │ ├── item.go │ │ └── list.go │ ├── item.go │ ├── list.go │ ├── rdev_config.go │ ├── rdev_profile.go │ └── resources.go ├── component_variable │ ├── action_create.go │ ├── action_delete.go │ ├── action_edit.go │ ├── item.go │ └── list.go ├── environment │ ├── action_clone.go │ ├── action_create.go │ ├── action_definition.go │ ├── action_delete.go │ ├── action_deploy.go │ ├── action_edit_build_settings.go │ ├── action_edit_components.go │ ├── action_edit_configuration.go │ ├── action_edit_settings.go │ ├── action_genesis_source.go │ ├── action_kubeconfig.go │ ├── action_start.go │ ├── action_stop.go │ ├── item.go │ └── list.go ├── error.go ├── event │ ├── item.go │ └── list.go ├── k8s │ ├── item.go │ └── list.go ├── organization │ ├── item.go │ └── list.go ├── pipeline │ ├── item.go │ └── list.go ├── project │ ├── action_create.go │ ├── action_delete.go │ ├── action_edit_build_settings.go │ ├── action_edit_settings.go │ ├── item.go │ └── list.go ├── project_variable │ ├── action_create.go │ ├── action_delete.go │ ├── action_edit.go │ ├── item.go │ └── list.go ├── registry_integration │ ├── item.go │ └── list.go ├── secret │ ├── decrypt.go │ ├── encrypt.go │ └── transcript_configuration.go ├── template │ ├── definition.go │ ├── item.go │ ├── list.go │ ├── repository │ │ ├── item.go │ │ └── list.go │ └── validate.go ├── variable │ ├── action_create.go │ ├── action_delete.go │ ├── action_edit.go │ ├── item.go │ └── list.go └── variable_group │ ├── action_create.go │ ├── action_delete.go │ ├── action_edit.go │ ├── item.go │ └── list.go ├── build └── info.go ├── config ├── config.go ├── enum │ └── bool.go ├── manager.cobra.go ├── manager.go ├── manager.loader.go ├── option │ ├── bool.go │ ├── count.go │ ├── duration.go │ ├── group.go │ ├── pflag.go │ ├── string.go │ └── value.go ├── options.go ├── settings.go ├── types.go └── vars.go ├── debug_component ├── action │ ├── action.go │ ├── down.go │ ├── down │ │ ├── down.cobra.go │ │ ├── down.go │ │ └── vars.go │ ├── up.go │ ├── up │ │ ├── up.cobra.go │ │ ├── up.go │ │ └── vars.go │ └── vars.go ├── debug_component.go ├── main.go └── workspace.go ├── environment ├── environment_resource.go └── environment_resource_interactive.go ├── formatter ├── formatter.go ├── stylish.endpoints.go ├── stylish.git.go ├── stylish.go ├── stylish.k8s.go ├── stylish.pipeline.go ├── stylish.registry_integration.go ├── stylish.secret.go ├── stylish.template.go ├── stylish.template.repository.go ├── stylish.template.variable.go └── stylish.variable.groups.go ├── helper ├── git │ ├── git.go │ ├── git_spec.go │ └── manager.go ├── rdev │ ├── data.go │ ├── generate │ │ └── rdev.yaml │ ├── loader.go │ └── rdev.go └── template │ ├── data.go │ ├── generate │ ├── README.md │ ├── bunnyshell.yaml │ └── template.yaml │ ├── loader.go │ └── template.go ├── interactive ├── cobra.go ├── interactive.go ├── question.go ├── validations.go └── vars.go ├── k8s ├── bridge │ ├── bunnyshell.go │ ├── resource.loader.go │ ├── resource.spec.go │ └── vars.go ├── client.go ├── kubectl │ └── exec │ │ └── exec.go └── port_forward.go ├── lib ├── apiclient.go ├── bunny.go ├── command.go ├── pagination.go └── printer.go ├── net ├── client.go └── vars.go ├── port_forward ├── main.go ├── port_forward_manager.go └── workspace │ ├── vars.go │ └── workspace.go ├── progress ├── event.go ├── pipeline.go ├── progress.pipeline.go ├── vars.go └── workflow.go ├── remote_development ├── action │ ├── action.go │ ├── down.go │ ├── down │ │ ├── down.cobra.go │ │ ├── down.go │ │ └── vars.go │ ├── up.go │ ├── up.interactive.go │ ├── up │ │ ├── profile.go │ │ ├── syncmode.go │ │ ├── up.cobra.go │ │ ├── up.go │ │ ├── up.loader.go │ │ ├── up.loader.remote.go │ │ ├── utils.go │ │ └── vars.go │ └── vars.go ├── bridge.go ├── config │ ├── config.go │ ├── manager.cobra.go │ ├── manager.go │ ├── types.go │ └── vars.go ├── main.go ├── remote_development.go ├── workspace.go └── workspace │ ├── vars.go │ └── workspace.go ├── util ├── cobra.go ├── network.go ├── os.go ├── spinner.go ├── util.go ├── vars.go └── workspace.go └── wizard ├── component.go ├── environment.go ├── k8s ├── container.go ├── pod.go ├── rdev.go └── var.go ├── organization.go ├── project.go ├── vars.go ├── wizard.go └── wizard.pagination.go /.github/workflows/audit.yaml: -------------------------------------------------------------------------------- 1 | name: Audit 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | audit: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | 14 | - name: Set up Go 15 | uses: actions/setup-go@v5 16 | with: 17 | go-version: 1.23 18 | 19 | - name: Download dependencies 20 | run: go mod download 21 | 22 | - name: Verify dependencies 23 | run: go mod verify 24 | 25 | - name: Build 26 | run: go build -v ./... 27 | 28 | - name: Run go vet 29 | run: go vet ./... 30 | 31 | - name: Install staticcheck 32 | run: go install honnef.co/go/tools/cmd/staticcheck@latest 33 | 34 | - name: Run staticcheck 35 | run: staticcheck ./... 36 | 37 | - name: Install golint 38 | run: go install golang.org/x/lint/golint@latest 39 | 40 | - name: Run golint 41 | run: golint ./... 42 | 43 | - name: Run tests 44 | run: go test -race -vet=off ./... 45 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Release on tag 2 | 3 | on: 4 | push: 5 | tags: 6 | - v* 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - 13 | name: Log in to Docker Hub 14 | uses: docker/login-action@v3 15 | with: 16 | username: ${{ secrets.DOCKERHUB_USERNAME }} 17 | password: ${{ secrets.DOCKERHUB_TOKEN }} 18 | 19 | - 20 | name: Checkout 21 | uses: actions/checkout@v4 22 | with: 23 | fetch-depth: 0 24 | - 25 | name: Fetch all tags 26 | run: git fetch --force --tags 27 | 28 | - 29 | name: Set up Go 30 | uses: actions/setup-go@v5 31 | with: 32 | go-version: 1.23 33 | - 34 | name: Set up QEMU 35 | uses: docker/setup-qemu-action@v3 36 | - 37 | name: Run GoReleaser 38 | uses: goreleaser/goreleaser-action@v5 39 | with: 40 | distribution: goreleaser 41 | version: latest 42 | args: release --clean 43 | env: 44 | GITHUB_TOKEN: ${{ secrets.GH_PAT }} 45 | SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # gorelease 2 | /dist 3 | 4 | # go mod vendor 5 | /vendor 6 | 7 | # other 8 | .DS_Store 9 | .vscode 10 | /.idea 11 | __debug_bin 12 | /.dev -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine as slim 2 | 3 | # promoting json to main format along with jq for access 4 | RUN apk --no-cache add jq 5 | 6 | # autocomplete support 7 | RUN apk --no-cache add bash bash-completion 8 | RUN echo 'source <(bns completion bash)' >> ~/.bashrc 9 | 10 | # common tools 11 | RUN apk --no-cache add curl sed 12 | 13 | # autocomplete 14 | RUN echo 'source <(bns completion bash)' >> /root/.bashrc 15 | 16 | # binaries 17 | COPY bns /usr/bin 18 | 19 | # main config file 20 | RUN mkdir /root/.bunnyshell 21 | COPY config.sample.yaml /root/.bunnyshell/config.yaml 22 | 23 | # default to the cli 24 | ENTRYPOINT ["bns"] 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022-present GitLab B.V. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .DEFAULT_GOAL := build-local 2 | 3 | .PHONY: build-local 4 | 5 | build-local: 6 | goreleaser release --snapshot --rm-dist 7 | -------------------------------------------------------------------------------- /cmd/component/action/root.go: -------------------------------------------------------------------------------- 1 | package action 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | ) 6 | 7 | var mainCmd = &cobra.Command{} 8 | 9 | func GetMainCommand() *cobra.Command { 10 | return mainCmd 11 | } 12 | -------------------------------------------------------------------------------- /cmd/component/list.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/component" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func init() { 11 | options := config.GetOptions() 12 | settings := config.GetSettings() 13 | 14 | listOptions := component.NewListOptions() 15 | 16 | command := &cobra.Command{ 17 | Use: "list", 18 | GroupID: mainGroup.ID, 19 | 20 | ValidArgsFunction: cobra.NoFileCompletions, 21 | 22 | RunE: func(cmd *cobra.Command, args []string) error { 23 | listOptions.Organization = settings.Profile.Context.Organization 24 | listOptions.Project = settings.Profile.Context.Project 25 | listOptions.Environment = settings.Profile.Context.Environment 26 | 27 | return lib.ShowCollection(cmd, listOptions, func() (lib.ModelWithPagination, error) { 28 | return component.List(listOptions) 29 | }) 30 | }, 31 | } 32 | 33 | flags := command.Flags() 34 | 35 | flags.AddFlag(options.Organization.GetFlag("organization")) 36 | flags.AddFlag(options.Project.GetFlag("project")) 37 | flags.AddFlag(options.Environment.GetFlag("environment")) 38 | 39 | listOptions.UpdateFlagSet(flags) 40 | 41 | mainCmd.AddCommand(command) 42 | } 43 | -------------------------------------------------------------------------------- /cmd/component/root.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "bunnyshell.com/cli/cmd/component/action" 5 | "bunnyshell.com/cli/cmd/component/variable" 6 | "bunnyshell.com/cli/pkg/config" 7 | "bunnyshell.com/cli/pkg/util" 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | var mainCmd = &cobra.Command{ 12 | Use: "components", 13 | Aliases: []string{"comp"}, 14 | 15 | Short: "Components", 16 | Long: "Bunnyshell Components", 17 | } 18 | 19 | var mainGroup = &cobra.Group{ 20 | ID: "components", 21 | Title: "Commands for Components:", 22 | } 23 | 24 | func init() { 25 | config.MainManager.CommandWithAPI(mainCmd) 26 | 27 | mainCmd.AddGroup(mainGroup) 28 | 29 | util.AddGroupedCommands( 30 | mainCmd, 31 | cobra.Group{ 32 | ID: "actions", 33 | Title: "Commands for Component Actions:", 34 | }, 35 | action.GetMainCommand().Commands(), 36 | ) 37 | 38 | util.AddGroupedCommands( 39 | mainCmd, 40 | cobra.Group{ 41 | ID: "variables", 42 | Title: "Commands for Component Variables:", 43 | }, 44 | []*cobra.Command{variable.GetMainCommand()}, 45 | ) 46 | 47 | config.MainManager.CommandWithGlobalOptions(mainCmd) 48 | } 49 | 50 | func GetMainCommand() *cobra.Command { 51 | return mainCmd 52 | } 53 | -------------------------------------------------------------------------------- /cmd/component/show.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/component" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func init() { 11 | options := config.GetOptions() 12 | settings := config.GetSettings() 13 | 14 | itemOptions := component.NewItemOptions("") 15 | 16 | command := &cobra.Command{ 17 | Use: "show", 18 | GroupID: mainGroup.ID, 19 | 20 | ValidArgsFunction: cobra.NoFileCompletions, 21 | 22 | RunE: func(cmd *cobra.Command, args []string) error { 23 | itemOptions.ID = settings.Profile.Context.ServiceComponent 24 | 25 | model, err := component.Get(itemOptions) 26 | if err != nil { 27 | return lib.FormatCommandError(cmd, err) 28 | } 29 | 30 | return lib.FormatCommandData(cmd, model) 31 | }, 32 | } 33 | 34 | flags := command.Flags() 35 | 36 | flags.AddFlag(options.ServiceComponent.GetRequiredFlag("id")) 37 | 38 | mainCmd.AddCommand(command) 39 | } 40 | -------------------------------------------------------------------------------- /cmd/component/variable/action/delete.go: -------------------------------------------------------------------------------- 1 | package action 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/component_variable" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func init() { 11 | var componentName string 12 | var componentVariableName string 13 | 14 | settings := config.GetSettings() 15 | 16 | deleteOptions := component_variable.NewDeleteOptions() 17 | 18 | command := &cobra.Command{ 19 | Use: "delete", 20 | 21 | ValidArgsFunction: cobra.NoFileCompletions, 22 | 23 | RunE: func(cmd *cobra.Command, args []string) error { 24 | if componentVariableName != "" { 25 | componentVariable, err := findComponentVariableByName(componentVariableName, componentName, &settings.Profile) 26 | if err != nil { 27 | return err 28 | } 29 | deleteOptions.ID = componentVariable.GetId() 30 | } 31 | 32 | err := component_variable.Delete(deleteOptions) 33 | if err != nil { 34 | return lib.FormatCommandError(cmd, err) 35 | } 36 | 37 | cmd.Printf("\nComponent variable %s successfully deleted\n", deleteOptions.ID) 38 | 39 | return nil 40 | }, 41 | } 42 | 43 | flags := command.Flags() 44 | 45 | flags.AddFlag(GetIDOption(&deleteOptions.ID).GetFlag("id")) 46 | flags.StringVar(&componentVariableName, "name", componentVariableName, "Component Variable Name") 47 | command.MarkFlagsMutuallyExclusive("name", "id") 48 | 49 | updateComponentIdentifierFlags(command, &componentName) 50 | command.MarkFlagsMutuallyExclusive("component-name", "id", "component") 51 | 52 | mainCmd.AddCommand(command) 53 | } 54 | -------------------------------------------------------------------------------- /cmd/component/variable/list.go: -------------------------------------------------------------------------------- 1 | package variable 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/component_variable" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func init() { 11 | options := config.GetOptions() 12 | settings := config.GetSettings() 13 | 14 | listOptions := component_variable.NewListOptions() 15 | 16 | command := &cobra.Command{ 17 | Use: "list", 18 | GroupID: mainGroup.ID, 19 | 20 | ValidArgsFunction: cobra.NoFileCompletions, 21 | 22 | RunE: func(cmd *cobra.Command, args []string) error { 23 | listOptions.Organization = settings.Profile.Context.Organization 24 | listOptions.Project = settings.Profile.Context.Project 25 | listOptions.Environment = settings.Profile.Context.Environment 26 | listOptions.Component = settings.Profile.Context.ServiceComponent 27 | 28 | return lib.ShowCollection(cmd, listOptions, func() (lib.ModelWithPagination, error) { 29 | return component_variable.List(listOptions) 30 | }) 31 | }, 32 | } 33 | 34 | flags := command.Flags() 35 | 36 | flags.AddFlag(options.Organization.GetFlag("organization")) 37 | flags.AddFlag(options.Project.GetFlag("project")) 38 | flags.AddFlag(options.Environment.GetFlag("environment")) 39 | flags.AddFlag(options.ServiceComponent.GetFlag("component")) 40 | 41 | listOptions.UpdateFlagSet(flags) 42 | 43 | mainCmd.AddCommand(command) 44 | } 45 | -------------------------------------------------------------------------------- /cmd/component/variable/root.go: -------------------------------------------------------------------------------- 1 | package variable 2 | 3 | import ( 4 | "bunnyshell.com/cli/cmd/component/variable/action" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/util" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | var mainCmd = &cobra.Command{ 11 | Use: "variables", 12 | Aliases: []string{"vars"}, 13 | 14 | Short: "Component Variables", 15 | } 16 | 17 | var mainGroup = &cobra.Group{ 18 | ID: "variables", 19 | Title: "Commands for Component Variables:", 20 | } 21 | 22 | func init() { 23 | config.MainManager.CommandWithAPI(mainCmd) 24 | 25 | mainCmd.AddGroup(mainGroup) 26 | 27 | util.AddGroupedCommands( 28 | mainCmd, 29 | cobra.Group{ 30 | ID: "actions", 31 | Title: "Commands for Component variables Actions:", 32 | }, 33 | action.GetMainCommand().Commands(), 34 | ) 35 | } 36 | 37 | func GetMainCommand() *cobra.Command { 38 | return mainCmd 39 | } 40 | -------------------------------------------------------------------------------- /cmd/component/variable/show.go: -------------------------------------------------------------------------------- 1 | package variable 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/component_variable" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func init() { 11 | options := config.GetOptions() 12 | settings := config.GetSettings() 13 | 14 | itemOptions := component_variable.NewItemOptions("") 15 | 16 | command := &cobra.Command{ 17 | Use: "show", 18 | GroupID: mainGroup.ID, 19 | 20 | ValidArgsFunction: cobra.NoFileCompletions, 21 | 22 | RunE: func(cmd *cobra.Command, args []string) error { 23 | itemOptions.ID = settings.Profile.Context.ServiceComponent 24 | 25 | model, err := component_variable.Get(itemOptions) 26 | if err != nil { 27 | return lib.FormatCommandError(cmd, err) 28 | } 29 | 30 | return lib.FormatCommandData(cmd, model) 31 | }, 32 | } 33 | 34 | flags := command.Flags() 35 | 36 | flags.AddFlag(options.ServiceComponent.GetRequiredFlag("id")) 37 | 38 | mainCmd.AddCommand(command) 39 | } 40 | -------------------------------------------------------------------------------- /cmd/component_debug/down.go: -------------------------------------------------------------------------------- 1 | package component_debug 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/config" 5 | "bunnyshell.com/cli/pkg/k8s/bridge" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "bunnyshell.com/cli/pkg/debug_component/action" 8 | "bunnyshell.com/cli/pkg/debug_component/action/down" 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | func init() { 13 | options := config.GetOptions() 14 | settings := config.GetSettings() 15 | 16 | resourceLoader := bridge.NewResourceLoader() 17 | downOptions := down.NewOptions(resourceLoader) 18 | 19 | command := &cobra.Command{ 20 | Use: "stop", 21 | 22 | ValidArgsFunction: cobra.NoFileCompletions, 23 | 24 | PreRunE: lib.OnlyStylish, 25 | 26 | RunE: func(cmd *cobra.Command, args []string) error { 27 | if err := resourceLoader.Load(settings.Profile); err != nil { 28 | return err 29 | } 30 | 31 | downParameters, err := downOptions.ToParameters() 32 | if err != nil { 33 | return err 34 | } 35 | 36 | downAction := action.NewDown(*resourceLoader.Environment) 37 | 38 | return downAction.Run(downParameters) 39 | }, 40 | } 41 | 42 | flags := command.Flags() 43 | 44 | flags.AddFlag(options.Organization.GetFlag("organization")) 45 | flags.AddFlag(options.Project.GetFlag("project")) 46 | flags.AddFlag(options.Environment.GetFlag("environment")) 47 | flags.AddFlag(options.ServiceComponent.GetFlag("component")) 48 | 49 | downOptions.UpdateFlagSet(command, flags) 50 | 51 | mainCmd.AddCommand(command) 52 | } 53 | -------------------------------------------------------------------------------- /cmd/component_debug/root.go: -------------------------------------------------------------------------------- 1 | package component_debug 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/config" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var mainCmd = &cobra.Command{ 9 | Use: "debug", 10 | Aliases: []string{"debug"}, 11 | 12 | Short: "Debug Component", 13 | } 14 | 15 | func init() { 16 | config.MainManager.CommandWithAPI(mainCmd) 17 | } 18 | 19 | func GetMainCommand() *cobra.Command { 20 | return mainCmd 21 | } 22 | -------------------------------------------------------------------------------- /cmd/component_debug/ssh.go: -------------------------------------------------------------------------------- 1 | package component_debug 2 | 3 | import ( 4 | "github.com/spf13/pflag" 5 | ) 6 | 7 | type SSHOptions struct { 8 | Shell string 9 | 10 | NoTTY bool 11 | NoBanner bool 12 | 13 | OverrideClusterServer string 14 | } 15 | 16 | func (o *SSHOptions) UpdateFlagSet(flags *pflag.FlagSet) { 17 | flags.StringVar(&o.Shell, "shell", o.Shell, "Shell to use") 18 | 19 | flags.BoolVar(&o.NoTTY, "no-tty", o.NoTTY, "Do not allocate a TTY") 20 | flags.BoolVar(&o.NoBanner, "no-banner", o.NoBanner, "Do not show environment banner before ssh") 21 | } 22 | -------------------------------------------------------------------------------- /cmd/configure/profile/default.go: -------------------------------------------------------------------------------- 1 | package profile 2 | 3 | import ( 4 | "errors" 5 | 6 | "bunnyshell.com/cli/pkg/config" 7 | "bunnyshell.com/cli/pkg/lib" 8 | "bunnyshell.com/cli/pkg/util" 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | func init() { 13 | profileName := "" 14 | 15 | command := &cobra.Command{ 16 | Use: "default", 17 | 18 | ValidArgsFunction: cobra.NoFileCompletions, 19 | 20 | PreRunE: func(cmd *cobra.Command, args []string) error { 21 | if errors.Is(config.MainManager.Error, config.ErrConfigLoad) { 22 | return config.MainManager.Error 23 | } 24 | 25 | return nil 26 | }, 27 | 28 | RunE: func(cmd *cobra.Command, args []string) error { 29 | if err := config.MainManager.SetDefaultProfile(profileName); err != nil { 30 | return lib.FormatCommandError(cmd, err) 31 | } 32 | 33 | if err := config.MainManager.Save(); err != nil { 34 | return lib.FormatCommandError(cmd, err) 35 | } 36 | 37 | return lib.FormatCommandData(cmd, map[string]interface{}{ 38 | "message": "Profile set as default", 39 | "data": profileName, 40 | }) 41 | }, 42 | } 43 | 44 | flags := command.Flags() 45 | 46 | flags.StringVar(&profileName, "profile", profileName, "Profile name to set as default") 47 | util.MarkFlagRequiredWithHelp(flags.Lookup("profile"), "The local profile name to set as the default profile") 48 | 49 | mainCmd.AddCommand(command) 50 | } 51 | 52 | func setDefaultProfile(profile *config.Profile) error { 53 | return setDefaultProfileByName(profile.Name) 54 | } 55 | 56 | func setDefaultProfileByName(name string) error { 57 | return config.MainManager.SetDefaultProfile(name) 58 | } 59 | -------------------------------------------------------------------------------- /cmd/configure/profile/list.go: -------------------------------------------------------------------------------- 1 | package profile 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/config" 5 | "bunnyshell.com/cli/pkg/lib" 6 | "bunnyshell.com/cli/pkg/util" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func init() { 11 | command := &cobra.Command{ 12 | Use: "list", 13 | 14 | ValidArgsFunction: cobra.NoFileCompletions, 15 | 16 | PersistentPreRunE: util.PersistentPreRunChain, 17 | 18 | RunE: func(cmd *cobra.Command, args []string) error { 19 | result := map[string]interface{}{ 20 | "file": config.GetSettings().ConfigFile, 21 | } 22 | 23 | if config.MainManager.Error != nil { 24 | result["error"] = config.MainManager.Error.Error() 25 | } else { 26 | result["data"] = config.GetConfig().Profiles 27 | } 28 | 29 | return lib.FormatCommandData(cmd, result) 30 | }, 31 | } 32 | 33 | mainCmd.AddCommand(command) 34 | } 35 | -------------------------------------------------------------------------------- /cmd/configure/profile/remove.go: -------------------------------------------------------------------------------- 1 | package profile 2 | 3 | import ( 4 | "errors" 5 | 6 | "bunnyshell.com/cli/pkg/config" 7 | "bunnyshell.com/cli/pkg/lib" 8 | "bunnyshell.com/cli/pkg/util" 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | func init() { 13 | profileName := "" 14 | 15 | command := &cobra.Command{ 16 | Use: "remove", 17 | 18 | ValidArgsFunction: cobra.NoFileCompletions, 19 | 20 | PersistentPreRunE: util.PersistentPreRunChain, 21 | PreRunE: func(cmd *cobra.Command, args []string) error { 22 | if errors.Is(config.MainManager.Error, config.ErrConfigLoad) { 23 | return config.MainManager.Error 24 | } 25 | 26 | return nil 27 | }, 28 | 29 | RunE: func(cmd *cobra.Command, args []string) error { 30 | if err := removeProfileByName(profileName); err != nil { 31 | return lib.FormatCommandError(cmd, err) 32 | } 33 | 34 | if err := config.MainManager.Save(); err != nil { 35 | return lib.FormatCommandError(cmd, err) 36 | } 37 | 38 | return lib.FormatCommandData(cmd, map[string]interface{}{ 39 | "message": "Profile removed", 40 | "data": profileName, 41 | }) 42 | }, 43 | } 44 | 45 | flags := command.Flags() 46 | 47 | flags.StringVar(&profileName, "profile", profileName, "Profile name to remove") 48 | util.MarkFlagRequiredWithHelp(flags.Lookup("profile"), "The local profile name to remove from available profiles") 49 | 50 | mainCmd.AddCommand(command) 51 | } 52 | 53 | func removeProfileByName(name string) error { 54 | return config.MainManager.RemoveProfile(name) 55 | } 56 | -------------------------------------------------------------------------------- /cmd/configure/profile/root.go: -------------------------------------------------------------------------------- 1 | package profile 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/util" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var mainCmd = &cobra.Command{ 9 | Use: "profiles", 10 | 11 | Short: "Manage profiles", 12 | Long: "Manage CLI Configuration profiles", 13 | 14 | PersistentPreRunE: util.PersistentPreRunChain, 15 | } 16 | 17 | func GetMainCommand() *cobra.Command { 18 | return mainCmd 19 | } 20 | -------------------------------------------------------------------------------- /cmd/configure/root.go: -------------------------------------------------------------------------------- 1 | package configure 2 | 3 | import ( 4 | "bunnyshell.com/cli/cmd/configure/profile" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var mainCmd = &cobra.Command{ 9 | Use: "configure", 10 | 11 | Short: "Configure CLI settings", 12 | } 13 | 14 | func init() { 15 | mainCmd.AddCommand(profile.GetMainCommand()) 16 | } 17 | 18 | func GetMainCommand() *cobra.Command { 19 | return mainCmd 20 | } 21 | -------------------------------------------------------------------------------- /cmd/configure/show.go: -------------------------------------------------------------------------------- 1 | package configure 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/config" 5 | "bunnyshell.com/cli/pkg/lib" 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | func init() { 10 | showConfigCommand := &cobra.Command{ 11 | Use: "show", 12 | 13 | Short: "Show current config", 14 | Long: "Show currently used CLI config", 15 | 16 | ValidArgsFunction: cobra.NoFileCompletions, 17 | 18 | RunE: func(cmd *cobra.Command, args []string) error { 19 | if config.MainManager.Error != nil { 20 | return lib.FormatCommandError(cmd, config.MainManager.Error) 21 | } 22 | 23 | return lib.FormatCommandData(cmd, map[string]interface{}{ 24 | "file": config.GetSettings().ConfigFile, 25 | "data": config.GetConfig(), 26 | }) 27 | }, 28 | } 29 | 30 | mainCmd.AddCommand(showConfigCommand) 31 | } 32 | -------------------------------------------------------------------------------- /cmd/environment/action/clone.go: -------------------------------------------------------------------------------- 1 | package action 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/environment" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func init() { 11 | options := config.GetOptions() 12 | settings := config.GetSettings() 13 | 14 | cloneOptions := environment.NewCloneOptions("") 15 | 16 | command := &cobra.Command{ 17 | Use: "clone", 18 | 19 | ValidArgsFunction: cobra.NoFileCompletions, 20 | 21 | RunE: func(cmd *cobra.Command, args []string) error { 22 | cloneOptions.ID = settings.Profile.Context.Environment 23 | 24 | model, err := environment.Clone(cloneOptions) 25 | if err != nil { 26 | return lib.FormatCommandError(cmd, err) 27 | } 28 | 29 | printLogs := settings.IsStylish() 30 | 31 | if printLogs { 32 | cmd.Printf("\nEnvironment %s successfully cloned:\n\n", cloneOptions.ID) 33 | } 34 | 35 | return lib.FormatCommandData(cmd, model) 36 | }, 37 | } 38 | 39 | flags := command.Flags() 40 | 41 | flags.AddFlag(options.Environment.GetRequiredFlag("id")) 42 | 43 | cloneOptions.UpdateFlagSet(flags) 44 | 45 | mainCmd.AddCommand(command) 46 | } 47 | -------------------------------------------------------------------------------- /cmd/environment/action/definition.go: -------------------------------------------------------------------------------- 1 | package action 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/environment" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func init() { 11 | options := config.GetOptions() 12 | settings := config.GetSettings() 13 | 14 | definitionOptions := environment.NewDefinitionOptions("") 15 | 16 | command := &cobra.Command{ 17 | Use: "definition", 18 | Aliases: []string{"def"}, 19 | 20 | ValidArgsFunction: cobra.NoFileCompletions, 21 | 22 | RunE: func(cmd *cobra.Command, args []string) error { 23 | definitionOptions.ID = settings.Profile.Context.Environment 24 | 25 | definition, err := environment.Definition(definitionOptions) 26 | if err != nil { 27 | return lib.FormatCommandError(cmd, err) 28 | } 29 | 30 | if settings.OutputFormat == "json" { 31 | return lib.FormatCommandData(cmd, definition.Data) 32 | } 33 | 34 | cmd.Println(string(definition.Bytes)) 35 | 36 | return nil 37 | }, 38 | } 39 | 40 | flags := command.Flags() 41 | 42 | flags.AddFlag(options.Environment.GetRequiredFlag("id")) 43 | 44 | mainCmd.AddCommand(command) 45 | } 46 | -------------------------------------------------------------------------------- /cmd/environment/action/delete.go: -------------------------------------------------------------------------------- 1 | package action 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/environment" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func init() { 11 | options := config.GetOptions() 12 | settings := config.GetSettings() 13 | 14 | deleteOptions := environment.NewDeleteOptions("") 15 | 16 | command := &cobra.Command{ 17 | Use: "delete", 18 | 19 | ValidArgsFunction: cobra.NoFileCompletions, 20 | 21 | PreRunE: func(cmd *cobra.Command, args []string) error { 22 | return validateActionOptions(&deleteOptions.ActionOptions) 23 | }, 24 | 25 | RunE: func(cmd *cobra.Command, args []string) error { 26 | deleteOptions.ID = settings.Profile.Context.Environment 27 | 28 | event, err := environment.Delete(deleteOptions) 29 | if err != nil { 30 | return lib.FormatCommandError(cmd, err) 31 | } 32 | 33 | if deleteOptions.WithoutPipeline { 34 | return lib.FormatCommandData(cmd, event) 35 | } 36 | 37 | if err = processEventPipeline(cmd, event, "delete", settings.IsStylish()); err != nil { 38 | cmd.Printf("\nEnvironment %s deletion failed\n", deleteOptions.ID) 39 | 40 | return err 41 | } 42 | 43 | cmd.Printf("\nEnvironment %s successfully deleted\n", deleteOptions.ID) 44 | 45 | return nil 46 | }, 47 | } 48 | 49 | flags := command.Flags() 50 | 51 | flags.AddFlag(options.Environment.GetRequiredFlag("id")) 52 | 53 | deleteOptions.UpdateFlagSet(flags) 54 | 55 | mainCmd.AddCommand(command) 56 | } 57 | -------------------------------------------------------------------------------- /cmd/environment/action/deploy.go: -------------------------------------------------------------------------------- 1 | package action 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/environment" 5 | "bunnyshell.com/cli/pkg/config" 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | type DeployData struct { 10 | K8SIntegration string 11 | } 12 | 13 | func init() { 14 | options := config.GetOptions() 15 | settings := config.GetSettings() 16 | 17 | deployOptions := environment.NewDeployOptions("") 18 | deployData := DeployData{} 19 | 20 | command := &cobra.Command{ 21 | Use: "deploy", 22 | 23 | ValidArgsFunction: cobra.NoFileCompletions, 24 | 25 | PreRunE: func(cmd *cobra.Command, args []string) error { 26 | return validateActionOptions(&deployOptions.ActionOptions) 27 | }, 28 | 29 | RunE: func(cmd *cobra.Command, args []string) error { 30 | deployOptions.ID = settings.Profile.Context.Environment 31 | 32 | return HandleDeploy(cmd, deployOptions, "", deployData.K8SIntegration, settings.IsStylish()) 33 | }, 34 | } 35 | 36 | flags := command.Flags() 37 | 38 | flags.AddFlag(options.Environment.GetRequiredFlag("id")) 39 | 40 | deployOptions.UpdateFlagSet(flags) 41 | 42 | flags.StringVar(&deployData.K8SIntegration, "k8s", deployData.K8SIntegration, "Use a Kubernetes integration for the deployment (if not set)") 43 | 44 | mainCmd.AddCommand(command) 45 | } 46 | -------------------------------------------------------------------------------- /cmd/environment/action/endpoints.go: -------------------------------------------------------------------------------- 1 | package action 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/config" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | func init() { 9 | options := config.GetOptions() 10 | settings := config.GetSettings() 11 | 12 | command := &cobra.Command{ 13 | Use: "endpoints", 14 | Aliases: []string{"end"}, 15 | 16 | ValidArgsFunction: cobra.NoFileCompletions, 17 | 18 | RunE: func(cmd *cobra.Command, args []string) error { 19 | return showEnvironmentEndpoints(cmd, settings.Profile.Context.Environment) 20 | }, 21 | } 22 | 23 | flags := command.Flags() 24 | 25 | flags.AddFlag(options.Environment.GetRequiredFlag("id")) 26 | 27 | mainCmd.AddCommand(command) 28 | } 29 | -------------------------------------------------------------------------------- /cmd/environment/action/start.go: -------------------------------------------------------------------------------- 1 | package action 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/environment" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func init() { 11 | options := config.GetOptions() 12 | settings := config.GetSettings() 13 | 14 | startOptions := environment.NewStartOptions("") 15 | 16 | command := &cobra.Command{ 17 | Use: "start", 18 | 19 | ValidArgsFunction: cobra.NoFileCompletions, 20 | 21 | PreRunE: func(cmd *cobra.Command, args []string) error { 22 | return validateActionOptions(&startOptions.ActionOptions) 23 | }, 24 | 25 | RunE: func(cmd *cobra.Command, args []string) error { 26 | startOptions.ID = settings.Profile.Context.Environment 27 | 28 | event, err := environment.Start(startOptions) 29 | if err != nil { 30 | return lib.FormatCommandError(cmd, err) 31 | } 32 | 33 | if startOptions.WithoutPipeline { 34 | return lib.FormatCommandData(cmd, event) 35 | } 36 | 37 | printLogs := settings.IsStylish() 38 | 39 | if err = processEventPipeline(cmd, event, "start", printLogs); err != nil { 40 | if printLogs { 41 | cmd.Printf("\nEnvironment %s starting failed\n", startOptions.ID) 42 | } 43 | 44 | return err 45 | } 46 | 47 | if printLogs { 48 | cmd.Printf("\nEnvironment %s successfully started\n", startOptions.ID) 49 | } 50 | 51 | return showEnvironmentEndpoints(cmd, startOptions.ID) 52 | }, 53 | } 54 | 55 | flags := command.Flags() 56 | 57 | flags.AddFlag(options.Environment.GetRequiredFlag("id")) 58 | 59 | startOptions.UpdateFlagSet(flags) 60 | 61 | mainCmd.AddCommand(command) 62 | } 63 | -------------------------------------------------------------------------------- /cmd/environment/action/stop.go: -------------------------------------------------------------------------------- 1 | package action 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/environment" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func init() { 11 | options := config.GetOptions() 12 | settings := config.GetSettings() 13 | 14 | stopOptions := environment.NewStopOptions("") 15 | 16 | command := &cobra.Command{ 17 | Use: "stop", 18 | 19 | ValidArgsFunction: cobra.NoFileCompletions, 20 | 21 | PreRunE: func(cmd *cobra.Command, args []string) error { 22 | return validateActionOptions(&stopOptions.ActionOptions) 23 | }, 24 | 25 | RunE: func(cmd *cobra.Command, args []string) error { 26 | stopOptions.ID = settings.Profile.Context.Environment 27 | 28 | event, err := environment.Stop(stopOptions) 29 | if err != nil { 30 | return lib.FormatCommandError(cmd, err) 31 | } 32 | 33 | if stopOptions.WithoutPipeline { 34 | return lib.FormatCommandData(cmd, event) 35 | } 36 | 37 | printLogs := settings.IsStylish() 38 | 39 | if err = processEventPipeline(cmd, event, "stop", printLogs); err != nil { 40 | if printLogs { 41 | cmd.Printf("\nEnvironment %s stopping failed\n", stopOptions.ID) 42 | } 43 | 44 | return err 45 | } 46 | 47 | if printLogs { 48 | cmd.Printf("\nEnvironment %s successfully stopped\n", stopOptions.ID) 49 | } 50 | 51 | return nil 52 | }, 53 | } 54 | 55 | flags := command.Flags() 56 | 57 | flags.AddFlag(options.Environment.GetRequiredFlag("id")) 58 | 59 | stopOptions.UpdateFlagSet(flags) 60 | 61 | mainCmd.AddCommand(command) 62 | } 63 | -------------------------------------------------------------------------------- /cmd/environment/action/update.settings.go: -------------------------------------------------------------------------------- 1 | package action 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/environment" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "bunnyshell.com/cli/pkg/util" 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | func init() { 12 | options := config.GetOptions() 13 | settings := config.GetSettings() 14 | 15 | editSettingsOptions := environment.NewEditSettingsOptions("") 16 | 17 | command := &cobra.Command{ 18 | Use: "update-settings", 19 | 20 | ValidArgsFunction: cobra.NoFileCompletions, 21 | 22 | RunE: func(cmd *cobra.Command, args []string) error { 23 | editSettingsOptions.ID = settings.Profile.Context.Environment 24 | 25 | environmentModel, err := environment.Get(&editSettingsOptions.ItemOptions) 26 | if err != nil { 27 | return lib.FormatCommandError(cmd, err) 28 | } 29 | editSettingsOptions.UpdateEditSettingsForType(environmentModel.GetType()) 30 | 31 | model, err := environment.EditSettings(editSettingsOptions) 32 | if err != nil { 33 | return lib.FormatCommandError(cmd, err) 34 | } 35 | 36 | return lib.FormatCommandData(cmd, model) 37 | }, 38 | } 39 | 40 | flags := command.Flags() 41 | 42 | flags.AddFlag(options.Environment.GetFlag("id", util.FlagRequired)) 43 | 44 | editSettingsOptions.UpdateCommandFlags(command) 45 | 46 | mainCmd.AddCommand(command) 47 | } 48 | -------------------------------------------------------------------------------- /cmd/environment/list.go: -------------------------------------------------------------------------------- 1 | package environment 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/environment" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "bunnyshell.com/cli/pkg/util" 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | func init() { 12 | options := config.GetOptions() 13 | settings := config.GetSettings() 14 | 15 | listOptions := environment.NewListOptions() 16 | 17 | command := &cobra.Command{ 18 | Use: "list", 19 | GroupID: mainGroup.ID, 20 | 21 | ValidArgsFunction: cobra.NoFileCompletions, 22 | 23 | PersistentPreRunE: util.PersistentPreRunChain, 24 | 25 | RunE: func(cmd *cobra.Command, args []string) error { 26 | listOptions.Organization = settings.Profile.Context.Organization 27 | listOptions.Project = settings.Profile.Context.Project 28 | 29 | return lib.ShowCollection(cmd, listOptions, func() (lib.ModelWithPagination, error) { 30 | return environment.List(listOptions) 31 | }) 32 | }, 33 | } 34 | 35 | flags := command.Flags() 36 | 37 | flags.AddFlag(options.Organization.GetFlag("organization")) 38 | flags.AddFlag(options.Project.GetFlag("project")) 39 | 40 | listOptions.UpdateFlagSet(flags) 41 | 42 | mainCmd.AddCommand(command) 43 | } 44 | -------------------------------------------------------------------------------- /cmd/environment/root.go: -------------------------------------------------------------------------------- 1 | package environment 2 | 3 | import ( 4 | "bunnyshell.com/cli/cmd/environment/action" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/util" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | var mainGroup = &cobra.Group{ 11 | ID: "environments", 12 | Title: "Commands for Environment:", 13 | } 14 | 15 | var mainCmd = &cobra.Command{ 16 | Use: "environments", 17 | Aliases: []string{"env"}, 18 | 19 | Short: "Environments", 20 | Long: "Bunnyshell Environments", 21 | 22 | ValidArgsFunction: cobra.NoFileCompletions, 23 | } 24 | 25 | func init() { 26 | config.MainManager.CommandWithAPI(mainCmd) 27 | 28 | mainCmd.AddGroup(mainGroup) 29 | 30 | util.AddGroupedCommands( 31 | mainCmd, 32 | cobra.Group{ 33 | ID: "actions", 34 | Title: "Commands for Environment Actions:", 35 | }, 36 | action.GetMainCommand().Commands(), 37 | ) 38 | } 39 | 40 | func GetMainCommand() *cobra.Command { 41 | return mainCmd 42 | } 43 | -------------------------------------------------------------------------------- /cmd/environment/show.go: -------------------------------------------------------------------------------- 1 | package environment 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/environment" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func init() { 11 | options := config.GetOptions() 12 | settings := config.GetSettings() 13 | 14 | command := &cobra.Command{ 15 | Use: "show", 16 | GroupID: mainGroup.ID, 17 | 18 | RunE: func(cmd *cobra.Command, args []string) error { 19 | itemOptions := environment.NewItemOptions(settings.Profile.Context.Environment) 20 | 21 | model, err := environment.Get(itemOptions) 22 | if err != nil { 23 | return lib.FormatCommandError(cmd, err) 24 | } 25 | 26 | return lib.FormatCommandData(cmd, model) 27 | }, 28 | } 29 | 30 | flags := command.Flags() 31 | 32 | flags.AddFlag(options.Environment.GetRequiredFlag("id")) 33 | 34 | mainCmd.AddCommand(command) 35 | } 36 | -------------------------------------------------------------------------------- /cmd/event/list.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/event" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func init() { 11 | options := config.GetOptions() 12 | settings := config.GetSettings() 13 | 14 | listOptions := event.NewListOptions() 15 | 16 | command := &cobra.Command{ 17 | Use: "list", 18 | 19 | ValidArgsFunction: cobra.NoFileCompletions, 20 | 21 | RunE: func(cmd *cobra.Command, args []string) error { 22 | listOptions.Organization = settings.Profile.Context.Organization 23 | listOptions.Environment = settings.Profile.Context.Environment 24 | 25 | return lib.ShowCollection(cmd, listOptions, func() (lib.ModelWithPagination, error) { 26 | return event.List(listOptions) 27 | }) 28 | }, 29 | } 30 | 31 | flags := command.Flags() 32 | 33 | flags.AddFlag(options.Organization.GetFlag("organization")) 34 | flags.AddFlag(options.Environment.GetFlag("environment")) 35 | 36 | listOptions.UpdateFlagSet(flags) 37 | 38 | mainCmd.AddCommand(command) 39 | } 40 | -------------------------------------------------------------------------------- /cmd/event/root.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/config" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var mainCmd = &cobra.Command{ 9 | Use: "events", 10 | 11 | Short: "Events", 12 | Long: "Bunnyshell Events", 13 | } 14 | 15 | func init() { 16 | config.MainManager.CommandWithAPI(mainCmd) 17 | } 18 | 19 | func GetMainCommand() *cobra.Command { 20 | return mainCmd 21 | } 22 | -------------------------------------------------------------------------------- /cmd/git/info.go: -------------------------------------------------------------------------------- 1 | package git 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/component/git" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func init() { 11 | options := config.GetOptions() 12 | settings := config.GetSettings() 13 | 14 | listOptions := git.NewListOptions() 15 | 16 | command := &cobra.Command{ 17 | Use: "info", 18 | 19 | ValidArgsFunction: cobra.NoFileCompletions, 20 | 21 | RunE: func(cmd *cobra.Command, args []string) error { 22 | listOptions.Organization = settings.Profile.Context.Organization 23 | listOptions.Project = settings.Profile.Context.Project 24 | listOptions.Environment = settings.Profile.Context.Environment 25 | 26 | return lib.ShowCollection(cmd, listOptions, func() (lib.ModelWithPagination, error) { 27 | return git.List(listOptions) 28 | }) 29 | }, 30 | } 31 | 32 | flags := command.Flags() 33 | 34 | flags.AddFlag(options.Organization.GetFlag("organization")) 35 | flags.AddFlag(options.Environment.GetFlag("environment")) 36 | flags.AddFlag(options.Project.GetFlag("project")) 37 | 38 | listOptions.UpdateFlagSet(flags) 39 | 40 | mainCmd.AddCommand(command) 41 | } 42 | -------------------------------------------------------------------------------- /cmd/git/prepare.go: -------------------------------------------------------------------------------- 1 | package git 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/component/git" 5 | "bunnyshell.com/cli/pkg/config" 6 | gitHelper "bunnyshell.com/cli/pkg/helper/git" 7 | "bunnyshell.com/cli/pkg/lib" 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | func init() { 12 | options := config.GetOptions() 13 | settings := config.GetSettings() 14 | 15 | aggOptions := git.NewAggregateOptions() 16 | prepareOptions := gitHelper.NewPrepareOptions() 17 | 18 | command := &cobra.Command{ 19 | Use: "prepare", 20 | 21 | ValidArgsFunction: cobra.NoFileCompletions, 22 | 23 | RunE: func(cmd *cobra.Command, args []string) error { 24 | aggOptions.Organization = settings.Profile.Context.Organization 25 | aggOptions.Project = settings.Profile.Context.Project 26 | aggOptions.Environment = settings.Profile.Context.Environment 27 | 28 | components, err := git.Aggregate(aggOptions) 29 | if err != nil { 30 | return lib.FormatCommandError(cmd, err) 31 | } 32 | 33 | if err = gitHelper.PrintPrepareInfo(components, prepareOptions); err != nil { 34 | return lib.FormatCommandError(cmd, err) 35 | } 36 | 37 | return nil 38 | }, 39 | } 40 | 41 | flags := command.Flags() 42 | 43 | flags.AddFlag(options.Organization.GetFlag("organization")) 44 | flags.AddFlag(options.Project.GetFlag("project")) 45 | flags.AddFlag(options.Environment.GetFlag("environment")) 46 | 47 | aggOptions.UpdateFlagSet(flags) 48 | prepareOptions.UpdateFlagSet(flags) 49 | 50 | mainCmd.AddCommand(command) 51 | } 52 | -------------------------------------------------------------------------------- /cmd/git/root.go: -------------------------------------------------------------------------------- 1 | package git 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/config" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var mainCmd = &cobra.Command{ 9 | Use: "git", 10 | 11 | Short: "Git Operations", 12 | Long: "Bunnyshell Git Operations", 13 | } 14 | 15 | func init() { 16 | config.MainManager.CommandWithAPI(mainCmd) 17 | } 18 | 19 | func GetMainCommand() *cobra.Command { 20 | return mainCmd 21 | } 22 | -------------------------------------------------------------------------------- /cmd/k8sIntegration/list.go: -------------------------------------------------------------------------------- 1 | package k8sIntegration 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/k8s" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func init() { 11 | options := config.GetOptions() 12 | settings := config.GetSettings() 13 | 14 | listOptions := k8s.NewListOptions() 15 | 16 | command := &cobra.Command{ 17 | Use: "list", 18 | GroupID: mainGroup.ID, 19 | 20 | ValidArgsFunction: cobra.NoFileCompletions, 21 | 22 | RunE: func(cmd *cobra.Command, args []string) error { 23 | listOptions.Environment = settings.Profile.Context.Environment 24 | listOptions.Organization = settings.Profile.Context.Organization 25 | 26 | return lib.ShowCollection(cmd, listOptions, func() (lib.ModelWithPagination, error) { 27 | return k8s.List(listOptions) 28 | }) 29 | }, 30 | } 31 | 32 | flags := command.Flags() 33 | 34 | flags.AddFlag(options.Organization.GetFlag("organization")) 35 | flags.AddFlag(options.Environment.GetFlag("environment")) 36 | 37 | listOptions.UpdateFlagSet(flags) 38 | 39 | mainCmd.AddCommand(command) 40 | } 41 | -------------------------------------------------------------------------------- /cmd/k8sIntegration/root.go: -------------------------------------------------------------------------------- 1 | package k8sIntegration 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/config" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var mainCmd = &cobra.Command{ 9 | Use: "k8s-clusters", 10 | Aliases: []string{"k8s"}, 11 | 12 | Short: "Kubernetes Cluster Integrations", 13 | Long: "Bunnyshell Kubernetes Cluster Integrations", 14 | } 15 | 16 | var mainGroup = cobra.Group{ 17 | ID: "k8s-clusters", 18 | Title: "Commands for Kubernetes Integrations:", 19 | } 20 | 21 | func init() { 22 | config.MainManager.CommandWithAPI(mainCmd) 23 | 24 | mainCmd.AddGroup(&mainGroup) 25 | } 26 | 27 | func GetMainCommand() *cobra.Command { 28 | return mainCmd 29 | } 30 | -------------------------------------------------------------------------------- /cmd/k8sIntegration/show.go: -------------------------------------------------------------------------------- 1 | package k8sIntegration 2 | 3 | import ( 4 | "fmt" 5 | 6 | "bunnyshell.com/cli/pkg/api/k8s" 7 | "bunnyshell.com/cli/pkg/build" 8 | "bunnyshell.com/cli/pkg/config/option" 9 | "bunnyshell.com/cli/pkg/lib" 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | func init() { 14 | itemOptions := k8s.NewItemOptions("") 15 | 16 | command := &cobra.Command{ 17 | Use: "show", 18 | GroupID: mainGroup.ID, 19 | 20 | ValidArgsFunction: cobra.NoFileCompletions, 21 | 22 | RunE: func(cmd *cobra.Command, args []string) error { 23 | model, err := k8s.Get(itemOptions) 24 | if err != nil { 25 | return lib.FormatCommandError(cmd, err) 26 | } 27 | 28 | return lib.FormatCommandData(cmd, model) 29 | }, 30 | } 31 | 32 | flags := command.Flags() 33 | 34 | flags.AddFlag(getIDOption(&itemOptions.ID).GetRequiredFlag("id")) 35 | 36 | mainCmd.AddCommand(command) 37 | } 38 | 39 | func getIDOption(value *string) *option.String { 40 | help := fmt.Sprintf( 41 | `Find available Kubernetes Integrations with "%s k8s-clusters list"`, 42 | build.Name, 43 | ) 44 | 45 | idOption := option.NewStringOption(value) 46 | 47 | idOption.AddFlagWithExtraHelp("id", "Kubernetes Integrations Id", help) 48 | 49 | return idOption 50 | } 51 | -------------------------------------------------------------------------------- /cmd/organization/list.go: -------------------------------------------------------------------------------- 1 | package organization 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/organization" 5 | "bunnyshell.com/cli/pkg/lib" 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | func init() { 10 | listOptions := organization.NewListOptions() 11 | 12 | command := &cobra.Command{ 13 | Use: "list", 14 | 15 | ValidArgsFunction: cobra.NoFileCompletions, 16 | 17 | RunE: func(cmd *cobra.Command, args []string) error { 18 | return lib.ShowCollection(cmd, listOptions, func() (lib.ModelWithPagination, error) { 19 | return organization.List(listOptions) 20 | }) 21 | }, 22 | } 23 | 24 | flags := command.Flags() 25 | 26 | listOptions.UpdateFlagSet(flags) 27 | 28 | mainCmd.AddCommand(command) 29 | } 30 | -------------------------------------------------------------------------------- /cmd/organization/root.go: -------------------------------------------------------------------------------- 1 | package organization 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/config" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var mainCmd = &cobra.Command{ 9 | Use: "organizations", 10 | Aliases: []string{"org"}, 11 | 12 | Short: "Organizations", 13 | Long: "Bunnyshell Organizations", 14 | } 15 | 16 | func init() { 17 | config.MainManager.CommandWithAPI(mainCmd) 18 | } 19 | 20 | func GetMainCommand() *cobra.Command { 21 | return mainCmd 22 | } 23 | -------------------------------------------------------------------------------- /cmd/organization/show.go: -------------------------------------------------------------------------------- 1 | package organization 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/organization" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func init() { 11 | options := config.GetOptions() 12 | settings := config.GetSettings() 13 | 14 | itemOptions := organization.NewItemOptions("") 15 | 16 | command := &cobra.Command{ 17 | Use: "show", 18 | 19 | ValidArgsFunction: cobra.NoFileCompletions, 20 | 21 | RunE: func(cmd *cobra.Command, args []string) error { 22 | itemOptions.ID = settings.Profile.Context.Organization 23 | 24 | model, err := organization.Get(itemOptions) 25 | if err != nil { 26 | return lib.FormatCommandError(cmd, err) 27 | } 28 | 29 | return lib.FormatCommandData(cmd, model) 30 | }, 31 | } 32 | 33 | flags := command.Flags() 34 | 35 | flags.AddFlag(options.Organization.GetRequiredFlag("id")) 36 | 37 | mainCmd.AddCommand(command) 38 | } 39 | -------------------------------------------------------------------------------- /cmd/pipeline/list.go: -------------------------------------------------------------------------------- 1 | package pipeline 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/pipeline" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func init() { 11 | options := config.GetOptions() 12 | settings := config.GetSettings() 13 | 14 | listOptions := pipeline.NewListOptions() 15 | 16 | command := &cobra.Command{ 17 | Use: "list", 18 | 19 | ValidArgsFunction: cobra.NoFileCompletions, 20 | 21 | RunE: func(cmd *cobra.Command, args []string) error { 22 | listOptions.Environment = settings.Profile.Context.Environment 23 | listOptions.Organization = settings.Profile.Context.Organization 24 | 25 | return lib.ShowCollection(cmd, listOptions, func() (lib.ModelWithPagination, error) { 26 | return pipeline.List(listOptions) 27 | }) 28 | }, 29 | } 30 | 31 | flags := command.Flags() 32 | 33 | flags.AddFlag(options.Organization.GetFlag("organization")) 34 | flags.AddFlag(options.Environment.GetFlag("environment")) 35 | 36 | listOptions.UpdateFlagSet(flags) 37 | 38 | mainCmd.AddCommand(command) 39 | } 40 | -------------------------------------------------------------------------------- /cmd/pipeline/monitor.go: -------------------------------------------------------------------------------- 1 | package pipeline 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/lib" 5 | "bunnyshell.com/cli/pkg/progress" 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | func init() { 10 | var pipelineID string 11 | 12 | progressOptions := progress.NewOptions() 13 | 14 | command := &cobra.Command{ 15 | Use: "monitor", 16 | 17 | ValidArgsFunction: cobra.NoFileCompletions, 18 | 19 | PreRunE: lib.OnlyStylish, 20 | 21 | RunE: func(cmd *cobra.Command, args []string) error { 22 | if err := progress.Pipeline(pipelineID, progressOptions); err != nil { 23 | return lib.FormatCommandError(cmd, err) 24 | } 25 | 26 | return nil 27 | }, 28 | } 29 | 30 | flags := command.Flags() 31 | 32 | flags.AddFlag(getIDOption(&pipelineID).GetRequiredFlag("id")) 33 | 34 | flags.DurationVar(&progressOptions.Interval, "interval", progressOptions.Interval, "Pipeline check interval") 35 | 36 | mainCmd.AddCommand(command) 37 | } 38 | -------------------------------------------------------------------------------- /cmd/pipeline/root.go: -------------------------------------------------------------------------------- 1 | package pipeline 2 | 3 | import ( 4 | "fmt" 5 | 6 | "bunnyshell.com/cli/pkg/build" 7 | "bunnyshell.com/cli/pkg/config" 8 | "bunnyshell.com/cli/pkg/config/option" 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | var mainCmd = &cobra.Command{ 13 | Use: "pipeline", 14 | Aliases: []string{"pipe"}, 15 | 16 | Short: "Pipeline", 17 | Long: "Bunnyshell Pipeline", 18 | } 19 | 20 | func init() { 21 | config.MainManager.CommandWithAPI(mainCmd) 22 | } 23 | 24 | func GetMainCommand() *cobra.Command { 25 | return mainCmd 26 | } 27 | 28 | func getIDOption(value *string) *option.String { 29 | help := fmt.Sprintf( 30 | `Find available Pipelines with "%s pipeline list"`, 31 | build.Name, 32 | ) 33 | 34 | option := option.NewStringOption(value) 35 | 36 | option.AddFlagWithExtraHelp("id", "Pipeline ID", help) 37 | 38 | return option 39 | } 40 | -------------------------------------------------------------------------------- /cmd/pipeline/show.go: -------------------------------------------------------------------------------- 1 | package pipeline 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/pipeline" 5 | "bunnyshell.com/cli/pkg/lib" 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | func init() { 10 | itemOptions := pipeline.NewItemOptions("") 11 | 12 | command := &cobra.Command{ 13 | Use: "show", 14 | 15 | ValidArgsFunction: cobra.NoFileCompletions, 16 | 17 | RunE: func(cmd *cobra.Command, args []string) error { 18 | model, err := pipeline.Get(itemOptions) 19 | if err != nil { 20 | return lib.FormatCommandError(cmd, err) 21 | } 22 | 23 | return lib.FormatCommandData(cmd, model) 24 | }, 25 | } 26 | 27 | flags := command.Flags() 28 | 29 | flags.AddFlag(getIDOption(&itemOptions.ID).GetRequiredFlag("id")) 30 | 31 | mainCmd.AddCommand(command) 32 | } 33 | -------------------------------------------------------------------------------- /cmd/project/action/delete.go: -------------------------------------------------------------------------------- 1 | package action 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/project" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func init() { 11 | options := config.GetOptions() 12 | settings := config.GetSettings() 13 | 14 | deleteOptions := project.NewDeleteOptions() 15 | 16 | command := &cobra.Command{ 17 | Use: "delete", 18 | 19 | ValidArgsFunction: cobra.NoFileCompletions, 20 | 21 | RunE: func(cmd *cobra.Command, args []string) error { 22 | deleteOptions.ID = settings.Profile.Context.Project 23 | 24 | err := project.Delete(deleteOptions) 25 | if err != nil { 26 | return lib.FormatCommandError(cmd, err) 27 | } 28 | 29 | cmd.Printf("\nProject %s successfully deleted\n", deleteOptions.ID) 30 | 31 | return nil 32 | }, 33 | } 34 | 35 | flags := command.Flags() 36 | 37 | flags.AddFlag(options.Project.GetRequiredFlag("id")) 38 | 39 | mainCmd.AddCommand(command) 40 | } 41 | -------------------------------------------------------------------------------- /cmd/project/action/root.go: -------------------------------------------------------------------------------- 1 | package action 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | ) 6 | 7 | var mainCmd = &cobra.Command{} 8 | 9 | func GetMainCommand() *cobra.Command { 10 | return mainCmd 11 | } 12 | -------------------------------------------------------------------------------- /cmd/project/action/update.build_settings.go: -------------------------------------------------------------------------------- 1 | package action 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/build_settings" 5 | "bunnyshell.com/cli/pkg/api/project" 6 | "bunnyshell.com/cli/pkg/config" 7 | "bunnyshell.com/cli/pkg/lib" 8 | "bunnyshell.com/cli/pkg/util" 9 | "bunnyshell.com/sdk" 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | func init() { 14 | options := config.GetOptions() 15 | settings := config.GetSettings() 16 | 17 | editBuildSettingsOptions := project.NewEditBuildSettingsOptions("") 18 | 19 | command := &cobra.Command{ 20 | Use: "update-build-settings", 21 | 22 | ValidArgsFunction: cobra.NoFileCompletions, 23 | 24 | RunE: func(cmd *cobra.Command, args []string) error { 25 | editBuildSettingsOptions.ID = settings.Profile.Context.Project 26 | 27 | _, err := project.EditBuildSettings(editBuildSettingsOptions) 28 | if err != nil { 29 | return lib.FormatCommandError(cmd, err) 30 | } 31 | 32 | model, err := build_settings.CheckBuildSettingsValidation[sdk.ProjectItem]( 33 | project.Get, 34 | &editBuildSettingsOptions.EditOptions, 35 | settings.IsStylish(), 36 | ) 37 | if err != nil { 38 | return lib.FormatCommandError(cmd, err) 39 | } 40 | 41 | return lib.FormatCommandData(cmd, model) 42 | }, 43 | } 44 | 45 | flags := command.Flags() 46 | 47 | flags.AddFlag(options.Project.GetFlag("id", util.FlagRequired)) 48 | 49 | editBuildSettingsOptions.UpdateFlagSet(flags) 50 | 51 | mainCmd.AddCommand(command) 52 | } 53 | -------------------------------------------------------------------------------- /cmd/project/action/update.settings.go: -------------------------------------------------------------------------------- 1 | package action 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/project" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "bunnyshell.com/cli/pkg/util" 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | func init() { 12 | options := config.GetOptions() 13 | settings := config.GetSettings() 14 | 15 | editSettingsOptions := project.NewEditSettingsOptions("") 16 | 17 | command := &cobra.Command{ 18 | Use: "update-settings", 19 | 20 | ValidArgsFunction: cobra.NoFileCompletions, 21 | 22 | RunE: func(cmd *cobra.Command, args []string) error { 23 | editSettingsOptions.ID = settings.Profile.Context.Project 24 | 25 | model, err := project.EditSettings(editSettingsOptions) 26 | if err != nil { 27 | return lib.FormatCommandError(cmd, err) 28 | } 29 | 30 | return lib.FormatCommandData(cmd, model) 31 | }, 32 | } 33 | 34 | flags := command.Flags() 35 | 36 | flags.AddFlag(options.Project.GetFlag("id", util.FlagRequired)) 37 | 38 | editSettingsOptions.UpdateFlagSet(flags) 39 | 40 | mainCmd.AddCommand(command) 41 | } 42 | -------------------------------------------------------------------------------- /cmd/project/list.go: -------------------------------------------------------------------------------- 1 | package project 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/project" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func init() { 11 | options := config.GetOptions() 12 | settings := config.GetSettings() 13 | 14 | listOptions := project.NewListOptions() 15 | 16 | command := &cobra.Command{ 17 | Use: "list", 18 | 19 | ValidArgsFunction: cobra.NoFileCompletions, 20 | 21 | RunE: func(cmd *cobra.Command, args []string) error { 22 | listOptions.Organization = settings.Profile.Context.Organization 23 | 24 | return lib.ShowCollection(cmd, listOptions, func() (lib.ModelWithPagination, error) { 25 | return project.List(listOptions) 26 | }) 27 | }, 28 | } 29 | 30 | flags := command.Flags() 31 | 32 | flags.AddFlag(options.Organization.GetFlag("organization")) 33 | 34 | listOptions.UpdateFlagSet(flags) 35 | 36 | mainCmd.AddCommand(command) 37 | } 38 | -------------------------------------------------------------------------------- /cmd/project/root.go: -------------------------------------------------------------------------------- 1 | package project 2 | 3 | import ( 4 | "bunnyshell.com/cli/cmd/project/action" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/util" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | var mainCmd = &cobra.Command{ 11 | Use: "projects", 12 | Aliases: []string{"proj"}, 13 | 14 | Short: "Projects", 15 | Long: "Bunnyshell Projects", 16 | 17 | ValidArgsFunction: cobra.NoFileCompletions, 18 | } 19 | 20 | func init() { 21 | config.MainManager.CommandWithAPI(mainCmd) 22 | 23 | util.AddGroupedCommands( 24 | mainCmd, 25 | cobra.Group{ 26 | ID: "actions", 27 | Title: "Commands for Projects:", 28 | }, 29 | action.GetMainCommand().Commands(), 30 | ) 31 | } 32 | 33 | func GetMainCommand() *cobra.Command { 34 | return mainCmd 35 | } 36 | -------------------------------------------------------------------------------- /cmd/project/show.go: -------------------------------------------------------------------------------- 1 | package project 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/project" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func init() { 11 | options := config.GetOptions() 12 | settings := config.GetSettings() 13 | 14 | itemOptions := project.NewItemOptions("") 15 | 16 | command := &cobra.Command{ 17 | Use: "show", 18 | 19 | ValidArgsFunction: cobra.NoFileCompletions, 20 | 21 | RunE: func(cmd *cobra.Command, args []string) error { 22 | itemOptions.ID = settings.Profile.Context.Project 23 | 24 | model, err := project.Get(itemOptions) 25 | if err != nil { 26 | return lib.FormatCommandError(cmd, err) 27 | } 28 | 29 | return lib.FormatCommandData(cmd, model) 30 | }, 31 | } 32 | 33 | flags := command.Flags() 34 | 35 | flags.AddFlag(options.Project.GetRequiredFlag("id")) 36 | 37 | mainCmd.AddCommand(command) 38 | } 39 | -------------------------------------------------------------------------------- /cmd/project_variable/action/delete.go: -------------------------------------------------------------------------------- 1 | package action 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/project_variable" 5 | "bunnyshell.com/cli/pkg/lib" 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | func init() { 10 | deleteOptions := project_variable.NewDeleteOptions() 11 | 12 | command := &cobra.Command{ 13 | Use: "delete", 14 | 15 | ValidArgsFunction: cobra.NoFileCompletions, 16 | 17 | RunE: func(cmd *cobra.Command, args []string) error { 18 | err := project_variable.Delete(deleteOptions) 19 | if err != nil { 20 | return lib.FormatCommandError(cmd, err) 21 | } 22 | 23 | cmd.Printf("\nProject variable %s successfully deleted\n", deleteOptions.ID) 24 | 25 | return nil 26 | }, 27 | } 28 | 29 | flags := command.Flags() 30 | 31 | flags.AddFlag(GetIDOption(&deleteOptions.ID).GetRequiredFlag("id")) 32 | 33 | mainCmd.AddCommand(command) 34 | } 35 | -------------------------------------------------------------------------------- /cmd/project_variable/action/edit.go: -------------------------------------------------------------------------------- 1 | package action 2 | 3 | import ( 4 | "io" 5 | "os" 6 | 7 | "bunnyshell.com/cli/pkg/api/project_variable" 8 | "bunnyshell.com/cli/pkg/lib" 9 | "bunnyshell.com/cli/pkg/util" 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | func init() { 14 | editOptions := project_variable.NewEditOptions("") 15 | 16 | command := &cobra.Command{ 17 | Use: "edit", 18 | 19 | ValidArgsFunction: cobra.NoFileCompletions, 20 | 21 | PreRunE: func(cmd *cobra.Command, args []string) error { 22 | hasStdin, err := util.IsStdinPresent() 23 | if err != nil { 24 | return err 25 | } 26 | 27 | flags := cmd.Flags() 28 | if flags.Changed("value") && hasStdin { 29 | return errMultipleValueInputs 30 | } 31 | 32 | return nil 33 | }, 34 | 35 | RunE: func(cmd *cobra.Command, args []string) error { 36 | flags := cmd.Flags() 37 | if flags.Changed("value") { 38 | editOptions.ProjectVariableEditAction.SetValue(flags.Lookup("value").Value.String()) 39 | } 40 | 41 | hasStdin, err := util.IsStdinPresent() 42 | if err != nil { 43 | return err 44 | } 45 | 46 | if hasStdin { 47 | buf, err := io.ReadAll(os.Stdin) 48 | if err != nil { 49 | return err 50 | } 51 | 52 | editOptions.ProjectVariableEditAction.SetValue(string(buf)) 53 | } 54 | 55 | model, err := project_variable.Edit(editOptions) 56 | if err != nil { 57 | return lib.FormatCommandError(cmd, err) 58 | } 59 | 60 | return lib.FormatCommandData(cmd, model) 61 | }, 62 | } 63 | 64 | flags := command.Flags() 65 | 66 | flags.AddFlag(GetIDOption(&editOptions.ID).GetRequiredFlag("id")) 67 | 68 | editOptions.UpdateFlagSet(flags) 69 | 70 | mainCmd.AddCommand(command) 71 | } 72 | -------------------------------------------------------------------------------- /cmd/project_variable/action/root.go: -------------------------------------------------------------------------------- 1 | package action 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "bunnyshell.com/cli/pkg/build" 8 | "bunnyshell.com/cli/pkg/config/option" 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | var ( 13 | errMissingValue = errors.New("the plain value must be provided") 14 | errMultipleValueInputs = errors.New("the value must be provided either by argument or by stdin, not both") 15 | ) 16 | 17 | var mainCmd = &cobra.Command{} 18 | 19 | func GetMainCommand() *cobra.Command { 20 | return mainCmd 21 | } 22 | 23 | func GetIDOption(value *string) *option.String { 24 | help := fmt.Sprintf( 25 | `Find available project variables with "%s variables list"`, 26 | build.Name, 27 | ) 28 | 29 | idOption := option.NewStringOption(value) 30 | 31 | idOption.AddFlagWithExtraHelp("id", "Project Variable Id", help) 32 | 33 | return idOption 34 | } 35 | -------------------------------------------------------------------------------- /cmd/project_variable/list.go: -------------------------------------------------------------------------------- 1 | package project_variable 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/project_variable" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func init() { 11 | options := config.GetOptions() 12 | settings := config.GetSettings() 13 | 14 | listOptions := project_variable.NewListOptions() 15 | 16 | command := &cobra.Command{ 17 | Use: "list", 18 | 19 | ValidArgsFunction: cobra.NoFileCompletions, 20 | 21 | RunE: func(cmd *cobra.Command, args []string) error { 22 | listOptions.Organization = settings.Profile.Context.Organization 23 | listOptions.Project = settings.Profile.Context.Project 24 | 25 | return lib.ShowCollection(cmd, listOptions, func() (lib.ModelWithPagination, error) { 26 | return project_variable.List(listOptions) 27 | }) 28 | }, 29 | } 30 | 31 | flags := command.Flags() 32 | 33 | flags.AddFlag(options.Organization.GetFlag("organization")) 34 | flags.AddFlag(options.Project.GetFlag("project")) 35 | 36 | listOptions.UpdateFlagSet(flags) 37 | 38 | mainCmd.AddCommand(command) 39 | } 40 | -------------------------------------------------------------------------------- /cmd/project_variable/root.go: -------------------------------------------------------------------------------- 1 | package project_variable 2 | 3 | import ( 4 | "bunnyshell.com/cli/cmd/project_variable/action" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/util" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | var mainCmd = &cobra.Command{ 11 | Use: "project-variables", 12 | Aliases: []string{"pvar"}, 13 | 14 | Short: "Project Variables", 15 | Long: "Bunnyshell Project Variables", 16 | } 17 | 18 | func init() { 19 | config.MainManager.CommandWithAPI(mainCmd) 20 | 21 | util.AddGroupedCommands( 22 | mainCmd, 23 | cobra.Group{ 24 | ID: "actions", 25 | Title: "Commands for Project Variables:", 26 | }, 27 | action.GetMainCommand().Commands(), 28 | ) 29 | } 30 | 31 | func GetMainCommand() *cobra.Command { 32 | return mainCmd 33 | } 34 | -------------------------------------------------------------------------------- /cmd/project_variable/show.go: -------------------------------------------------------------------------------- 1 | package project_variable 2 | 3 | import ( 4 | "bunnyshell.com/cli/cmd/project_variable/action" 5 | "bunnyshell.com/cli/pkg/api/project_variable" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func init() { 11 | itemOptions := project_variable.NewItemOptions("") 12 | 13 | command := &cobra.Command{ 14 | Use: "show", 15 | 16 | ValidArgsFunction: cobra.NoFileCompletions, 17 | 18 | RunE: func(cmd *cobra.Command, args []string) error { 19 | model, err := project_variable.Get(itemOptions) 20 | if err != nil { 21 | return lib.FormatCommandError(cmd, err) 22 | } 23 | 24 | return lib.FormatCommandData(cmd, model) 25 | }, 26 | } 27 | 28 | flags := command.Flags() 29 | 30 | flags.AddFlag(action.GetIDOption(&itemOptions.ID).GetRequiredFlag("id")) 31 | 32 | mainCmd.AddCommand(command) 33 | } 34 | -------------------------------------------------------------------------------- /cmd/registry_integration/list.go: -------------------------------------------------------------------------------- 1 | package registry_integration 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/registry_integration" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func init() { 11 | options := config.GetOptions() 12 | settings := config.GetSettings() 13 | 14 | listOptions := registry_integration.NewListOptions() 15 | 16 | command := &cobra.Command{ 17 | Use: "list", 18 | GroupID: mainGroup.ID, 19 | 20 | ValidArgsFunction: cobra.NoFileCompletions, 21 | 22 | RunE: func(cmd *cobra.Command, args []string) error { 23 | listOptions.Organization = settings.Profile.Context.Organization 24 | 25 | return lib.ShowCollection(cmd, listOptions, func() (lib.ModelWithPagination, error) { 26 | return registry_integration.List(listOptions) 27 | }) 28 | }, 29 | } 30 | 31 | flags := command.Flags() 32 | 33 | flags.AddFlag(options.Organization.GetFlag("organization")) 34 | 35 | listOptions.UpdateFlagSet(flags) 36 | 37 | mainCmd.AddCommand(command) 38 | } 39 | -------------------------------------------------------------------------------- /cmd/registry_integration/root.go: -------------------------------------------------------------------------------- 1 | package registry_integration 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/config" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var mainCmd = &cobra.Command{ 9 | Use: "container-registries", 10 | Aliases: []string{"creg"}, 11 | 12 | Short: "Container Registry Integrations", 13 | Long: "Bunnyshell Container Registry Integrations", 14 | } 15 | 16 | var mainGroup = cobra.Group{ 17 | ID: "container-registries", 18 | Title: "Commands for Container Registries:", 19 | } 20 | 21 | func init() { 22 | config.MainManager.CommandWithAPI(mainCmd) 23 | 24 | mainCmd.AddGroup(&mainGroup) 25 | } 26 | 27 | func GetMainCommand() *cobra.Command { 28 | return mainCmd 29 | } 30 | -------------------------------------------------------------------------------- /cmd/registry_integration/show.go: -------------------------------------------------------------------------------- 1 | package registry_integration 2 | 3 | import ( 4 | "fmt" 5 | 6 | "bunnyshell.com/cli/pkg/api/registry_integration" 7 | "bunnyshell.com/cli/pkg/build" 8 | "bunnyshell.com/cli/pkg/config/option" 9 | "bunnyshell.com/cli/pkg/lib" 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | func init() { 14 | itemOptions := registry_integration.NewItemOptions("") 15 | 16 | command := &cobra.Command{ 17 | Use: "show", 18 | GroupID: mainGroup.ID, 19 | 20 | ValidArgsFunction: cobra.NoFileCompletions, 21 | 22 | RunE: func(cmd *cobra.Command, args []string) error { 23 | model, err := registry_integration.Get(itemOptions) 24 | if err != nil { 25 | return lib.FormatCommandError(cmd, err) 26 | } 27 | 28 | return lib.FormatCommandData(cmd, model) 29 | }, 30 | } 31 | 32 | flags := command.Flags() 33 | 34 | flags.AddFlag(getIDOption(&itemOptions.ID).GetRequiredFlag("id")) 35 | 36 | mainCmd.AddCommand(command) 37 | } 38 | 39 | func getIDOption(value *string) *option.String { 40 | help := fmt.Sprintf( 41 | `Find available Container Registries Integrations with "%s container-registries list"`, 42 | build.Name, 43 | ) 44 | 45 | idOption := option.NewStringOption(value) 46 | 47 | idOption.AddFlagWithExtraHelp("id", "Container Registry Integration Id", help) 48 | 49 | return idOption 50 | } 51 | -------------------------------------------------------------------------------- /cmd/remote_development/config/generate.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/config/option" 5 | "bunnyshell.com/cli/pkg/helper/rdev" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "bunnyshell.com/cli/pkg/util" 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | func init() { 12 | directory := ".bunnyshell" 13 | 14 | command := &cobra.Command{ 15 | Use: "generate", 16 | Aliases: []string{"gen"}, 17 | 18 | ValidArgsFunction: cobra.NoFileCompletions, 19 | 20 | RunE: func(cmd *cobra.Command, args []string) error { 21 | if err := rdev.Generate(directory); err != nil { 22 | return lib.FormatCommandError(cmd, err) 23 | } 24 | 25 | return nil 26 | }, 27 | } 28 | 29 | flags := command.Flags() 30 | 31 | flags.AddFlag(getDirectoryOption(&directory).GetFlag("directory", util.FlagDirname)) 32 | 33 | mainCmd.AddCommand(command) 34 | } 35 | 36 | func getDirectoryOption(value *string) *option.String { 37 | help := "New directory in which to generate the RemoteDev config file" 38 | 39 | option := option.NewStringOption(value) 40 | 41 | option.AddFlagWithExtraHelp("directory", "Directory to generate the config in", help) 42 | 43 | return option 44 | } 45 | -------------------------------------------------------------------------------- /cmd/remote_development/config/root.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/util" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var mainCmd = &cobra.Command{ 9 | Use: "config", 10 | Aliases: []string{"cfg"}, 11 | 12 | Short: "Manage Remote Development config", 13 | Long: "Manage Remote Development config", 14 | 15 | PersistentPreRunE: util.PersistentPreRunChain, 16 | } 17 | 18 | func GetMainCommand() *cobra.Command { 19 | return mainCmd 20 | } 21 | -------------------------------------------------------------------------------- /cmd/remote_development/config/show.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/config" 5 | "bunnyshell.com/cli/pkg/k8s/bridge" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "bunnyshell.com/cli/pkg/remote_development/action/up" 8 | remoteDevConfig "bunnyshell.com/cli/pkg/remote_development/config" 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | func init() { 13 | options := config.GetOptions() 14 | settings := config.GetSettings() 15 | 16 | resourceLoader := bridge.NewResourceLoader() 17 | upOptions := up.NewOptions(remoteDevConfig.NewManager(), resourceLoader) 18 | 19 | command := &cobra.Command{ 20 | Use: "show", 21 | 22 | ValidArgsFunction: cobra.NoFileCompletions, 23 | 24 | PreRunE: lib.OnlyStylish, 25 | 26 | Hidden: true, 27 | 28 | RunE: func(cmd *cobra.Command, args []string) error { 29 | upOptions.SetCommand(args) 30 | 31 | if err := resourceLoader.Load(settings.Profile); err != nil { 32 | return err 33 | } 34 | 35 | upParameters, err := upOptions.ToParameters() 36 | if err != nil { 37 | return err 38 | } 39 | 40 | return lib.FormatCommandData(cmd, upParameters) 41 | }, 42 | } 43 | 44 | flags := command.Flags() 45 | 46 | flags.AddFlag(options.Organization.GetFlag("organization")) 47 | flags.AddFlag(options.Project.GetFlag("project")) 48 | flags.AddFlag(options.Environment.GetFlag("environment")) 49 | flags.AddFlag(options.ServiceComponent.GetFlag("component")) 50 | 51 | upOptions.UpdateFlagSet(command, flags) 52 | 53 | mainCmd.AddCommand(command) 54 | } 55 | -------------------------------------------------------------------------------- /cmd/remote_development/down.go: -------------------------------------------------------------------------------- 1 | package remote_development 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/config" 5 | "bunnyshell.com/cli/pkg/k8s/bridge" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "bunnyshell.com/cli/pkg/remote_development/action" 8 | "bunnyshell.com/cli/pkg/remote_development/action/down" 9 | remoteDevConfig "bunnyshell.com/cli/pkg/remote_development/config" 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | func init() { 14 | options := config.GetOptions() 15 | settings := config.GetSettings() 16 | 17 | resourceLoader := bridge.NewResourceLoader() 18 | downOptions := down.NewOptions(remoteDevConfig.NewManager(), resourceLoader) 19 | 20 | command := &cobra.Command{ 21 | Use: "down", 22 | 23 | ValidArgsFunction: cobra.NoFileCompletions, 24 | 25 | PreRunE: lib.OnlyStylish, 26 | 27 | RunE: func(cmd *cobra.Command, args []string) error { 28 | if err := resourceLoader.Load(settings.Profile); err != nil { 29 | return err 30 | } 31 | 32 | downParameters, err := downOptions.ToParameters() 33 | if err != nil { 34 | return err 35 | } 36 | 37 | downAction := action.NewDown(*resourceLoader.Environment) 38 | 39 | return downAction.Run(downParameters) 40 | }, 41 | } 42 | 43 | flags := command.Flags() 44 | 45 | flags.AddFlag(options.Organization.GetFlag("organization")) 46 | flags.AddFlag(options.Project.GetFlag("project")) 47 | flags.AddFlag(options.Environment.GetFlag("environment")) 48 | flags.AddFlag(options.ServiceComponent.GetFlag("component")) 49 | 50 | downOptions.UpdateFlagSet(command, flags) 51 | 52 | mainCmd.AddCommand(command) 53 | } 54 | -------------------------------------------------------------------------------- /cmd/remote_development/root.go: -------------------------------------------------------------------------------- 1 | package remote_development 2 | 3 | import ( 4 | cfgCommand "bunnyshell.com/cli/cmd/remote_development/config" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/util" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | var mainCmd = &cobra.Command{ 11 | Use: "remote-development", 12 | Aliases: []string{"rdev"}, 13 | 14 | Short: "Remote Development", 15 | } 16 | 17 | func init() { 18 | config.MainManager.CommandWithAPI(mainCmd) 19 | 20 | util.AddGroupedCommands( 21 | mainCmd, 22 | cobra.Group{ 23 | ID: "Config", 24 | Title: "Commands for config management:", 25 | }, 26 | []*cobra.Command{ 27 | cfgCommand.GetMainCommand(), 28 | }, 29 | ) 30 | } 31 | 32 | func GetMainCommand() *cobra.Command { 33 | return mainCmd 34 | } 35 | -------------------------------------------------------------------------------- /cmd/secret/encrypt_definition.go: -------------------------------------------------------------------------------- 1 | package secret 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/secret" 5 | "bunnyshell.com/cli/pkg/build" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "github.com/MakeNowJust/heredoc" 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | var encryptDefinitionCommandExample = heredoc.Docf(` 12 | %[1]s%[2]s secret encrypt-definition --organization dMVwZO5jGN --file plain.txt 13 | %[1]scat plain.txt | %[2]s secret encrypt-definition --organization dMVwZO5jGN 14 | `, "\t", build.Name) 15 | 16 | func init() { 17 | transcriptConfigurationOptions := secret.NewTranscriptConfigurationOptions() 18 | 19 | command := &cobra.Command{ 20 | Use: "encrypt-definition", 21 | 22 | Short: "Encrypts an environment definition for the given organization", 23 | Example: encryptDefinitionCommandExample, 24 | 25 | ValidArgsFunction: cobra.NoFileCompletions, 26 | 27 | PreRunE: func(cmd *cobra.Command, args []string) error { 28 | return validateDefinitionCommand(transcriptConfigurationOptions) 29 | }, 30 | 31 | RunE: func(cmd *cobra.Command, args []string) error { 32 | encryptedDefinition, err := executeTranscriptConfiguration(transcriptConfigurationOptions, secret.TranscriptModeObfuscated) 33 | if err != nil { 34 | return lib.FormatCommandError(cmd, err) 35 | } 36 | 37 | cmd.Println(encryptedDefinition) 38 | 39 | return nil 40 | }, 41 | } 42 | 43 | transcriptConfigurationOptions.UpdateSelfFlags(command.Flags()) 44 | 45 | mainCmd.AddCommand(command) 46 | } 47 | -------------------------------------------------------------------------------- /cmd/secret/root.go: -------------------------------------------------------------------------------- 1 | package secret 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/config" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var mainCmd = &cobra.Command{ 9 | Use: "secrets", 10 | Aliases: []string{"sec"}, 11 | 12 | Short: "Secrets", 13 | Long: "Bunnyshell Secrets", 14 | } 15 | 16 | func init() { 17 | config.MainManager.CommandWithAPI(mainCmd) 18 | } 19 | 20 | func GetMainCommand() *cobra.Command { 21 | return mainCmd 22 | } 23 | -------------------------------------------------------------------------------- /cmd/template/definition.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/template" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func init() { 11 | settings := config.GetSettings() 12 | 13 | definitionOptions := template.NewDefinitionOptions("") 14 | 15 | command := &cobra.Command{ 16 | Use: "definition", 17 | Aliases: []string{"def"}, 18 | GroupID: mainGroup.ID, 19 | 20 | ValidArgsFunction: cobra.NoFileCompletions, 21 | 22 | RunE: func(cmd *cobra.Command, args []string) error { 23 | definition, err := template.Definition(definitionOptions) 24 | if err != nil { 25 | return lib.FormatCommandError(cmd, err) 26 | } 27 | 28 | if settings.OutputFormat == "json" { 29 | return lib.FormatCommandData(cmd, definition.Data) 30 | } 31 | 32 | cmd.Println(string(definition.Bytes)) 33 | 34 | return nil 35 | }, 36 | } 37 | 38 | flags := command.Flags() 39 | 40 | flags.AddFlag(getIDOption(&definitionOptions.ID).GetRequiredFlag("id")) 41 | 42 | definitionOptions.UpdateFlagSet(flags) 43 | 44 | mainCmd.AddCommand(command) 45 | } 46 | -------------------------------------------------------------------------------- /cmd/template/generate.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/config/option" 5 | "bunnyshell.com/cli/pkg/helper/template" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "bunnyshell.com/cli/pkg/util" 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | func init() { 12 | directory := "" 13 | 14 | command := &cobra.Command{ 15 | Use: "generate", 16 | Aliases: []string{"gen"}, 17 | GroupID: mainGroup.ID, 18 | 19 | ValidArgsFunction: cobra.NoFileCompletions, 20 | 21 | RunE: func(cmd *cobra.Command, args []string) error { 22 | if err := template.Generate(directory); err != nil { 23 | return lib.FormatCommandError(cmd, err) 24 | } 25 | 26 | cmd.Println("Template generated successfully in " + directory) 27 | 28 | return nil 29 | }, 30 | } 31 | 32 | flags := command.Flags() 33 | 34 | flags.AddFlag(getDirectoryOption(&directory).GetFlag("directory", util.FlagRequired, util.FlagDirname)) 35 | 36 | mainCmd.AddCommand(command) 37 | } 38 | 39 | func getDirectoryOption(value *string) *option.String { 40 | help := "New directory in which to generate the template" 41 | 42 | option := option.NewStringOption(value) 43 | 44 | option.AddFlagWithExtraHelp("directory", "Directory to generate the template in", help) 45 | 46 | return option 47 | } 48 | -------------------------------------------------------------------------------- /cmd/template/list.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/template" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func init() { 11 | options := config.GetOptions() 12 | settings := config.GetSettings() 13 | 14 | listOptions := template.NewListOptions() 15 | 16 | command := &cobra.Command{ 17 | Use: "list", 18 | GroupID: mainGroup.ID, 19 | 20 | ValidArgsFunction: cobra.NoFileCompletions, 21 | 22 | RunE: func(cmd *cobra.Command, args []string) error { 23 | listOptions.Organization = settings.Profile.Context.Organization 24 | 25 | if listOptions.Source == "public" { 26 | listOptions.Organization = "" 27 | } 28 | 29 | return lib.ShowCollection(cmd, listOptions, func() (lib.ModelWithPagination, error) { 30 | return template.List(listOptions) 31 | }) 32 | }, 33 | } 34 | 35 | flags := command.Flags() 36 | 37 | flags.AddFlag(options.Organization.GetFlag("organization")) 38 | 39 | listOptions.UpdateFlagSet(flags) 40 | 41 | mainCmd.AddCommand(command) 42 | } 43 | -------------------------------------------------------------------------------- /cmd/template/repository/list.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/template/repository" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func init() { 11 | options := config.GetOptions() 12 | settings := config.GetSettings() 13 | 14 | listOptions := repository.NewListOptions() 15 | 16 | command := &cobra.Command{ 17 | Use: "list", 18 | 19 | ValidArgsFunction: cobra.NoFileCompletions, 20 | 21 | RunE: func(cmd *cobra.Command, args []string) error { 22 | listOptions.Organization = settings.Profile.Context.Organization 23 | 24 | return lib.ShowCollection(cmd, listOptions, func() (lib.ModelWithPagination, error) { 25 | return repository.List(listOptions) 26 | }) 27 | }, 28 | } 29 | 30 | flags := command.Flags() 31 | 32 | flags.AddFlag(options.Organization.GetFlag("organization")) 33 | 34 | listOptions.UpdateFlagSet(flags) 35 | 36 | mainCmd.AddCommand(command) 37 | } 38 | -------------------------------------------------------------------------------- /cmd/template/repository/root.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/config" 5 | "github.com/spf13/cobra" 6 | ) 7 | 8 | var mainCmd = &cobra.Command{ 9 | Use: "repository", 10 | Aliases: []string{"repo"}, 11 | 12 | Short: "Template Repository", 13 | Long: "Bunnyshell Template Repository", 14 | } 15 | 16 | func init() { 17 | config.MainManager.CommandWithAPI(mainCmd) 18 | } 19 | 20 | func GetMainCommand() *cobra.Command { 21 | return mainCmd 22 | } 23 | -------------------------------------------------------------------------------- /cmd/template/repository/show.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "fmt" 5 | 6 | "bunnyshell.com/cli/pkg/api/template/repository" 7 | "bunnyshell.com/cli/pkg/build" 8 | "bunnyshell.com/cli/pkg/config/option" 9 | "bunnyshell.com/cli/pkg/lib" 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | func init() { 14 | itemOptions := repository.NewItemOptions("") 15 | 16 | command := &cobra.Command{ 17 | Use: "show", 18 | 19 | ValidArgsFunction: cobra.NoFileCompletions, 20 | 21 | RunE: func(cmd *cobra.Command, args []string) error { 22 | model, err := repository.Get(itemOptions) 23 | if err != nil { 24 | return lib.FormatCommandError(cmd, err) 25 | } 26 | 27 | return lib.FormatCommandData(cmd, model) 28 | }, 29 | } 30 | 31 | flags := command.Flags() 32 | 33 | flags.AddFlag(getIDOption(&itemOptions.ID).GetRequiredFlag("id")) 34 | 35 | mainCmd.AddCommand(command) 36 | } 37 | 38 | func getIDOption(value *string) *option.String { 39 | help := fmt.Sprintf( 40 | `Find available TemplateRepositories with "%s templates repository list"`, 41 | build.Name, 42 | ) 43 | 44 | option := option.NewStringOption(value) 45 | 46 | option.AddFlagWithExtraHelp("id", "TemplateRepository ID", help) 47 | 48 | return option 49 | } 50 | -------------------------------------------------------------------------------- /cmd/template/root.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "fmt" 5 | 6 | "bunnyshell.com/cli/cmd/template/repository" 7 | "bunnyshell.com/cli/pkg/build" 8 | "bunnyshell.com/cli/pkg/config" 9 | "bunnyshell.com/cli/pkg/config/option" 10 | "bunnyshell.com/cli/pkg/util" 11 | "github.com/spf13/cobra" 12 | ) 13 | 14 | var mainCmd = &cobra.Command{ 15 | Use: "templates", 16 | Aliases: []string{"tpl"}, 17 | 18 | Short: "Template", 19 | Long: "Bunnyshell Template", 20 | } 21 | 22 | var mainGroup = &cobra.Group{ 23 | ID: "templates", 24 | Title: "Commands for Templates:", 25 | } 26 | 27 | func init() { 28 | config.MainManager.CommandWithAPI(mainCmd) 29 | 30 | mainCmd.AddGroup(mainGroup) 31 | 32 | util.AddGroupedCommands( 33 | mainCmd, 34 | cobra.Group{ 35 | ID: "subresources", 36 | Title: "Commands for Template subresources:", 37 | }, 38 | []*cobra.Command{ 39 | repository.GetMainCommand(), 40 | }, 41 | ) 42 | } 43 | 44 | func GetMainCommand() *cobra.Command { 45 | return mainCmd 46 | } 47 | 48 | func getIDOption(value *string) *option.String { 49 | help := fmt.Sprintf( 50 | `Find available Templates with "%s templates list"`, 51 | build.Name, 52 | ) 53 | 54 | option := option.NewStringOption(value) 55 | 56 | option.AddFlagWithExtraHelp("id", "Template ID", help) 57 | 58 | return option 59 | } 60 | -------------------------------------------------------------------------------- /cmd/template/show.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/template" 5 | "bunnyshell.com/cli/pkg/lib" 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | func init() { 10 | itemOptions := template.NewItemOptions("") 11 | 12 | command := &cobra.Command{ 13 | Use: "show", 14 | GroupID: mainGroup.ID, 15 | 16 | ValidArgsFunction: cobra.NoFileCompletions, 17 | 18 | RunE: func(cmd *cobra.Command, args []string) error { 19 | model, err := template.Get(itemOptions) 20 | if err != nil { 21 | return lib.FormatCommandError(cmd, err) 22 | } 23 | 24 | return lib.FormatCommandData(cmd, model) 25 | }, 26 | } 27 | 28 | flags := command.Flags() 29 | 30 | flags.AddFlag(getIDOption(&itemOptions.ID).GetRequiredFlag("id")) 31 | 32 | mainCmd.AddCommand(command) 33 | } 34 | -------------------------------------------------------------------------------- /cmd/utils/root.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "bunnyshell.com/cli/cmd/git" 5 | "bunnyshell.com/cli/cmd/component_debug" 6 | "bunnyshell.com/cli/cmd/remote_development" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | var mainCmd = &cobra.Command{} 11 | 12 | func init() { 13 | mainCmd.AddCommand(git.GetMainCommand()) 14 | mainCmd.AddCommand(remote_development.GetMainCommand()) 15 | mainCmd.AddCommand(component_debug.GetMainCommand()) 16 | } 17 | 18 | func GetMainCommand() *cobra.Command { 19 | return mainCmd 20 | } 21 | -------------------------------------------------------------------------------- /cmd/variable/action/delete.go: -------------------------------------------------------------------------------- 1 | package action 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/variable" 5 | "bunnyshell.com/cli/pkg/lib" 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | func init() { 10 | deleteOptions := variable.NewDeleteOptions() 11 | 12 | command := &cobra.Command{ 13 | Use: "delete", 14 | 15 | ValidArgsFunction: cobra.NoFileCompletions, 16 | 17 | RunE: func(cmd *cobra.Command, args []string) error { 18 | err := variable.Delete(deleteOptions) 19 | if err != nil { 20 | return lib.FormatCommandError(cmd, err) 21 | } 22 | 23 | cmd.Printf("\nEnvironment variable %s successfully deleted\n", deleteOptions.ID) 24 | 25 | return nil 26 | }, 27 | } 28 | 29 | flags := command.Flags() 30 | 31 | flags.AddFlag(GetIDOption(&deleteOptions.ID).GetRequiredFlag("id")) 32 | 33 | mainCmd.AddCommand(command) 34 | } 35 | -------------------------------------------------------------------------------- /cmd/variable/action/edit.go: -------------------------------------------------------------------------------- 1 | package action 2 | 3 | import ( 4 | "io" 5 | "os" 6 | 7 | "bunnyshell.com/cli/pkg/api/variable" 8 | "bunnyshell.com/cli/pkg/lib" 9 | "bunnyshell.com/cli/pkg/util" 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | func init() { 14 | editOptions := variable.NewEditOptions("") 15 | 16 | command := &cobra.Command{ 17 | Use: "edit", 18 | 19 | ValidArgsFunction: cobra.NoFileCompletions, 20 | 21 | PreRunE: func(cmd *cobra.Command, args []string) error { 22 | hasStdin, err := util.IsStdinPresent() 23 | if err != nil { 24 | return err 25 | } 26 | 27 | flags := cmd.Flags() 28 | if flags.Changed("value") && hasStdin { 29 | return errMultipleValueInputs 30 | } 31 | 32 | return nil 33 | }, 34 | 35 | RunE: func(cmd *cobra.Command, args []string) error { 36 | flags := cmd.Flags() 37 | if flags.Changed("value") { 38 | editOptions.EnvironmentVariableEditAction.SetValue(flags.Lookup("value").Value.String()) 39 | } 40 | 41 | hasStdin, err := util.IsStdinPresent() 42 | if err != nil { 43 | return err 44 | } 45 | 46 | if hasStdin { 47 | buf, err := io.ReadAll(os.Stdin) 48 | if err != nil { 49 | return err 50 | } 51 | 52 | editOptions.EnvironmentVariableEditAction.SetValue(string(buf)) 53 | } 54 | 55 | model, err := variable.Edit(editOptions) 56 | if err != nil { 57 | return lib.FormatCommandError(cmd, err) 58 | } 59 | 60 | return lib.FormatCommandData(cmd, model) 61 | }, 62 | } 63 | 64 | flags := command.Flags() 65 | 66 | flags.AddFlag(GetIDOption(&editOptions.ID).GetRequiredFlag("id")) 67 | editOptions.UpdateFlagSet(flags) 68 | 69 | mainCmd.AddCommand(command) 70 | } 71 | -------------------------------------------------------------------------------- /cmd/variable/action/root.go: -------------------------------------------------------------------------------- 1 | package action 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "bunnyshell.com/cli/pkg/build" 8 | "bunnyshell.com/cli/pkg/config/option" 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | var ( 13 | errMissingValue = errors.New("the plain value must be provided") 14 | errMultipleValueInputs = errors.New("the value must be provided either by argument or by stdin, not both") 15 | ) 16 | 17 | var mainCmd = &cobra.Command{} 18 | 19 | func GetMainCommand() *cobra.Command { 20 | return mainCmd 21 | } 22 | 23 | func GetIDOption(value *string) *option.String { 24 | help := fmt.Sprintf( 25 | `Find available environment variables with "%s variables list"`, 26 | build.Name, 27 | ) 28 | 29 | idOption := option.NewStringOption(value) 30 | 31 | idOption.AddFlagWithExtraHelp("id", "Environment Variable Id", help) 32 | 33 | return idOption 34 | } 35 | -------------------------------------------------------------------------------- /cmd/variable/list.go: -------------------------------------------------------------------------------- 1 | package variable 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/variable" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func init() { 11 | options := config.GetOptions() 12 | settings := config.GetSettings() 13 | 14 | listOptions := variable.NewListOptions() 15 | 16 | command := &cobra.Command{ 17 | Use: "list", 18 | 19 | ValidArgsFunction: cobra.NoFileCompletions, 20 | 21 | RunE: func(cmd *cobra.Command, args []string) error { 22 | listOptions.Organization = settings.Profile.Context.Organization 23 | listOptions.Environment = settings.Profile.Context.Environment 24 | 25 | return lib.ShowCollection(cmd, listOptions, func() (lib.ModelWithPagination, error) { 26 | return variable.List(listOptions) 27 | }) 28 | }, 29 | } 30 | 31 | flags := command.Flags() 32 | 33 | flags.AddFlag(options.Organization.GetFlag("organization")) 34 | flags.AddFlag(options.Environment.GetFlag("environment")) 35 | 36 | listOptions.UpdateFlagSet(flags) 37 | 38 | mainCmd.AddCommand(command) 39 | } 40 | -------------------------------------------------------------------------------- /cmd/variable/root.go: -------------------------------------------------------------------------------- 1 | package variable 2 | 3 | import ( 4 | "bunnyshell.com/cli/cmd/variable/action" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/util" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | var mainCmd = &cobra.Command{ 11 | Use: "variables", 12 | Aliases: []string{"var"}, 13 | 14 | Short: "Environment Variables", 15 | Long: "Bunnyshell Environment Variables", 16 | } 17 | 18 | func init() { 19 | config.MainManager.CommandWithAPI(mainCmd) 20 | 21 | util.AddGroupedCommands( 22 | mainCmd, 23 | cobra.Group{ 24 | ID: "actions", 25 | Title: "Commands for Environment Variables:", 26 | }, 27 | action.GetMainCommand().Commands(), 28 | ) 29 | } 30 | 31 | func GetMainCommand() *cobra.Command { 32 | return mainCmd 33 | } 34 | -------------------------------------------------------------------------------- /cmd/variable/show.go: -------------------------------------------------------------------------------- 1 | package variable 2 | 3 | import ( 4 | "bunnyshell.com/cli/cmd/variable/action" 5 | "bunnyshell.com/cli/pkg/api/variable" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func init() { 11 | itemOptions := variable.NewItemOptions("") 12 | 13 | command := &cobra.Command{ 14 | Use: "show", 15 | 16 | ValidArgsFunction: cobra.NoFileCompletions, 17 | 18 | RunE: func(cmd *cobra.Command, args []string) error { 19 | model, err := variable.Get(itemOptions) 20 | if err != nil { 21 | return lib.FormatCommandError(cmd, err) 22 | } 23 | 24 | return lib.FormatCommandData(cmd, model) 25 | }, 26 | } 27 | 28 | flags := command.Flags() 29 | 30 | flags.AddFlag(action.GetIDOption(&itemOptions.ID).GetRequiredFlag("id")) 31 | 32 | mainCmd.AddCommand(command) 33 | } 34 | -------------------------------------------------------------------------------- /cmd/variable_group/action/delete.go: -------------------------------------------------------------------------------- 1 | package action 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/variable_group" 5 | "bunnyshell.com/cli/pkg/lib" 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | func init() { 10 | deleteOptions := variable_group.NewDeleteOptions() 11 | 12 | command := &cobra.Command{ 13 | Use: "delete", 14 | 15 | ValidArgsFunction: cobra.NoFileCompletions, 16 | 17 | RunE: func(cmd *cobra.Command, args []string) error { 18 | err := variable_group.Delete(deleteOptions) 19 | if err != nil { 20 | return lib.FormatCommandError(cmd, err) 21 | } 22 | 23 | cmd.Printf("\nGrouped environment variable %s successfully deleted\n", deleteOptions.ID) 24 | 25 | return nil 26 | }, 27 | } 28 | 29 | flags := command.Flags() 30 | 31 | flags.AddFlag(GetIDOption(&deleteOptions.ID).GetRequiredFlag("id")) 32 | 33 | mainCmd.AddCommand(command) 34 | } 35 | -------------------------------------------------------------------------------- /cmd/variable_group/action/edit.go: -------------------------------------------------------------------------------- 1 | package action 2 | 3 | import ( 4 | "io" 5 | "os" 6 | 7 | "bunnyshell.com/cli/pkg/api/variable_group" 8 | "bunnyshell.com/cli/pkg/lib" 9 | "bunnyshell.com/cli/pkg/util" 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | func init() { 14 | editOptions := variable_group.NewEditOptions("") 15 | 16 | command := &cobra.Command{ 17 | Use: "edit", 18 | 19 | ValidArgsFunction: cobra.NoFileCompletions, 20 | 21 | PreRunE: func(cmd *cobra.Command, _ []string) error { 22 | hasStdin, err := util.IsStdinPresent() 23 | if err != nil { 24 | return err 25 | } 26 | 27 | flags := cmd.Flags() 28 | if flags.Changed("value") && hasStdin { 29 | return errMultipleValueInputs 30 | } 31 | 32 | return nil 33 | }, 34 | 35 | RunE: func(cmd *cobra.Command, _ []string) error { 36 | flags := cmd.Flags() 37 | if flags.Changed("value") { 38 | editOptions.EnvironItemEditAction.SetValue(flags.Lookup("value").Value.String()) 39 | } 40 | 41 | hasStdin, err := util.IsStdinPresent() 42 | if err != nil { 43 | return err 44 | } 45 | 46 | if hasStdin { 47 | buf, err := io.ReadAll(os.Stdin) 48 | if err != nil { 49 | return err 50 | } 51 | 52 | editOptions.EnvironItemEditAction.SetValue(string(buf)) 53 | } 54 | 55 | model, err := variable_group.Edit(editOptions) 56 | if err != nil { 57 | return lib.FormatCommandError(cmd, err) 58 | } 59 | 60 | return lib.FormatCommandData(cmd, model) 61 | }, 62 | } 63 | 64 | flags := command.Flags() 65 | 66 | flags.AddFlag(GetIDOption(&editOptions.ID).GetRequiredFlag("id")) 67 | editOptions.UpdateFlagSet(flags) 68 | 69 | mainCmd.AddCommand(command) 70 | } 71 | -------------------------------------------------------------------------------- /cmd/variable_group/action/root.go: -------------------------------------------------------------------------------- 1 | package action 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "bunnyshell.com/cli/pkg/build" 8 | "bunnyshell.com/cli/pkg/config/option" 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | var ( 13 | errMissingValue = errors.New("the plain value must be provided") 14 | errMultipleValueInputs = errors.New("the value must be provided either by argument or by stdin, not both") 15 | ) 16 | 17 | var mainCmd = &cobra.Command{} 18 | 19 | func GetMainCommand() *cobra.Command { 20 | return mainCmd 21 | } 22 | 23 | func GetIDOption(value *string) *option.String { 24 | help := fmt.Sprintf( 25 | `Find available environment variables with "%s variables list"`, 26 | build.Name, 27 | ) 28 | 29 | idOption := option.NewStringOption(value) 30 | 31 | idOption.AddFlagWithExtraHelp("id", "Environment Variable Id", help) 32 | 33 | return idOption 34 | } 35 | -------------------------------------------------------------------------------- /cmd/variable_group/list.go: -------------------------------------------------------------------------------- 1 | package variable_group 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/variable_group" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func init() { 11 | options := config.GetOptions() 12 | settings := config.GetSettings() 13 | 14 | listOptions := variable_group.NewListOptions() 15 | 16 | command := &cobra.Command{ 17 | Use: "list", 18 | 19 | ValidArgsFunction: cobra.NoFileCompletions, 20 | 21 | RunE: func(cmd *cobra.Command, _ []string) error { 22 | listOptions.Organization = settings.Profile.Context.Organization 23 | listOptions.Environment = settings.Profile.Context.Environment 24 | 25 | return lib.ShowCollection(cmd, listOptions, func() (lib.ModelWithPagination, error) { 26 | return variable_group.List(listOptions) 27 | }) 28 | }, 29 | } 30 | 31 | flags := command.Flags() 32 | 33 | flags.AddFlag(options.Organization.GetFlag("organization")) 34 | flags.AddFlag(options.Environment.GetFlag("environment")) 35 | 36 | listOptions.UpdateFlagSet(flags) 37 | 38 | mainCmd.AddCommand(command) 39 | } 40 | -------------------------------------------------------------------------------- /cmd/variable_group/root.go: -------------------------------------------------------------------------------- 1 | package variable_group 2 | 3 | import ( 4 | "bunnyshell.com/cli/cmd/variable_group/action" 5 | "bunnyshell.com/cli/pkg/config" 6 | "bunnyshell.com/cli/pkg/util" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | var mainCmd = &cobra.Command{ 11 | Use: "variables-groups", 12 | Aliases: []string{"variables-group", "var-groups", "var-group", "var-g"}, 13 | 14 | Short: "Grouped Environment Variables", 15 | Long: "Bunnyshell Environment Variables in Groups", 16 | } 17 | 18 | func init() { 19 | config.MainManager.CommandWithAPI(mainCmd) 20 | 21 | util.AddGroupedCommands( 22 | mainCmd, 23 | cobra.Group{ 24 | ID: "actions", 25 | Title: "Commands for Environment Variables:", 26 | }, 27 | action.GetMainCommand().Commands(), 28 | ) 29 | } 30 | 31 | func GetMainCommand() *cobra.Command { 32 | return mainCmd 33 | } 34 | -------------------------------------------------------------------------------- /cmd/variable_group/show.go: -------------------------------------------------------------------------------- 1 | package variable_group 2 | 3 | import ( 4 | "bunnyshell.com/cli/cmd/variable/action" 5 | "bunnyshell.com/cli/pkg/api/variable_group" 6 | "bunnyshell.com/cli/pkg/lib" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | func init() { 11 | itemOptions := variable_group.NewItemOptions("") 12 | 13 | command := &cobra.Command{ 14 | Use: "show", 15 | 16 | ValidArgsFunction: cobra.NoFileCompletions, 17 | 18 | RunE: func(cmd *cobra.Command, _ []string) error { 19 | model, err := variable_group.Get(itemOptions) 20 | if err != nil { 21 | return lib.FormatCommandError(cmd, err) 22 | } 23 | 24 | return lib.FormatCommandData(cmd, model) 25 | }, 26 | } 27 | 28 | flags := command.Flags() 29 | 30 | flags.AddFlag(action.GetIDOption(&itemOptions.ID).GetRequiredFlag("id")) 31 | 32 | mainCmd.AddCommand(command) 33 | } 34 | -------------------------------------------------------------------------------- /config.sample.yaml: -------------------------------------------------------------------------------- 1 | defaultprofile: sample 2 | outputformat: json 3 | profiles: 4 | sample: 5 | # @see https://environments.bunnyshell.com/access-token 6 | token: '' 7 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | 8 | "bunnyshell.com/cli/cmd" 9 | "bunnyshell.com/cli/pkg/config" 10 | "bunnyshell.com/cli/pkg/interactive" 11 | ) 12 | 13 | func main() { 14 | defer recovery() 15 | 16 | cmd.Execute() 17 | } 18 | 19 | func recovery() { 20 | rErr := recover() 21 | if rErr == nil { 22 | return 23 | } 24 | 25 | if err, ok := rErr.(error); ok { 26 | if errors.Is(err, interactive.ErrInvalidValue) { 27 | fmt.Println("[panic]", err) 28 | os.Exit(1) 29 | } 30 | 31 | if errors.Is(err, config.ErrInvalidValue) { 32 | fmt.Println("[panic]", err) 33 | os.Exit(1) 34 | } 35 | } 36 | 37 | panic(rErr) 38 | } 39 | -------------------------------------------------------------------------------- /pkg/api/common/action.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "github.com/spf13/pflag" 5 | ) 6 | 7 | type ActionOptions struct { 8 | ItemOptions 9 | 10 | WithoutPipeline bool 11 | } 12 | 13 | func NewActionOptions(id string) *ActionOptions { 14 | return &ActionOptions{ 15 | ItemOptions: *NewItemOptions(id), 16 | 17 | WithoutPipeline: false, 18 | } 19 | } 20 | 21 | func (ao *ActionOptions) UpdateFlagSet(flags *pflag.FlagSet) { 22 | flags.BoolVar(&ao.WithoutPipeline, "no-wait", ao.WithoutPipeline, "Do not wait for pipeline until finish") 23 | } 24 | -------------------------------------------------------------------------------- /pkg/api/common/item.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | type ItemOptions struct { 4 | Options 5 | 6 | ID string 7 | } 8 | 9 | func NewItemOptions(id string) *ItemOptions { 10 | return &ItemOptions{ 11 | ID: id, 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /pkg/api/common/list.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "github.com/spf13/pflag" 5 | ) 6 | 7 | type ListOptions struct { 8 | Options 9 | 10 | Page int32 11 | } 12 | 13 | func NewListOptions() *ListOptions { 14 | return &ListOptions{ 15 | Page: 1, 16 | } 17 | } 18 | 19 | func (lo *ListOptions) UpdateFlagSet(flags *pflag.FlagSet) { 20 | flags.Int32Var(&lo.Page, "page", lo.Page, "Listing Page") 21 | } 22 | 23 | func (lo *ListOptions) SetPage(page int32) { 24 | lo.Page = page 25 | } 26 | -------------------------------------------------------------------------------- /pkg/api/common/options.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import "bunnyshell.com/cli/pkg/config" 4 | 5 | type Options struct { 6 | Profile *config.Profile 7 | } 8 | 9 | func NewOptions() *Options { 10 | return &Options{} 11 | } 12 | 13 | func (o *Options) GetProfile() config.Profile { 14 | if o.Profile == nil { 15 | return config.GetSettings().Profile 16 | } 17 | 18 | return *o.Profile 19 | } 20 | -------------------------------------------------------------------------------- /pkg/api/common/partial_action.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/pflag" 7 | ) 8 | 9 | const componentVarName = "component" 10 | 11 | type PartialActionOptions struct { 12 | ActionOptions 13 | 14 | components []string 15 | 16 | flags *pflag.FlagSet 17 | } 18 | 19 | func NewPartialActionOptions(id string) *PartialActionOptions { 20 | return &PartialActionOptions{ 21 | ActionOptions: *NewActionOptions(id), 22 | 23 | components: []string{}, 24 | } 25 | } 26 | 27 | func (pao *PartialActionOptions) UpdateFlagSet(flags *pflag.FlagSet) { 28 | pao.ActionOptions.UpdateFlagSet(flags) 29 | 30 | // We'll need this later to check if the flag was provided at all - in order to determine if it was a partial action or not. 31 | pao.flags = flags 32 | 33 | usage := fmt.Sprintf("Execute a partial action with the set components. Provide \"--%s ''\" for a no-components operation.", componentVarName) 34 | flags.StringArrayVar(&pao.components, componentVarName, pao.components, usage) 35 | } 36 | 37 | // Handle the "--component ”" case. 38 | func (pao *PartialActionOptions) GetActionComponents() []string { 39 | if len(pao.components) == 1 && pao.components[0] == "" { 40 | return []string{} 41 | } 42 | 43 | return pao.components 44 | } 45 | 46 | func (pao *PartialActionOptions) IsPartial() bool { 47 | if pao.flags == nil { 48 | return false 49 | } 50 | 51 | return pao.flags.Lookup(componentVarName).Changed 52 | } 53 | -------------------------------------------------------------------------------- /pkg/api/component/action_edit.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/common" 5 | "bunnyshell.com/cli/pkg/api/environment" 6 | "github.com/spf13/pflag" 7 | ) 8 | 9 | type EditComponentOptions struct { 10 | common.ItemOptions 11 | 12 | environment.DeployOptions 13 | 14 | EditComponentData 15 | 16 | WithDeploy bool 17 | } 18 | 19 | type EditComponentData struct { 20 | K8SIntegration string 21 | 22 | TargetRepository string 23 | TargetBranch string 24 | } 25 | 26 | func NewEditComponentOptions(component string) *EditComponentOptions { 27 | return &EditComponentOptions{ 28 | ItemOptions: *common.NewItemOptions(component), 29 | 30 | DeployOptions: *environment.NewDeployOptions(""), 31 | } 32 | } 33 | 34 | func (eo *EditComponentOptions) UpdateFlagSet(flags *pflag.FlagSet) { 35 | data := eo.EditComponentData 36 | 37 | flags.BoolVar(&eo.WithDeploy, "deploy", eo.WithDeploy, "Deploy the environment after update") 38 | 39 | flags.StringVar(&data.K8SIntegration, "k8s", data.K8SIntegration, "Set Kubernetes integration for the environment (if not set)") 40 | 41 | eo.DeployOptions.UpdateFlagSet(flags) 42 | } 43 | -------------------------------------------------------------------------------- /pkg/api/component/endpoint/item.go: -------------------------------------------------------------------------------- 1 | package endpoint 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/common" 8 | "bunnyshell.com/cli/pkg/lib" 9 | "bunnyshell.com/sdk" 10 | ) 11 | 12 | func NewItemOptions(id string) *common.ItemOptions { 13 | return common.NewItemOptions(id) 14 | } 15 | 16 | func Get(options *common.ItemOptions) (*sdk.ComponentEndpointItem, error) { 17 | model, resp, err := GetRaw(options) 18 | if err != nil { 19 | return nil, api.ParseError(resp, err) 20 | } 21 | 22 | return model, nil 23 | } 24 | 25 | func GetRaw(options *common.ItemOptions) (*sdk.ComponentEndpointItem, *http.Response, error) { 26 | profile := options.GetProfile() 27 | 28 | ctx, cancel := lib.GetContextFromProfile(profile) 29 | defer cancel() 30 | 31 | request := lib.GetAPIFromProfile(profile).ComponentEndpointAPI.ComponentEndpointView(ctx, options.ID) 32 | 33 | return request.Execute() 34 | } 35 | -------------------------------------------------------------------------------- /pkg/api/component/git/item.go: -------------------------------------------------------------------------------- 1 | package git 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/common" 8 | "bunnyshell.com/cli/pkg/lib" 9 | "bunnyshell.com/sdk" 10 | ) 11 | 12 | func NewItemOptions(id string) *common.ItemOptions { 13 | return common.NewItemOptions(id) 14 | } 15 | 16 | func Get(options *common.ItemOptions) (*sdk.ComponentGitItem, error) { 17 | model, resp, err := GetRaw(options) 18 | if err != nil { 19 | return nil, api.ParseError(resp, err) 20 | } 21 | 22 | return model, nil 23 | } 24 | 25 | func GetRaw(options *common.ItemOptions) (*sdk.ComponentGitItem, *http.Response, error) { 26 | profile := options.GetProfile() 27 | 28 | ctx, cancel := lib.GetContextFromProfile(profile) 29 | defer cancel() 30 | 31 | request := lib.GetAPIFromProfile(profile).ComponentGitAPI.ComponentGitView(ctx, options.ID) 32 | 33 | return request.Execute() 34 | } 35 | -------------------------------------------------------------------------------- /pkg/api/component/item.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/common" 8 | "bunnyshell.com/cli/pkg/lib" 9 | "bunnyshell.com/sdk" 10 | ) 11 | 12 | func NewItemOptions(id string) *common.ItemOptions { 13 | return common.NewItemOptions(id) 14 | } 15 | 16 | func Get(options *common.ItemOptions) (*sdk.ComponentItem, error) { 17 | model, resp, err := GetRaw(options) 18 | if err != nil { 19 | return nil, api.ParseError(resp, err) 20 | } 21 | 22 | return model, nil 23 | } 24 | 25 | func GetRaw(options *common.ItemOptions) (*sdk.ComponentItem, *http.Response, error) { 26 | profile := options.GetProfile() 27 | 28 | ctx, cancel := lib.GetContextFromProfile(profile) 29 | defer cancel() 30 | 31 | request := lib.GetAPIFromProfile(profile).ComponentAPI.ComponentView(ctx, options.ID) 32 | 33 | return request.Execute() 34 | } 35 | -------------------------------------------------------------------------------- /pkg/api/component/rdev_config.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/common" 8 | "bunnyshell.com/cli/pkg/lib" 9 | "bunnyshell.com/sdk" 10 | ) 11 | 12 | type RDevConfigOptions struct { 13 | common.ItemOptions 14 | } 15 | 16 | func NewRDevConfigOptions(id string) *RDevContextOptions { 17 | return &RDevContextOptions{ 18 | ItemOptions: *common.NewItemOptions(id), 19 | } 20 | } 21 | 22 | func RDevConfig(options *RDevContextOptions) (*sdk.ComponentConfigItem, error) { 23 | model, resp, err := RDevConfigRaw(options) 24 | if err != nil { 25 | return nil, api.ParseError(resp, err) 26 | } 27 | 28 | return model, nil 29 | } 30 | 31 | func RDevConfigRaw(options *RDevContextOptions) (*sdk.ComponentConfigItem, *http.Response, error) { 32 | profile := options.GetProfile() 33 | 34 | ctx, cancel := lib.GetContextFromProfile(profile) 35 | defer cancel() 36 | 37 | request := lib.GetAPIFromProfile(profile).ComponentAPI.ComponentRemoteDevConfig(ctx, options.ID) 38 | 39 | return request.Execute() 40 | } 41 | -------------------------------------------------------------------------------- /pkg/api/component/rdev_profile.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/common" 8 | "bunnyshell.com/cli/pkg/lib" 9 | "bunnyshell.com/sdk" 10 | ) 11 | 12 | type RDevContextOptions struct { 13 | common.ItemOptions 14 | 15 | Profile any 16 | } 17 | 18 | func NewRDevContextOptions(id string) *RDevContextOptions { 19 | return &RDevContextOptions{ 20 | ItemOptions: *common.NewItemOptions(id), 21 | } 22 | } 23 | 24 | func RDevContext(options *RDevContextOptions) (*sdk.ComponentProfileItem, error) { 25 | model, resp, err := RDevContextRaw(options) 26 | if err != nil { 27 | return nil, api.ParseError(resp, err) 28 | } 29 | 30 | return model, nil 31 | } 32 | 33 | func RDevContextRaw(options *RDevContextOptions) (*sdk.ComponentProfileItem, *http.Response, error) { 34 | profile := options.GetProfile() 35 | 36 | ctx, cancel := lib.GetContextFromProfile(profile) 37 | defer cancel() 38 | 39 | request := lib.GetAPIFromProfile(profile).ComponentAPI.ComponentRemoteDevProfile(ctx, options.ID) 40 | 41 | return request.Body(options.Profile).Execute() 42 | } 43 | -------------------------------------------------------------------------------- /pkg/api/component/resources.go: -------------------------------------------------------------------------------- 1 | package component 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/common" 8 | "bunnyshell.com/cli/pkg/lib" 9 | "bunnyshell.com/sdk" 10 | ) 11 | 12 | type ResourceOptions struct { 13 | common.ItemOptions 14 | } 15 | 16 | func NewResourceOptions(id string) *ResourceOptions { 17 | return &ResourceOptions{ 18 | ItemOptions: *common.NewItemOptions(id), 19 | } 20 | } 21 | 22 | func Resources(options *ResourceOptions) ([]sdk.ComponentResourceItem, error) { 23 | model, resp, err := ResourcesRaw(options) 24 | if err != nil { 25 | return nil, api.ParseError(resp, err) 26 | } 27 | 28 | return model, nil 29 | } 30 | 31 | func ResourcesRaw(options *ResourceOptions) ([]sdk.ComponentResourceItem, *http.Response, error) { 32 | profile := options.GetProfile() 33 | 34 | ctx, cancel := lib.GetContextFromProfile(profile) 35 | defer cancel() 36 | 37 | request := lib.GetAPIFromProfile(profile).ComponentAPI.ComponentResources(ctx, options.ID) 38 | 39 | return request.Execute() 40 | } 41 | -------------------------------------------------------------------------------- /pkg/api/component_variable/action_delete.go: -------------------------------------------------------------------------------- 1 | package component_variable 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/common" 8 | "bunnyshell.com/cli/pkg/lib" 9 | ) 10 | 11 | type DeleteOptions struct { 12 | common.ItemOptions 13 | } 14 | 15 | func NewDeleteOptions() *DeleteOptions { 16 | return &DeleteOptions{} 17 | } 18 | 19 | func Delete(options *DeleteOptions) error { 20 | resp, err := DeleteRaw(options) 21 | if err != nil { 22 | return api.ParseError(resp, err) 23 | } 24 | 25 | return nil 26 | } 27 | 28 | func DeleteRaw(options *DeleteOptions) (*http.Response, error) { 29 | profile := options.GetProfile() 30 | 31 | ctx, cancel := lib.GetContextFromProfile(profile) 32 | defer cancel() 33 | 34 | request := lib.GetAPIFromProfile(profile). 35 | ServiceComponentVariableAPI.ServiceComponentVariableDelete(ctx, options.ID) 36 | 37 | return request.Execute() 38 | } 39 | -------------------------------------------------------------------------------- /pkg/api/component_variable/item.go: -------------------------------------------------------------------------------- 1 | package component_variable 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/common" 8 | "bunnyshell.com/cli/pkg/lib" 9 | "bunnyshell.com/sdk" 10 | ) 11 | 12 | func NewItemOptions(id string) *common.ItemOptions { 13 | return common.NewItemOptions(id) 14 | } 15 | 16 | func Get(options *common.ItemOptions) (*sdk.ServiceComponentVariableItem, error) { 17 | model, resp, err := GetRaw(options) 18 | if err != nil { 19 | return nil, api.ParseError(resp, err) 20 | } 21 | 22 | return model, nil 23 | } 24 | 25 | func GetRaw(options *common.ItemOptions) (*sdk.ServiceComponentVariableItem, *http.Response, error) { 26 | profile := options.GetProfile() 27 | 28 | ctx, cancel := lib.GetContextFromProfile(profile) 29 | defer cancel() 30 | 31 | request := lib.GetAPIFromProfile(profile).ServiceComponentVariableAPI.ServiceComponentVariableView(ctx, options.ID) 32 | 33 | return request.Execute() 34 | } 35 | -------------------------------------------------------------------------------- /pkg/api/environment/action_clone.go: -------------------------------------------------------------------------------- 1 | package environment 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/common" 8 | "bunnyshell.com/cli/pkg/lib" 9 | "bunnyshell.com/cli/pkg/util" 10 | "bunnyshell.com/sdk" 11 | "github.com/spf13/pflag" 12 | ) 13 | 14 | type CloneOptions struct { 15 | common.ActionOptions 16 | 17 | sdk.EnvironmentCloneAction 18 | } 19 | 20 | func NewCloneOptions(id string) *CloneOptions { 21 | return &CloneOptions{ 22 | ActionOptions: *common.NewActionOptions(id), 23 | 24 | EnvironmentCloneAction: *sdk.NewEnvironmentCloneActionWithDefaults(), 25 | } 26 | } 27 | 28 | func (co *CloneOptions) UpdateFlagSet(flags *pflag.FlagSet) { 29 | flags.StringVar(&co.EnvironmentCloneAction.Name, "name", co.EnvironmentCloneAction.Name, "Environment Clone Name") 30 | 31 | util.MarkFlagRequiredWithHelp(flags.Lookup("name"), "A unique name within the project for the environment clone") 32 | } 33 | 34 | func Clone(options *CloneOptions) (*sdk.EnvironmentItem, error) { 35 | model, resp, err := CloneRaw(options) 36 | if err != nil { 37 | return nil, api.ParseError(resp, err) 38 | } 39 | 40 | return model, err 41 | } 42 | 43 | func CloneRaw(options *CloneOptions) (*sdk.EnvironmentItem, *http.Response, error) { 44 | profile := options.GetProfile() 45 | 46 | ctx, cancel := lib.GetContextFromProfile(profile) 47 | defer cancel() 48 | 49 | request := lib.GetAPIFromProfile(profile).EnvironmentAPI.EnvironmentClone(ctx, options.ID) 50 | 51 | request = request.EnvironmentCloneAction(options.EnvironmentCloneAction) 52 | 53 | return request.Execute() 54 | } 55 | -------------------------------------------------------------------------------- /pkg/api/environment/action_definition.go: -------------------------------------------------------------------------------- 1 | package environment 2 | 3 | import ( 4 | "io" 5 | "net/http" 6 | 7 | "bunnyshell.com/cli/pkg/api" 8 | "bunnyshell.com/cli/pkg/api/common" 9 | "bunnyshell.com/cli/pkg/lib" 10 | ) 11 | 12 | type DefinitionData map[string]any 13 | 14 | type DefinitionItem struct { 15 | Data DefinitionData 16 | 17 | Bytes []byte 18 | } 19 | 20 | type DefinitionOptions struct { 21 | common.ItemOptions 22 | } 23 | 24 | func NewDefinitionOptions(id string) *DefinitionOptions { 25 | return &DefinitionOptions{ 26 | ItemOptions: *common.NewItemOptions(id), 27 | } 28 | } 29 | 30 | func Definition(options *DefinitionOptions) (*DefinitionItem, error) { 31 | model, resp, err := DefinitionRaw(options) 32 | if err != nil { 33 | return nil, api.ParseError(resp, err) 34 | } 35 | 36 | bytes, err := io.ReadAll(resp.Body) 37 | if err != nil { 38 | return nil, err 39 | } 40 | 41 | return &DefinitionItem{ 42 | Data: model, 43 | Bytes: bytes, 44 | }, nil 45 | } 46 | 47 | func DefinitionRaw(options *DefinitionOptions) (DefinitionData, *http.Response, error) { 48 | profile := options.GetProfile() 49 | 50 | ctx, cancel := lib.GetContextFromProfile(profile) 51 | defer cancel() 52 | 53 | request := lib.GetAPIFromProfile(profile).EnvironmentAPI.EnvironmentDefinition(ctx, options.ID) 54 | 55 | return request.Execute() 56 | } 57 | -------------------------------------------------------------------------------- /pkg/api/environment/action_delete.go: -------------------------------------------------------------------------------- 1 | package environment 2 | 3 | import ( 4 | "github.com/spf13/pflag" 5 | "net/http" 6 | 7 | "bunnyshell.com/cli/pkg/api" 8 | "bunnyshell.com/cli/pkg/api/common" 9 | "bunnyshell.com/cli/pkg/lib" 10 | "bunnyshell.com/sdk" 11 | ) 12 | 13 | type DeleteOptions struct { 14 | common.ActionOptions 15 | 16 | QueueIfSomethingInProgress bool 17 | } 18 | 19 | func NewDeleteOptions(id string) *DeleteOptions { 20 | return &DeleteOptions{ 21 | ActionOptions: *common.NewActionOptions(id), 22 | QueueIfSomethingInProgress: false, 23 | } 24 | } 25 | 26 | func (options *DeleteOptions) UpdateFlagSet(flags *pflag.FlagSet) { 27 | options.ActionOptions.UpdateFlagSet(flags) 28 | 29 | //flags.BoolVar(&options.QueueIfSomethingInProgress, "queue", options.QueueIfSomethingInProgress, "Queue the delete pipeline if another operation is in progress now") 30 | } 31 | 32 | func Delete(options *DeleteOptions) (*sdk.EventItem, error) { 33 | model, resp, err := DeleteRaw(options) 34 | if err != nil { 35 | return nil, api.ParseError(resp, err) 36 | } 37 | 38 | return model, nil 39 | } 40 | 41 | func DeleteRaw(options *DeleteOptions) (*sdk.EventItem, *http.Response, error) { 42 | profile := options.GetProfile() 43 | 44 | ctx, cancel := lib.GetContextFromProfile(profile) 45 | defer cancel() 46 | 47 | request := lib.GetAPIFromProfile(profile).EnvironmentAPI.EnvironmentDelete(ctx, options.ID) 48 | 49 | return request.Execute() 50 | } 51 | -------------------------------------------------------------------------------- /pkg/api/environment/action_edit_build_settings.go: -------------------------------------------------------------------------------- 1 | package environment 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/build_settings" 8 | "bunnyshell.com/cli/pkg/lib" 9 | "bunnyshell.com/sdk" 10 | ) 11 | 12 | type EditBuildSettingsOptions struct { 13 | build_settings.EditOptions 14 | 15 | sdk.EnvironmentEditBuildSettingsAction 16 | } 17 | 18 | func NewEditBuildSettingsOptions(project string) *EditBuildSettingsOptions { 19 | return &EditBuildSettingsOptions{ 20 | EditOptions: *build_settings.NewEditOptions(project), 21 | 22 | EnvironmentEditBuildSettingsAction: *sdk.NewEnvironmentEditBuildSettingsActionWithDefaults(), 23 | } 24 | } 25 | 26 | func EditBuildSettings(options *EditBuildSettingsOptions) (*sdk.EnvironmentItem, error) { 27 | model, resp, err := EditBuildSettingsRaw(options) 28 | if err != nil { 29 | return nil, api.ParseError(resp, err) 30 | } 31 | 32 | return model, nil 33 | } 34 | 35 | func EditBuildSettingsRaw(options *EditBuildSettingsOptions) (*sdk.EnvironmentItem, *http.Response, error) { 36 | profile := options.GetProfile() 37 | 38 | ctx, cancel := lib.GetContextFromProfile(profile) 39 | defer cancel() 40 | 41 | build_settings.ApplyEditOptionsToAction(&options.EnvironmentEditBuildSettingsAction, &options.EditData) 42 | 43 | request := lib.GetAPIFromProfile(profile).EnvironmentAPI. 44 | EnvironmentEditBuildSettings(ctx, options.ID). 45 | EnvironmentEditBuildSettingsAction(options.EnvironmentEditBuildSettingsAction) 46 | 47 | return request.Execute() 48 | } 49 | -------------------------------------------------------------------------------- /pkg/api/environment/action_start.go: -------------------------------------------------------------------------------- 1 | package environment 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/common" 8 | "bunnyshell.com/cli/pkg/lib" 9 | "bunnyshell.com/sdk" 10 | "github.com/spf13/pflag" 11 | ) 12 | 13 | type StartOptions struct { 14 | common.PartialActionOptions 15 | 16 | WithDependencies bool 17 | } 18 | 19 | func NewStartOptions(id string) *StartOptions { 20 | return &StartOptions{ 21 | PartialActionOptions: *common.NewPartialActionOptions(id), 22 | } 23 | } 24 | 25 | func (options *StartOptions) UpdateFlagSet(flags *pflag.FlagSet) { 26 | options.PartialActionOptions.UpdateFlagSet(flags) 27 | 28 | flags.BoolVar(&options.WithDependencies, "with-dependencies", options.WithDependencies, "Start the component dependencies too.") 29 | } 30 | 31 | func Start(options *StartOptions) (*sdk.EventItem, error) { 32 | model, resp, err := StartRaw(options) 33 | if err != nil { 34 | return nil, api.ParseError(resp, err) 35 | } 36 | 37 | return model, nil 38 | } 39 | 40 | func StartRaw(options *StartOptions) (*sdk.EventItem, *http.Response, error) { 41 | profile := options.GetProfile() 42 | 43 | ctx, cancel := lib.GetContextFromProfile(profile) 44 | defer cancel() 45 | 46 | isPartialAction := options.IsPartial() 47 | 48 | request := lib.GetAPIFromProfile(profile).EnvironmentAPI.EnvironmentStart(ctx, options.ID). 49 | EnvironmentPartialStartAction(sdk.EnvironmentPartialStartAction{ 50 | IsPartial: &isPartialAction, 51 | Components: options.GetActionComponents(), 52 | WithDependencies: &options.WithDependencies, 53 | }) 54 | 55 | return request.Execute() 56 | } 57 | -------------------------------------------------------------------------------- /pkg/api/environment/action_stop.go: -------------------------------------------------------------------------------- 1 | package environment 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/common" 8 | "bunnyshell.com/cli/pkg/lib" 9 | "bunnyshell.com/sdk" 10 | ) 11 | 12 | type StopOptions struct { 13 | common.PartialActionOptions 14 | } 15 | 16 | func NewStopOptions(id string) *StopOptions { 17 | return &StopOptions{ 18 | PartialActionOptions: *common.NewPartialActionOptions(id), 19 | } 20 | } 21 | 22 | func Stop(options *StopOptions) (*sdk.EventItem, error) { 23 | model, resp, err := StopRaw(options) 24 | if err != nil { 25 | return nil, api.ParseError(resp, err) 26 | } 27 | 28 | return model, nil 29 | } 30 | 31 | func StopRaw(options *StopOptions) (*sdk.EventItem, *http.Response, error) { 32 | profile := options.GetProfile() 33 | 34 | ctx, cancel := lib.GetContextFromProfile(profile) 35 | defer cancel() 36 | 37 | isPartialAction := options.IsPartial() 38 | 39 | request := lib.GetAPIFromProfile(profile).EnvironmentAPI.EnvironmentStop(ctx, options.ID). 40 | EnvironmentPartialAction(sdk.EnvironmentPartialAction{ 41 | IsPartial: &isPartialAction, 42 | Components: options.GetActionComponents(), 43 | }) 44 | 45 | return request.Execute() 46 | } 47 | -------------------------------------------------------------------------------- /pkg/api/environment/item.go: -------------------------------------------------------------------------------- 1 | package environment 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/common" 8 | "bunnyshell.com/cli/pkg/lib" 9 | "bunnyshell.com/sdk" 10 | ) 11 | 12 | func NewItemOptions(id string) *common.ItemOptions { 13 | return common.NewItemOptions(id) 14 | } 15 | 16 | func Get(options *common.ItemOptions) (*sdk.EnvironmentItem, error) { 17 | model, resp, err := GetRaw(options) 18 | if err != nil { 19 | return nil, api.ParseError(resp, err) 20 | } 21 | 22 | return model, nil 23 | } 24 | 25 | func GetRaw(options *common.ItemOptions) (*sdk.EnvironmentItem, *http.Response, error) { 26 | profile := options.GetProfile() 27 | 28 | ctx, cancel := lib.GetContextFromProfile(profile) 29 | defer cancel() 30 | 31 | request := lib.GetAPIFromProfile(profile).EnvironmentAPI.EnvironmentView(ctx, options.ID) 32 | 33 | return request.Execute() 34 | } 35 | -------------------------------------------------------------------------------- /pkg/api/error.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "net/http" 7 | 8 | "bunnyshell.com/sdk" 9 | ) 10 | 11 | type Error struct { 12 | Title string `json:"title" yaml:"title"` 13 | Detail string `json:"detail" yaml:"detail"` 14 | 15 | Violations []sdk.ProblemViolation `json:"violations" yaml:"violations"` 16 | } 17 | 18 | func (pe Error) Error() string { 19 | return pe.Title + ": " + pe.Detail 20 | } 21 | 22 | func ParseError(resp *http.Response, err error) error { 23 | switch err := err.(type) { 24 | case net.Error: 25 | if err.Timeout() { 26 | return Error{ 27 | Title: "Operation timed out", 28 | Detail: err.Error(), 29 | 30 | Violations: nil, 31 | } 32 | } 33 | case *sdk.GenericOpenAPIError: 34 | problem, isProblem := err.Model().(sdk.ProblemGeneric) 35 | if isProblem { 36 | return Error{ 37 | Title: *problem.Title, 38 | Detail: *problem.Detail, 39 | 40 | Violations: problem.Violations, 41 | } 42 | } 43 | } 44 | 45 | if resp == nil { 46 | return err 47 | } 48 | 49 | return Error{ 50 | Title: fmt.Sprintf("Response Status: %d", resp.StatusCode), 51 | Detail: err.Error(), 52 | 53 | Violations: nil, 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /pkg/api/event/item.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/common" 8 | "bunnyshell.com/cli/pkg/lib" 9 | "bunnyshell.com/sdk" 10 | ) 11 | 12 | type ItemOptions struct { 13 | common.ItemOptions 14 | } 15 | 16 | func NewItemOptions(id string) *ItemOptions { 17 | return &ItemOptions{ 18 | ItemOptions: *common.NewItemOptions(id), 19 | } 20 | } 21 | 22 | func Get(options *ItemOptions) (*sdk.EventItem, error) { 23 | model, resp, err := GetRaw(options) 24 | if err != nil { 25 | return nil, api.ParseError(resp, err) 26 | } 27 | 28 | return model, nil 29 | } 30 | 31 | func GetRaw(options *ItemOptions) (*sdk.EventItem, *http.Response, error) { 32 | profile := options.GetProfile() 33 | 34 | ctx, cancel := lib.GetContextFromProfile(profile) 35 | defer cancel() 36 | 37 | request := lib.GetAPIFromProfile(profile).EventAPI.EventView(ctx, options.ID) 38 | 39 | return request.Execute() 40 | } 41 | -------------------------------------------------------------------------------- /pkg/api/k8s/item.go: -------------------------------------------------------------------------------- 1 | package k8s 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/common" 8 | "bunnyshell.com/cli/pkg/lib" 9 | "bunnyshell.com/sdk" 10 | ) 11 | 12 | func NewItemOptions(id string) *common.ItemOptions { 13 | return common.NewItemOptions(id) 14 | } 15 | 16 | func Get(options *common.ItemOptions) (*sdk.KubernetesIntegrationItem, error) { 17 | model, resp, err := GetRaw(options) 18 | if err != nil { 19 | return nil, api.ParseError(resp, err) 20 | } 21 | 22 | return model, nil 23 | } 24 | 25 | func GetRaw(options *common.ItemOptions) (*sdk.KubernetesIntegrationItem, *http.Response, error) { 26 | profile := options.GetProfile() 27 | 28 | ctx, cancel := lib.GetContextFromProfile(profile) 29 | defer cancel() 30 | 31 | request := lib.GetAPIFromProfile(profile).KubernetesIntegrationAPI.KubernetesIntegrationView(ctx, options.ID) 32 | 33 | return request.Execute() 34 | } 35 | -------------------------------------------------------------------------------- /pkg/api/organization/item.go: -------------------------------------------------------------------------------- 1 | package organization 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/common" 8 | "bunnyshell.com/cli/pkg/lib" 9 | "bunnyshell.com/sdk" 10 | ) 11 | 12 | func NewItemOptions(id string) *common.ItemOptions { 13 | return common.NewItemOptions(id) 14 | } 15 | 16 | func Get(options *common.ItemOptions) (*sdk.OrganizationItem, error) { 17 | model, resp, err := GetRaw(options) 18 | if err != nil { 19 | return nil, api.ParseError(resp, err) 20 | } 21 | 22 | return model, nil 23 | } 24 | 25 | func GetRaw(options *common.ItemOptions) (*sdk.OrganizationItem, *http.Response, error) { 26 | profile := options.GetProfile() 27 | 28 | ctx, cancel := lib.GetContextFromProfile(profile) 29 | defer cancel() 30 | 31 | request := lib.GetAPIFromProfile(profile).OrganizationAPI.OrganizationView(ctx, options.ID) 32 | 33 | return request.Execute() 34 | } 35 | -------------------------------------------------------------------------------- /pkg/api/organization/list.go: -------------------------------------------------------------------------------- 1 | package organization 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/common" 8 | "bunnyshell.com/cli/pkg/lib" 9 | "bunnyshell.com/sdk" 10 | "github.com/spf13/pflag" 11 | ) 12 | 13 | type ListOptions struct { 14 | common.ListOptions 15 | 16 | Search string 17 | } 18 | 19 | func NewListOptions() *ListOptions { 20 | return &ListOptions{ 21 | ListOptions: *common.NewListOptions(), 22 | } 23 | } 24 | 25 | func (lo *ListOptions) UpdateFlagSet(flags *pflag.FlagSet) { 26 | flags.StringVar(&lo.Search, "search", lo.Search, "Search by name") 27 | 28 | lo.ListOptions.UpdateFlagSet(flags) 29 | } 30 | 31 | func List(options *ListOptions) (*sdk.PaginatedOrganizationCollection, error) { 32 | model, resp, err := ListRaw(options) 33 | if err != nil { 34 | return nil, api.ParseError(resp, err) 35 | } 36 | 37 | return model, nil 38 | } 39 | 40 | func ListRaw(options *ListOptions) (*sdk.PaginatedOrganizationCollection, *http.Response, error) { 41 | profile := options.GetProfile() 42 | 43 | ctx, cancel := lib.GetContextFromProfile(profile) 44 | defer cancel() 45 | 46 | request := lib.GetAPIFromProfile(profile).OrganizationAPI.OrganizationList(ctx) 47 | 48 | return applyOptions(request, options).Execute() 49 | } 50 | 51 | func applyOptions(request sdk.ApiOrganizationListRequest, options *ListOptions) sdk.ApiOrganizationListRequest { 52 | if options == nil { 53 | return request 54 | } 55 | 56 | if options.Page > 1 { 57 | request = request.Page(options.Page) 58 | } 59 | 60 | if options.Search != "" { 61 | request = request.Search(options.Search) 62 | } 63 | 64 | return request 65 | } 66 | -------------------------------------------------------------------------------- /pkg/api/pipeline/item.go: -------------------------------------------------------------------------------- 1 | package pipeline 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/common" 8 | "bunnyshell.com/cli/pkg/lib" 9 | "bunnyshell.com/sdk" 10 | ) 11 | 12 | func NewItemOptions(id string) *common.ItemOptions { 13 | return common.NewItemOptions(id) 14 | } 15 | 16 | func Get(options *common.ItemOptions) (*sdk.PipelineItem, error) { 17 | model, resp, err := GetRaw(options) 18 | if err != nil { 19 | return nil, api.ParseError(resp, err) 20 | } 21 | 22 | return model, nil 23 | } 24 | 25 | func GetRaw(options *common.ItemOptions) (*sdk.PipelineItem, *http.Response, error) { 26 | profile := options.GetProfile() 27 | 28 | ctx, cancel := lib.GetContextFromProfile(profile) 29 | defer cancel() 30 | 31 | request := lib.GetAPIFromProfile(profile).PipelineAPI.PipelineView(ctx, options.ID) 32 | 33 | return request.Execute() 34 | } 35 | -------------------------------------------------------------------------------- /pkg/api/project/action_create.go: -------------------------------------------------------------------------------- 1 | package project 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/common" 8 | "bunnyshell.com/cli/pkg/lib" 9 | "bunnyshell.com/cli/pkg/util" 10 | "bunnyshell.com/sdk" 11 | "github.com/spf13/pflag" 12 | ) 13 | 14 | type CreateOptions struct { 15 | common.Options 16 | 17 | sdk.ProjectCreateAction 18 | } 19 | 20 | func NewCreateOptions() *CreateOptions { 21 | projectCreateOptions := sdk.NewProjectCreateAction("", "") 22 | 23 | projectCreateOptions.Labels = &map[string]string{} 24 | 25 | return &CreateOptions{ 26 | ProjectCreateAction: *projectCreateOptions, 27 | } 28 | } 29 | 30 | func (co *CreateOptions) UpdateFlagSet(flags *pflag.FlagSet) { 31 | flags.StringVar(&co.Name, "name", co.Name, "Unique name for the project") 32 | util.MarkFlagRequiredWithHelp(flags.Lookup("name"), "A unique name within the organization for the new project") 33 | 34 | flags.StringToStringVar(co.Labels, "label", *co.Labels, "Set labels for the new project (key=value)") 35 | } 36 | 37 | func Create(options *CreateOptions) (*sdk.ProjectItem, error) { 38 | model, resp, err := CreateRaw(options) 39 | if err != nil { 40 | return nil, api.ParseError(resp, err) 41 | } 42 | 43 | return model, nil 44 | } 45 | 46 | func CreateRaw(options *CreateOptions) (*sdk.ProjectItem, *http.Response, error) { 47 | profile := options.GetProfile() 48 | 49 | ctx, cancel := lib.GetContextFromProfile(profile) 50 | defer cancel() 51 | 52 | request := lib.GetAPIFromProfile(profile). 53 | ProjectAPI.ProjectCreate(ctx). 54 | ProjectCreateAction(options.ProjectCreateAction) 55 | 56 | return request.Execute() 57 | } 58 | -------------------------------------------------------------------------------- /pkg/api/project/action_delete.go: -------------------------------------------------------------------------------- 1 | package project 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/common" 8 | "bunnyshell.com/cli/pkg/lib" 9 | ) 10 | 11 | type DeleteOptions struct { 12 | common.ItemOptions 13 | } 14 | 15 | func NewDeleteOptions() *DeleteOptions { 16 | return &DeleteOptions{} 17 | } 18 | 19 | func Delete(options *DeleteOptions) error { 20 | resp, err := DeleteRaw(options) 21 | if err != nil { 22 | return api.ParseError(resp, err) 23 | } 24 | 25 | return nil 26 | } 27 | 28 | func DeleteRaw(options *DeleteOptions) (*http.Response, error) { 29 | profile := options.GetProfile() 30 | 31 | ctx, cancel := lib.GetContextFromProfile(profile) 32 | defer cancel() 33 | 34 | request := lib.GetAPIFromProfile(profile). 35 | ProjectAPI.ProjectDelete(ctx, options.ID) 36 | 37 | return request.Execute() 38 | } 39 | -------------------------------------------------------------------------------- /pkg/api/project/action_edit_build_settings.go: -------------------------------------------------------------------------------- 1 | package project 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/build_settings" 8 | "bunnyshell.com/cli/pkg/lib" 9 | "bunnyshell.com/sdk" 10 | ) 11 | 12 | type EditBuildSettingsOptions struct { 13 | build_settings.EditOptions 14 | 15 | sdk.ProjectEditBuildSettingsAction 16 | } 17 | 18 | func NewEditBuildSettingsOptions(project string) *EditBuildSettingsOptions { 19 | return &EditBuildSettingsOptions{ 20 | EditOptions: *build_settings.NewEditOptions(project), 21 | 22 | ProjectEditBuildSettingsAction: *sdk.NewProjectEditBuildSettingsActionWithDefaults(), 23 | } 24 | } 25 | 26 | func EditBuildSettings(options *EditBuildSettingsOptions) (*sdk.ProjectItem, error) { 27 | model, resp, err := EditBuildSettingsRaw(options) 28 | if err != nil { 29 | return nil, api.ParseError(resp, err) 30 | } 31 | 32 | return model, nil 33 | } 34 | 35 | func EditBuildSettingsRaw(options *EditBuildSettingsOptions) (*sdk.ProjectItem, *http.Response, error) { 36 | profile := options.GetProfile() 37 | 38 | ctx, cancel := lib.GetContextFromProfile(profile) 39 | defer cancel() 40 | 41 | build_settings.ApplyEditOptionsToAction(&options.ProjectEditBuildSettingsAction, &options.EditData) 42 | 43 | request := lib.GetAPIFromProfile(profile).ProjectAPI. 44 | ProjectEditBuildSettings(ctx, options.ID). 45 | ProjectEditBuildSettingsAction(options.ProjectEditBuildSettingsAction) 46 | 47 | return request.Execute() 48 | } 49 | -------------------------------------------------------------------------------- /pkg/api/project/item.go: -------------------------------------------------------------------------------- 1 | package project 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/common" 8 | "bunnyshell.com/cli/pkg/lib" 9 | "bunnyshell.com/sdk" 10 | ) 11 | 12 | func NewItemOptions(id string) *common.ItemOptions { 13 | return common.NewItemOptions(id) 14 | } 15 | 16 | func Get(options *common.ItemOptions) (*sdk.ProjectItem, error) { 17 | model, resp, err := GetRaw(options) 18 | if err != nil { 19 | return nil, api.ParseError(resp, err) 20 | } 21 | 22 | return model, nil 23 | } 24 | 25 | func GetRaw(options *common.ItemOptions) (*sdk.ProjectItem, *http.Response, error) { 26 | profile := options.GetProfile() 27 | 28 | ctx, cancel := lib.GetContextFromProfile(profile) 29 | defer cancel() 30 | 31 | request := lib.GetAPIFromProfile(profile).ProjectAPI.ProjectView(ctx, options.ID) 32 | 33 | return request.Execute() 34 | } 35 | -------------------------------------------------------------------------------- /pkg/api/project_variable/action_delete.go: -------------------------------------------------------------------------------- 1 | package project_variable 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/common" 8 | "bunnyshell.com/cli/pkg/lib" 9 | ) 10 | 11 | type DeleteOptions struct { 12 | common.ItemOptions 13 | } 14 | 15 | func NewDeleteOptions() *DeleteOptions { 16 | return &DeleteOptions{} 17 | } 18 | 19 | func Delete(options *DeleteOptions) error { 20 | resp, err := DeleteRaw(options) 21 | if err != nil { 22 | return api.ParseError(resp, err) 23 | } 24 | 25 | return nil 26 | } 27 | 28 | func DeleteRaw(options *DeleteOptions) (*http.Response, error) { 29 | profile := options.GetProfile() 30 | 31 | ctx, cancel := lib.GetContextFromProfile(profile) 32 | defer cancel() 33 | 34 | request := lib.GetAPIFromProfile(profile). 35 | ProjectVariableAPI.ProjectVariableDelete(ctx, options.ID) 36 | 37 | return request.Execute() 38 | } 39 | -------------------------------------------------------------------------------- /pkg/api/project_variable/item.go: -------------------------------------------------------------------------------- 1 | package project_variable 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/common" 8 | "bunnyshell.com/cli/pkg/lib" 9 | "bunnyshell.com/sdk" 10 | ) 11 | 12 | func NewItemOptions(id string) *common.ItemOptions { 13 | return common.NewItemOptions(id) 14 | } 15 | 16 | func Get(options *common.ItemOptions) (*sdk.ProjectVariableItem, error) { 17 | model, resp, err := GetRaw(options) 18 | if err != nil { 19 | return nil, api.ParseError(resp, err) 20 | } 21 | 22 | return model, nil 23 | } 24 | 25 | func GetRaw(options *common.ItemOptions) (*sdk.ProjectVariableItem, *http.Response, error) { 26 | profile := options.GetProfile() 27 | 28 | ctx, cancel := lib.GetContextFromProfile(profile) 29 | defer cancel() 30 | 31 | request := lib.GetAPIFromProfile(profile).ProjectVariableAPI.ProjectVariableView(ctx, options.ID) 32 | 33 | return request.Execute() 34 | } 35 | -------------------------------------------------------------------------------- /pkg/api/registry_integration/item.go: -------------------------------------------------------------------------------- 1 | package registry_integration 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/common" 8 | "bunnyshell.com/cli/pkg/lib" 9 | "bunnyshell.com/sdk" 10 | ) 11 | 12 | func NewItemOptions(id string) *common.ItemOptions { 13 | return common.NewItemOptions(id) 14 | } 15 | 16 | func Get(options *common.ItemOptions) (*sdk.RegistryIntegrationItem, error) { 17 | model, resp, err := GetRaw(options) 18 | if err != nil { 19 | return nil, api.ParseError(resp, err) 20 | } 21 | 22 | return model, nil 23 | } 24 | 25 | func GetRaw(options *common.ItemOptions) (*sdk.RegistryIntegrationItem, *http.Response, error) { 26 | profile := options.GetProfile() 27 | 28 | ctx, cancel := lib.GetContextFromProfile(profile) 29 | defer cancel() 30 | 31 | request := lib.GetAPIFromProfile(profile).RegistryIntegrationAPI.RegistryIntegrationView(ctx, options.ID) 32 | 33 | return request.Execute() 34 | } 35 | -------------------------------------------------------------------------------- /pkg/api/secret/decrypt.go: -------------------------------------------------------------------------------- 1 | package secret 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/common" 8 | "bunnyshell.com/cli/pkg/lib" 9 | "bunnyshell.com/sdk" 10 | "github.com/spf13/pflag" 11 | ) 12 | 13 | type DecryptOptions struct { 14 | common.Options 15 | 16 | sdk.SecretDecryptAction 17 | } 18 | 19 | func NewDecryptOptions() *DecryptOptions { 20 | return &DecryptOptions{} 21 | } 22 | 23 | func (options *DecryptOptions) UpdateSelfFlags(flags *pflag.FlagSet) { 24 | flags.StringVar(&options.Organization, "organization", options.Organization, "The Organization this secret was encrypted for") 25 | } 26 | 27 | func Decrypt(options *DecryptOptions) (*sdk.SecretDecryptedItem, error) { 28 | model, resp, err := DecryptRaw(options) 29 | if err != nil { 30 | return nil, api.ParseError(resp, err) 31 | } 32 | 33 | return model, nil 34 | } 35 | 36 | func DecryptRaw(options *DecryptOptions) (*sdk.SecretDecryptedItem, *http.Response, error) { 37 | profile := options.GetProfile() 38 | 39 | ctx, cancel := lib.GetContextFromProfile(profile) 40 | defer cancel() 41 | 42 | request := lib.GetAPIFromProfile(profile). 43 | SecretAPI.SecretDecrypt(ctx). 44 | SecretDecryptAction(options.SecretDecryptAction) 45 | 46 | return request.Execute() 47 | } 48 | -------------------------------------------------------------------------------- /pkg/api/secret/encrypt.go: -------------------------------------------------------------------------------- 1 | package secret 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/common" 8 | "bunnyshell.com/cli/pkg/lib" 9 | "bunnyshell.com/sdk" 10 | "github.com/spf13/pflag" 11 | ) 12 | 13 | type EncryptOptions struct { 14 | common.Options 15 | 16 | sdk.SecretEncryptAction 17 | } 18 | 19 | func NewEncryptOptions() *EncryptOptions { 20 | return &EncryptOptions{} 21 | } 22 | 23 | func (options *EncryptOptions) UpdateSelfFlags(flags *pflag.FlagSet) { 24 | flags.StringVar(&options.Organization, "organization", options.Organization, "The Organization for which to encrypt the secret") 25 | } 26 | 27 | func Encrypt(options *EncryptOptions) (*sdk.SecretEncryptedItem, error) { 28 | model, resp, err := EncryptRaw(options) 29 | if err != nil { 30 | return nil, api.ParseError(resp, err) 31 | } 32 | 33 | return model, nil 34 | } 35 | 36 | func EncryptRaw(options *EncryptOptions) (*sdk.SecretEncryptedItem, *http.Response, error) { 37 | profile := options.GetProfile() 38 | 39 | ctx, cancel := lib.GetContextFromProfile(profile) 40 | defer cancel() 41 | 42 | request := lib.GetAPIFromProfile(profile). 43 | SecretAPI.SecretEncrypt(ctx). 44 | SecretEncryptAction(options.SecretEncryptAction) 45 | 46 | return request.Execute() 47 | } 48 | -------------------------------------------------------------------------------- /pkg/api/template/definition.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "io" 5 | "net/http" 6 | 7 | "bunnyshell.com/cli/pkg/api" 8 | "bunnyshell.com/cli/pkg/api/common" 9 | "bunnyshell.com/cli/pkg/lib" 10 | ) 11 | 12 | type DefinitionData map[string]any 13 | 14 | type DefinitionItem struct { 15 | Data DefinitionData 16 | 17 | Bytes []byte 18 | } 19 | 20 | type DefinitionOptions struct { 21 | common.ActionOptions 22 | } 23 | 24 | func NewDefinitionOptions(id string) *DefinitionOptions { 25 | return &DefinitionOptions{ 26 | ActionOptions: *common.NewActionOptions(id), 27 | } 28 | } 29 | 30 | func Definition(options *DefinitionOptions) (*DefinitionItem, error) { 31 | model, resp, err := DefinitionRaw(options) 32 | if err != nil { 33 | return nil, api.ParseError(resp, err) 34 | } 35 | 36 | bytes, err := io.ReadAll(resp.Body) 37 | if err != nil { 38 | return nil, err 39 | } 40 | 41 | return &DefinitionItem{ 42 | Data: model, 43 | Bytes: bytes, 44 | }, nil 45 | } 46 | 47 | func DefinitionRaw(options *DefinitionOptions) (DefinitionData, *http.Response, error) { 48 | profile := options.GetProfile() 49 | 50 | ctx, cancel := lib.GetContextFromProfile(profile) 51 | defer cancel() 52 | 53 | request := lib.GetAPIFromProfile(profile).TemplateAPI.TemplateDefinition(ctx, options.ID) 54 | 55 | return request.Execute() 56 | } 57 | -------------------------------------------------------------------------------- /pkg/api/template/item.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/common" 8 | "bunnyshell.com/cli/pkg/lib" 9 | "bunnyshell.com/sdk" 10 | ) 11 | 12 | func NewItemOptions(id string) *common.ItemOptions { 13 | return common.NewItemOptions(id) 14 | } 15 | 16 | func Get(options *common.ItemOptions) (*sdk.TemplateItem, error) { 17 | model, resp, err := GetRaw(options) 18 | if err != nil { 19 | return nil, api.ParseError(resp, err) 20 | } 21 | 22 | return model, nil 23 | } 24 | 25 | func GetRaw(options *common.ItemOptions) (*sdk.TemplateItem, *http.Response, error) { 26 | profile := options.GetProfile() 27 | 28 | ctx, cancel := lib.GetContextFromProfile(profile) 29 | defer cancel() 30 | 31 | request := lib.GetAPIFromProfile(profile).TemplateAPI.TemplateView(ctx, options.ID) 32 | 33 | return request.Execute() 34 | } 35 | -------------------------------------------------------------------------------- /pkg/api/template/repository/item.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/common" 8 | "bunnyshell.com/cli/pkg/lib" 9 | "bunnyshell.com/sdk" 10 | ) 11 | 12 | func NewItemOptions(id string) *common.ItemOptions { 13 | return common.NewItemOptions(id) 14 | } 15 | 16 | func Get(options *common.ItemOptions) (*sdk.TemplatesRepositoryItem, error) { 17 | model, resp, err := GetRaw(options) 18 | if err != nil { 19 | return nil, api.ParseError(resp, err) 20 | } 21 | 22 | return model, nil 23 | } 24 | 25 | func GetRaw(options *common.ItemOptions) (*sdk.TemplatesRepositoryItem, *http.Response, error) { 26 | profile := options.GetProfile() 27 | 28 | ctx, cancel := lib.GetContextFromProfile(profile) 29 | defer cancel() 30 | 31 | request := lib.GetAPIFromProfile(profile).TemplatesRepositoryAPI.TemplatesRepositoryView(ctx, options.ID) 32 | 33 | return request.Execute() 34 | } 35 | -------------------------------------------------------------------------------- /pkg/api/template/repository/list.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/common" 8 | "bunnyshell.com/cli/pkg/lib" 9 | "bunnyshell.com/sdk" 10 | "github.com/spf13/pflag" 11 | ) 12 | 13 | type ListOptions struct { 14 | common.ListOptions 15 | 16 | Organization string 17 | } 18 | 19 | func NewListOptions() *ListOptions { 20 | return &ListOptions{ 21 | ListOptions: *common.NewListOptions(), 22 | } 23 | } 24 | 25 | func (lo *ListOptions) UpdateFlagSet(flags *pflag.FlagSet) { 26 | lo.ListOptions.UpdateFlagSet(flags) 27 | } 28 | 29 | func List(options *ListOptions) (*sdk.PaginatedTemplatesRepositoryCollection, error) { 30 | model, resp, err := ListRaw(options) 31 | if err != nil { 32 | return nil, api.ParseError(resp, err) 33 | } 34 | 35 | return model, nil 36 | } 37 | 38 | func ListRaw(options *ListOptions) (*sdk.PaginatedTemplatesRepositoryCollection, *http.Response, error) { 39 | profile := options.GetProfile() 40 | 41 | ctx, cancel := lib.GetContextFromProfile(profile) 42 | defer cancel() 43 | 44 | request := lib.GetAPIFromProfile(profile).TemplatesRepositoryAPI.TemplatesRepositoryList(ctx) 45 | 46 | return applyOptions(request, options).Execute() 47 | } 48 | 49 | func applyOptions(request sdk.ApiTemplatesRepositoryListRequest, options *ListOptions) sdk.ApiTemplatesRepositoryListRequest { 50 | if options == nil { 51 | return request 52 | } 53 | 54 | if options.Page > 1 { 55 | request = request.Page(options.Page) 56 | } 57 | 58 | if options.Organization != "" { 59 | request = request.Organization(options.Organization) 60 | } 61 | 62 | return request 63 | } 64 | -------------------------------------------------------------------------------- /pkg/api/variable/action_delete.go: -------------------------------------------------------------------------------- 1 | package variable 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/common" 8 | "bunnyshell.com/cli/pkg/lib" 9 | ) 10 | 11 | type DeleteOptions struct { 12 | common.ItemOptions 13 | } 14 | 15 | func NewDeleteOptions() *DeleteOptions { 16 | return &DeleteOptions{} 17 | } 18 | 19 | func Delete(options *DeleteOptions) error { 20 | resp, err := DeleteRaw(options) 21 | if err != nil { 22 | return api.ParseError(resp, err) 23 | } 24 | 25 | return nil 26 | } 27 | 28 | func DeleteRaw(options *DeleteOptions) (*http.Response, error) { 29 | profile := options.GetProfile() 30 | 31 | ctx, cancel := lib.GetContextFromProfile(profile) 32 | defer cancel() 33 | 34 | request := lib.GetAPIFromProfile(profile). 35 | EnvironmentVariableAPI.EnvironmentVariableDelete(ctx, options.ID) 36 | 37 | return request.Execute() 38 | } 39 | -------------------------------------------------------------------------------- /pkg/api/variable/item.go: -------------------------------------------------------------------------------- 1 | package variable 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/common" 8 | "bunnyshell.com/cli/pkg/lib" 9 | "bunnyshell.com/sdk" 10 | ) 11 | 12 | func NewItemOptions(id string) *common.ItemOptions { 13 | return common.NewItemOptions(id) 14 | } 15 | 16 | func Get(options *common.ItemOptions) (*sdk.EnvironmentVariableItem, error) { 17 | model, resp, err := GetRaw(options) 18 | if err != nil { 19 | return nil, api.ParseError(resp, err) 20 | } 21 | 22 | return model, nil 23 | } 24 | 25 | func GetRaw(options *common.ItemOptions) (*sdk.EnvironmentVariableItem, *http.Response, error) { 26 | profile := options.GetProfile() 27 | 28 | ctx, cancel := lib.GetContextFromProfile(profile) 29 | defer cancel() 30 | 31 | request := lib.GetAPIFromProfile(profile).EnvironmentVariableAPI.EnvironmentVariableView(ctx, options.ID) 32 | 33 | return request.Execute() 34 | } 35 | -------------------------------------------------------------------------------- /pkg/api/variable_group/action_delete.go: -------------------------------------------------------------------------------- 1 | package variable_group 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/common" 8 | "bunnyshell.com/cli/pkg/lib" 9 | ) 10 | 11 | type DeleteOptions struct { 12 | common.ItemOptions 13 | } 14 | 15 | func NewDeleteOptions() *DeleteOptions { 16 | return &DeleteOptions{} 17 | } 18 | 19 | func Delete(options *DeleteOptions) error { 20 | resp, err := DeleteRaw(options) 21 | if err != nil { 22 | return api.ParseError(resp, err) 23 | } 24 | 25 | return nil 26 | } 27 | 28 | func DeleteRaw(options *DeleteOptions) (*http.Response, error) { 29 | profile := options.GetProfile() 30 | 31 | ctx, cancel := lib.GetContextFromProfile(profile) 32 | defer cancel() 33 | 34 | request := lib.GetAPIFromProfile(profile).EnvironItemAPI.EnvironItemDelete(ctx, options.ID) 35 | 36 | return request.Execute() 37 | } 38 | -------------------------------------------------------------------------------- /pkg/api/variable_group/item.go: -------------------------------------------------------------------------------- 1 | package variable_group 2 | 3 | import ( 4 | "net/http" 5 | 6 | "bunnyshell.com/cli/pkg/api" 7 | "bunnyshell.com/cli/pkg/api/common" 8 | "bunnyshell.com/cli/pkg/lib" 9 | "bunnyshell.com/sdk" 10 | ) 11 | 12 | func NewItemOptions(id string) *common.ItemOptions { 13 | return common.NewItemOptions(id) 14 | } 15 | 16 | func Get(options *common.ItemOptions) (*sdk.EnvironItemItem, error) { 17 | model, resp, err := GetRaw(options) 18 | if err != nil { 19 | return nil, api.ParseError(resp, err) 20 | } 21 | 22 | return model, nil 23 | } 24 | 25 | func GetRaw(options *common.ItemOptions) (*sdk.EnvironItemItem, *http.Response, error) { 26 | profile := options.GetProfile() 27 | 28 | ctx, cancel := lib.GetContextFromProfile(profile) 29 | defer cancel() 30 | 31 | request := lib.GetAPIFromProfile(profile).EnvironItemAPI.EnvironItemView(ctx, options.ID) 32 | 33 | return request.Execute() 34 | } 35 | -------------------------------------------------------------------------------- /pkg/build/info.go: -------------------------------------------------------------------------------- 1 | package build 2 | 3 | var ( 4 | Name = "bns" 5 | 6 | EnvPrefix = "bunnyshell" 7 | 8 | Version = "dev" 9 | Commit = "none" 10 | Date = "unknown" 11 | 12 | LatestReleaseUrl = "https://github.com/bunnyshell/cli/releases/latest" 13 | ) 14 | -------------------------------------------------------------------------------- /pkg/config/enum/bool.go: -------------------------------------------------------------------------------- 1 | package enum 2 | 3 | import ( 4 | "github.com/spf13/pflag" 5 | "github.com/thediveo/enumflag/v2" 6 | ) 7 | 8 | type Bool enumflag.Flag 9 | 10 | const ( 11 | BoolNone Bool = iota 12 | BoolTrue 13 | BoolFalse 14 | ) 15 | 16 | var BoolMap = map[Bool][]string{ 17 | BoolNone: {"inherit"}, 18 | BoolTrue: {"true"}, 19 | BoolFalse: {"false"}, 20 | } 21 | 22 | var BoolList = []string{ 23 | "true", 24 | "false", 25 | } 26 | 27 | func NewBoolValue(value *Bool) pflag.Value { 28 | return enumflag.New(value, "bool", BoolMap, enumflag.EnumCaseInsensitive) 29 | } 30 | 31 | func BoolFlag(value *Bool, name string, usage string) *pflag.Flag { 32 | enumValue := NewBoolValue(value) 33 | 34 | return &pflag.Flag{ 35 | Name: name, 36 | Shorthand: "", 37 | Usage: usage, 38 | Value: enumValue, 39 | DefValue: enumValue.String(), 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /pkg/config/option/bool.go: -------------------------------------------------------------------------------- 1 | package option 2 | 3 | import ( 4 | "github.com/spf13/pflag" 5 | ) 6 | 7 | type Bool struct { 8 | String 9 | } 10 | 11 | const trueStr = "true" 12 | 13 | type BoolGenerator func(flag *pflag.Flag) bool 14 | 15 | func NewBoolOption(val *bool) *Bool { 16 | value := newBoolValue(val) 17 | 18 | return &Bool{ 19 | String: *NewStringValueOption(value), 20 | } 21 | } 22 | 23 | func (option *Bool) AddFlag(name string, usage string) *pflag.Flag { 24 | return option.AddFlagShort(name, "", usage) 25 | } 26 | 27 | func (option *Bool) AddFlagShort(name string, short string, usage string) *pflag.Flag { 28 | flag := option.String.AddFlagShort(name, short, usage) 29 | 30 | flag.NoOptDefVal = trueStr 31 | 32 | return flag 33 | } 34 | 35 | func (option *Bool) ValueOr(generator BoolGenerator) string { 36 | return option.String.ValueOr(func(flag *pflag.Flag) string { 37 | value := generator(flag) 38 | 39 | if !value { 40 | return "" 41 | } 42 | 43 | return trueStr 44 | }) 45 | } 46 | -------------------------------------------------------------------------------- /pkg/config/option/count.go: -------------------------------------------------------------------------------- 1 | package option 2 | 3 | import ( 4 | "strconv" 5 | 6 | "github.com/spf13/pflag" 7 | ) 8 | 9 | type Count struct { 10 | String 11 | } 12 | 13 | type CountGenerator func(flag *pflag.Flag) int 14 | 15 | func NewCountOption(val *int) *Count { 16 | value := newCountValue(val) 17 | 18 | return &Count{ 19 | String: *NewStringValueOption(value), 20 | } 21 | } 22 | func (option *Count) AddFlag(name string, usage string) *pflag.Flag { 23 | return option.AddFlagShort(name, "", usage) 24 | } 25 | 26 | func (option *Count) AddFlagShort(name string, short string, usage string) *pflag.Flag { 27 | flag := option.String.AddFlagShort(name, short, usage) 28 | 29 | flag.NoOptDefVal = "+1" 30 | 31 | return flag 32 | } 33 | 34 | func (option *Count) ValueOr(generator CountGenerator) string { 35 | return option.String.ValueOr(func(flag *pflag.Flag) string { 36 | return strconv.Itoa(generator(flag)) 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /pkg/config/option/duration.go: -------------------------------------------------------------------------------- 1 | package option 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/spf13/pflag" 7 | ) 8 | 9 | type Duration struct { 10 | String 11 | } 12 | 13 | type DurationGenerator func(flag *pflag.Flag) time.Duration 14 | 15 | func NewDurationOption(val *time.Duration) *Duration { 16 | value := newDurationValue(val) 17 | 18 | return &Duration{ 19 | String: *NewStringValueOption(value), 20 | } 21 | } 22 | 23 | func (option *Duration) ValueOr(generator DurationGenerator) string { 24 | return option.String.ValueOr(func(flag *pflag.Flag) string { 25 | value := generator(flag).String() 26 | 27 | if value == "0s" { 28 | return "" 29 | } 30 | 31 | return value 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /pkg/config/option/group.go: -------------------------------------------------------------------------------- 1 | package option 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/util" 5 | "github.com/spf13/pflag" 6 | ) 7 | 8 | type Group struct { 9 | main *pflag.Flag 10 | flags []*pflag.Flag 11 | } 12 | 13 | func (group *Group) GetFlags() []*pflag.Flag { 14 | return group.flags 15 | } 16 | 17 | func (group *Group) GetMainFlag() *pflag.Flag { 18 | return group.main 19 | } 20 | 21 | func (group *Group) AddFlag(flag *pflag.Flag) { 22 | group.flags = append(group.flags, flag) 23 | 24 | if group.main == nil { 25 | group.main = flag 26 | } else { 27 | copyFlagExtra(group.main, flag) 28 | } 29 | } 30 | 31 | func (group *Group) GetFlag(name string) *pflag.Flag { 32 | for _, flag := range group.flags { 33 | if flag.Name == name { 34 | return flag 35 | } 36 | } 37 | 38 | return nil 39 | } 40 | 41 | func (group *Group) IsChanged() bool { 42 | for _, flag := range group.flags { 43 | if flag.Changed { 44 | return true 45 | } 46 | } 47 | 48 | return false 49 | } 50 | 51 | func (group *Group) updateFlags(value string) error { 52 | for _, flag := range group.flags { 53 | flag.Changed = true 54 | 55 | err := flag.Value.Set(value) 56 | if err != nil { 57 | return err 58 | } 59 | 60 | if required, ok := flag.Annotations[string(util.FlagRequired)]; ok { 61 | required[0] = util.StrFalse 62 | } 63 | } 64 | 65 | return nil 66 | } 67 | 68 | func copyFlagExtra(src *pflag.Flag, dst *pflag.Flag) { 69 | if src.Annotations != nil { 70 | dst.Annotations = map[string][]string{} 71 | 72 | for k, v := range src.Annotations { 73 | dst.Annotations[k] = v 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /pkg/config/option/pflag.go: -------------------------------------------------------------------------------- 1 | package option 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/spf13/pflag" 8 | ) 9 | 10 | var ( 11 | flagCount = 0 12 | 13 | flagSet = pflag.NewFlagSet("export_pflag_value", pflag.ContinueOnError) 14 | ) 15 | 16 | // unexported newStringValue() 17 | // @see https://github.com/spf13/pflag/blob/v1.0.5/string.go 18 | func newStringValue(val *string) Value { 19 | name := getNewFlagName() 20 | 21 | flagSet.StringVar(val, name, *val, "") 22 | 23 | return valueFrom(name) 24 | } 25 | 26 | // unexported newBoolValue() 27 | // @see https://github.com/spf13/pflag/blob/v1.0.5/bool.go 28 | func newBoolValue(val *bool) Value { 29 | name := getNewFlagName() 30 | 31 | flagSet.BoolVar(val, name, *val, "") 32 | 33 | return valueFrom(name) 34 | } 35 | 36 | // unexported newCountValue() 37 | // @see https://github.com/spf13/pflag/blob/v1.0.5/count.go 38 | func newCountValue(val *int) Value { 39 | name := getNewFlagName() 40 | 41 | flagSet.CountVar(val, name, "") 42 | 43 | return valueFrom(name) 44 | } 45 | 46 | // unexported newDurationValue() 47 | // @see https://github.com/spf13/pflag/blob/v1.0.5/duration.go 48 | func newDurationValue(val *time.Duration) Value { 49 | name := getNewFlagName() 50 | 51 | flagSet.DurationVar(val, name, *val, "") 52 | 53 | return valueFrom(name) 54 | } 55 | 56 | func getNewFlagName() string { 57 | flagCount++ 58 | 59 | return fmt.Sprintf("flag%d", flagCount) 60 | } 61 | 62 | func valueFrom(name string) Value { 63 | return Value{ 64 | pflag: flagSet.Lookup(name).Value, 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /pkg/config/option/value.go: -------------------------------------------------------------------------------- 1 | package option 2 | 3 | import ( 4 | "github.com/spf13/pflag" 5 | ) 6 | 7 | type Validator func(string, pflag.Value) error 8 | 9 | type Value struct { 10 | pflag pflag.Value 11 | 12 | Validator Validator 13 | } 14 | 15 | func (v Value) String() string { 16 | return v.pflag.String() 17 | } 18 | 19 | func (v Value) Type() string { 20 | return v.pflag.Type() 21 | } 22 | 23 | func (v Value) Set(data string) error { 24 | if v.Validator != nil { 25 | if err := v.Validator(data, v.pflag); err != nil { 26 | return err 27 | } 28 | } 29 | 30 | return v.pflag.Set(data) 31 | } 32 | -------------------------------------------------------------------------------- /pkg/config/settings.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type Settings struct { 8 | ConfigFile string 9 | 10 | Debug bool 11 | NoProgress bool 12 | 13 | NonInteractive bool 14 | 15 | Profile Profile 16 | 17 | Verbosity int 18 | 19 | OutputFormat string 20 | Timeout time.Duration 21 | } 22 | 23 | func NewSettings() *Settings { 24 | return &Settings{ 25 | Timeout: defaultTimeout, 26 | OutputFormat: defaultFormat, 27 | } 28 | } 29 | 30 | func (settings *Settings) IsStylish() bool { 31 | return settings.OutputFormat == "stylish" 32 | } 33 | -------------------------------------------------------------------------------- /pkg/config/types.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Context struct { 4 | Organization string `json:"organization,omitempty" yaml:"organization,omitempty"` 5 | Project string `json:"project,omitempty" yaml:"project,omitempty"` 6 | Environment string `json:"environment,omitempty" yaml:"environment,omitempty"` 7 | ServiceComponent string `json:"serviceComponent,omitempty" yaml:"serviceComponent,omitempty"` 8 | } 9 | 10 | type Profile struct { 11 | Name string `json:"-" yaml:"-"` 12 | 13 | Host string `json:"host,omitempty" yaml:"host,omitempty"` 14 | Scheme string `json:"scheme,omitempty" yaml:"scheme,omitempty"` 15 | Token string `json:"token,omitempty" yaml:"token,omitempty"` 16 | 17 | Context Context `json:"context,omitempty" yaml:"context,omitempty"` 18 | } 19 | 20 | type NamedProfiles map[string]Profile 21 | -------------------------------------------------------------------------------- /pkg/config/vars.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "errors" 5 | "time" 6 | ) 7 | 8 | const ( 9 | defaultFormat = "stylish" 10 | defaultTimeout = 30 * time.Second 11 | 12 | configDirPerm = 0o700 13 | configFilePerm = 0o600 14 | ) 15 | 16 | var ( 17 | MainManager = NewManager() 18 | 19 | Formats = []string{ 20 | "stylish", 21 | "json", 22 | "yaml", 23 | } 24 | FormatDescriptions = []string{ 25 | "stylish\tOutput format for human consumption", 26 | "json\tOutput in JSON", 27 | "yaml\tOutput in YAML", 28 | } 29 | 30 | ErrConfigExists = errors.New("configFile already exists") 31 | ErrUnknownProfile = errors.New("profile not found") 32 | ErrDuplicateProfile = errors.New("profile already exists") 33 | ErrConfigLoad = errors.New("unable to load config") 34 | ErrInvalidValue = errors.New("invalid value") 35 | ) 36 | 37 | func GetConfig() *Config { 38 | return MainManager.config 39 | } 40 | 41 | func GetOptions() *Options { 42 | return MainManager.options 43 | } 44 | 45 | func GetSettings() *Settings { 46 | return MainManager.settings 47 | } 48 | -------------------------------------------------------------------------------- /pkg/debug_component/action/action.go: -------------------------------------------------------------------------------- 1 | package action 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "bunnyshell.com/cli/pkg/remote_development/workspace" 8 | "bunnyshell.com/dev/pkg/debug" 9 | "bunnyshell.com/sdk" 10 | ) 11 | 12 | var ErrResourceKindNotSupported = errors.New("resource kind not supported") 13 | 14 | type Action struct { 15 | workspace *workspace.Workspace 16 | } 17 | 18 | func NewAction( 19 | environment sdk.EnvironmentItem, 20 | ) *Action { 21 | return &Action{ 22 | workspace: workspace.NewWorkspace(environment.GetId()), 23 | } 24 | } 25 | 26 | func (action *Action) GetDebugCmp(resource sdk.ComponentResourceItem, overrideClusterServer string) (*debug.DebugComponent, error) { 27 | kubeConfigFile, err := action.workspace.DownloadKubeConfig(overrideClusterServer) 28 | if err != nil { 29 | return nil, err 30 | } 31 | 32 | debugCmp := debug.NewDebugComponent(). 33 | WithKubernetesClient(kubeConfigFile). 34 | WithNamespaceName(resource.GetNamespace()) 35 | 36 | switch kind := resource.GetKind(); kind { 37 | case "Deployment": 38 | return debugCmp.WithDeploymentName(resource.GetName()), nil 39 | case "StatefulSet": 40 | return debugCmp.WithStatefulSetName(resource.GetName()), nil 41 | case "DaemonSet": 42 | return debugCmp.WithDaemonSetName(resource.GetName()), nil 43 | default: 44 | return nil, fmt.Errorf("%w: %s", ErrResourceKindNotSupported, kind) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /pkg/debug_component/action/down.go: -------------------------------------------------------------------------------- 1 | package action 2 | 3 | import ( 4 | "bunnyshell.com/sdk" 5 | ) 6 | 7 | type DownParameters struct { 8 | Resource sdk.ComponentResourceItem 9 | 10 | OverrideClusterServer string 11 | } 12 | 13 | type Down struct { 14 | Action 15 | } 16 | 17 | func NewDown( 18 | environment sdk.EnvironmentItem, 19 | ) *Down { 20 | return &Down{ 21 | Action: *NewAction(environment), 22 | } 23 | } 24 | 25 | func (down *Down) Run(parameters *DownParameters) error { 26 | debugCmp, err := down.Action.GetDebugCmp(parameters.Resource, parameters.OverrideClusterServer) 27 | if err != nil { 28 | return err 29 | } 30 | 31 | return debugCmp.Down() 32 | } 33 | -------------------------------------------------------------------------------- /pkg/debug_component/action/down/down.cobra.go: -------------------------------------------------------------------------------- 1 | package down 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | "github.com/spf13/pflag" 6 | ) 7 | 8 | func (down *Options) UpdateFlagSet( 9 | command *cobra.Command, 10 | flags *pflag.FlagSet, 11 | ) { 12 | flags.StringVarP(&down.resourcePath, "resource", "s", down.resourcePath, "The cluster resource to use (namespace/kind/name format).") 13 | flags.StringVar(&down.overrideClusterServer, "override-kubeconfig-cluster-server", down.overrideClusterServer, "Override kubeconfig cluster server with :port, host:port or scheme://host:port") 14 | 15 | } 16 | -------------------------------------------------------------------------------- /pkg/debug_component/action/down/down.go: -------------------------------------------------------------------------------- 1 | package down 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/debug_component/action" 5 | "bunnyshell.com/cli/pkg/k8s/bridge" 6 | ) 7 | 8 | type Options struct { 9 | ManualSelectSingleResource bool 10 | 11 | resourceLoader *bridge.ResourceLoader 12 | 13 | resourcePath string 14 | 15 | overrideClusterServer string 16 | } 17 | 18 | func NewOptions( 19 | resourceLoader *bridge.ResourceLoader, 20 | ) *Options { 21 | return &Options{ 22 | resourceLoader: resourceLoader, 23 | 24 | overrideClusterServer: "", 25 | } 26 | } 27 | 28 | func (down *Options) ToParameters() (*action.DownParameters, error) { 29 | down.resourceLoader.ManualSelectSingleResource = down.ManualSelectSingleResource 30 | 31 | if err := down.loadResource(); err != nil { 32 | return nil, err 33 | } 34 | 35 | parameters := &action.DownParameters{ 36 | Resource: *down.resourceLoader.GetResource(), 37 | OverrideClusterServer: down.overrideClusterServer, 38 | } 39 | 40 | return parameters, nil 41 | } 42 | 43 | func (down *Options) loadResource() error { 44 | if !down.resourceLoader.IsLoaded() { 45 | return ErrResourceLoaderNotHydrated 46 | } 47 | 48 | if down.resourceLoader.GetResource() != nil { 49 | return nil 50 | } 51 | 52 | if down.resourcePath != "" { 53 | return down.resourceLoader.SelectResourceFromString(down.resourcePath) 54 | } 55 | 56 | return down.resourceLoader.SelectResource() 57 | } 58 | -------------------------------------------------------------------------------- /pkg/debug_component/action/down/vars.go: -------------------------------------------------------------------------------- 1 | package down 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | var ErrResourceLoaderNotHydrated = errors.New("resourceLoader needs to be hydrated") 8 | -------------------------------------------------------------------------------- /pkg/debug_component/action/up/vars.go: -------------------------------------------------------------------------------- 1 | package up 2 | 3 | import ( 4 | "errors" 5 | "time" 6 | ) 7 | 8 | const defaultWaitTimeout = 120 * time.Second 9 | 10 | var ( 11 | ErrResourceLoaderNotHydrated = errors.New("resourceLoader needs to be hydrated") 12 | ) 13 | -------------------------------------------------------------------------------- /pkg/debug_component/action/vars.go: -------------------------------------------------------------------------------- 1 | package action 2 | 3 | import "errors" 4 | 5 | var ( 6 | ErrDebugCmpNotInitialized = errors.New("call Up.Run() successfully before calling other methods") 7 | ) 8 | -------------------------------------------------------------------------------- /pkg/debug_component/debug_component.go: -------------------------------------------------------------------------------- 1 | package debug_component 2 | 3 | import ( 4 | "fmt" 5 | 6 | "bunnyshell.com/cli/pkg/environment" 7 | "bunnyshell.com/dev/pkg/debug" 8 | ) 9 | 10 | var ( 11 | ErrNoOrganizationSelected = fmt.Errorf("you need to select an organization first") 12 | ) 13 | 14 | type DebugComponent struct { 15 | debugCmp *debug.DebugComponent 16 | environmentResource *environment.EnvironmentResource 17 | 18 | environmentWorkspaceDir string 19 | 20 | kubeConfigPath string 21 | 22 | waitTimeout int64 23 | } 24 | 25 | func NewDebugComponent() *DebugComponent { 26 | return &DebugComponent{ 27 | debugCmp: debug.NewDebugComponent(), 28 | environmentResource: environment.NewEnvironmentResource(), 29 | } 30 | } 31 | 32 | func (d *DebugComponent) WithEnvironmentResource(environmentResource *environment.EnvironmentResource) *DebugComponent { 33 | d.environmentResource = environmentResource 34 | 35 | return d 36 | } 37 | 38 | func (d *DebugComponent) WithEnvironmentWorkspaceDir(environmentWorkspaceDir string) *DebugComponent { 39 | d.environmentWorkspaceDir = environmentWorkspaceDir 40 | 41 | return d 42 | } 43 | 44 | func (d *DebugComponent) WithKubeConfigPath(kubeConfigPath string) *DebugComponent { 45 | d.kubeConfigPath = kubeConfigPath 46 | 47 | return d 48 | } 49 | 50 | func (d *DebugComponent) WithWaitTimeout(waitTimeout int64) *DebugComponent { 51 | d.waitTimeout = waitTimeout 52 | 53 | return d 54 | } 55 | -------------------------------------------------------------------------------- /pkg/debug_component/workspace.go: -------------------------------------------------------------------------------- 1 | package debug_component 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | 7 | "bunnyshell.com/cli/pkg/lib" 8 | "bunnyshell.com/cli/pkg/util" 9 | ) 10 | 11 | const ( 12 | KubeConfigFilename = "kube-config.yaml" 13 | ) 14 | 15 | // @deprecated Use bunnyshell.com/cli/pkg/remote_development/workspace 16 | func (d *DebugComponent) ensureEnvironmentWorkspaceDir() error { 17 | workspace, err := util.GetWorkspaceDir() 18 | if err != nil { 19 | return err 20 | } 21 | 22 | d.WithEnvironmentWorkspaceDir(filepath.Join(workspace, d.environmentResource.Environment.GetId())) 23 | 24 | return os.MkdirAll(d.environmentWorkspaceDir, 0755) 25 | } 26 | 27 | // @deprecated Use bunnyshell.com/cli/pkg/remote_development/workspace 28 | func (d *DebugComponent) ensureEnvironmentKubeConfig() error { 29 | kubeConfigPath := filepath.Join(d.environmentWorkspaceDir, KubeConfigFilename) 30 | if err := lib.DownloadEnvironmentKubeConfig(kubeConfigPath, d.environmentResource.Environment.GetId()); err != nil { 31 | return err 32 | } 33 | 34 | d.WithKubeConfigPath(kubeConfigPath) 35 | 36 | return nil 37 | } 38 | -------------------------------------------------------------------------------- /pkg/formatter/formatter.go: -------------------------------------------------------------------------------- 1 | package formatter 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | 8 | "gopkg.in/yaml.v3" 9 | ) 10 | 11 | var errUnknownFormat = errors.New("unknown format") 12 | 13 | func Formatter(data interface{}, format string) ([]byte, error) { 14 | if format == "stylish" { 15 | return stylish(data) 16 | } 17 | 18 | switch format { 19 | case "json": 20 | return JSONFormatter(data) 21 | case "yaml", "yml": 22 | return YAMLFormatter(data) 23 | } 24 | 25 | return nil, fmt.Errorf("%w: %s", errUnknownFormat, format) 26 | } 27 | 28 | func JSONFormatter(data interface{}) ([]byte, error) { 29 | return json.MarshalIndent(data, "", " ") 30 | } 31 | 32 | func YAMLFormatter(data interface{}) ([]byte, error) { 33 | return yaml.Marshal(data) 34 | } 35 | -------------------------------------------------------------------------------- /pkg/formatter/stylish.endpoints.go: -------------------------------------------------------------------------------- 1 | package formatter 2 | 3 | import ( 4 | "fmt" 5 | "text/tabwriter" 6 | 7 | "bunnyshell.com/sdk" 8 | ) 9 | 10 | func tabulateAggregateEndpoint(writer *tabwriter.Writer, components []sdk.ComponentEndpointCollection) { 11 | if len(components) == 0 { 12 | fmt.Fprintln(writer, "Environment has no defined public endpoints") 13 | 14 | return 15 | } 16 | 17 | for index, item := range components { 18 | if index != 0 { 19 | fmt.Fprintln(writer) 20 | } 21 | 22 | fmt.Fprintf(writer, "%v\t %v\n", "Name", item.GetName()) 23 | 24 | for index, endpoint := range item.GetEndpoints() { 25 | if index == 0 { 26 | fmt.Fprintf(writer, "%v\t %v\n", "Endpoints", endpoint) 27 | } else { 28 | fmt.Fprintf(writer, "\t %v\n", endpoint) 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /pkg/formatter/stylish.k8s.go: -------------------------------------------------------------------------------- 1 | package formatter 2 | 3 | import ( 4 | "fmt" 5 | "text/tabwriter" 6 | 7 | "bunnyshell.com/sdk" 8 | ) 9 | 10 | func tabulateKubernetesCollection(w *tabwriter.Writer, data *sdk.PaginatedKubernetesIntegrationCollection) { 11 | fmt.Fprintf(w, "%v\t %v\t %v\t %v\t %v\t %v\n", "K8SIntegrationID", "OrganizationID", "Cloud", "Provider", "Cluster", "Status") 12 | 13 | if data.Embedded != nil { 14 | for _, item := range data.Embedded.Item { 15 | fmt.Fprintf( 16 | w, 17 | "%v\t %v\t %v\t %v\t %v\t %v\n", 18 | item.GetId(), 19 | item.GetOrganization(), 20 | item.GetCloudName(), 21 | item.GetCloudProvider(), 22 | item.GetClusterName(), 23 | item.GetStatus(), 24 | ) 25 | } 26 | } 27 | } 28 | 29 | func tabulateKubernetesItem(w *tabwriter.Writer, item *sdk.KubernetesIntegrationItem) { 30 | fmt.Fprintf(w, "%v\t %v\n", "K8SIntegrationID", item.GetId()) 31 | fmt.Fprintf(w, "%v\t %v\n", "OrganizationID", item.GetOrganization()) 32 | fmt.Fprintf(w, "%v\t %v\n", "Cloud", item.GetCloudName()) 33 | fmt.Fprintf(w, "%v\t %v\n", "Provider", item.GetCloudProvider()) 34 | fmt.Fprintf(w, "%v\t %v\n", "Cluster", item.GetClusterName()) 35 | fmt.Fprintf(w, "%v\t %v\n", "Status", item.GetStatus()) 36 | } 37 | -------------------------------------------------------------------------------- /pkg/formatter/stylish.registry_integration.go: -------------------------------------------------------------------------------- 1 | package formatter 2 | 3 | import ( 4 | "fmt" 5 | "text/tabwriter" 6 | 7 | "bunnyshell.com/sdk" 8 | ) 9 | 10 | func tabulateRegistryIntegrationsCollection(w *tabwriter.Writer, data *sdk.PaginatedRegistryIntegrationCollection) { 11 | fmt.Fprintf(w, "%v\t %v\t %v\t %v\t %v\n", "IntegrationID", "OrganizationID", "Name", "Provider", "Status") 12 | 13 | if data.Embedded != nil { 14 | for _, item := range data.Embedded.Item { 15 | fmt.Fprintf( 16 | w, 17 | "%v\t %v\t %v\t %v\t %v\n", 18 | item.GetId(), 19 | item.GetOrganization(), 20 | item.GetName(), 21 | item.GetProviderName(), 22 | item.GetStatus(), 23 | ) 24 | } 25 | } 26 | } 27 | 28 | func tabulateRegistryIntegrationItem(w *tabwriter.Writer, item *sdk.RegistryIntegrationItem) { 29 | fmt.Fprintf(w, "%v\t %v\n", "IntegrationID", item.GetId()) 30 | fmt.Fprintf(w, "%v\t %v\n", "OrganizationID", item.GetOrganization()) 31 | fmt.Fprintf(w, "%v\t %v\n", "Name", item.GetName()) 32 | fmt.Fprintf(w, "%v\t %v\n", "Provider", item.GetProviderName()) 33 | fmt.Fprintf(w, "%v\t %v\n", "Status", item.GetStatus()) 34 | } 35 | -------------------------------------------------------------------------------- /pkg/formatter/stylish.secret.go: -------------------------------------------------------------------------------- 1 | package formatter 2 | 3 | import ( 4 | "fmt" 5 | "text/tabwriter" 6 | 7 | "bunnyshell.com/sdk" 8 | ) 9 | 10 | func tabulateSecretEncryptedItem(writer *tabwriter.Writer, item *sdk.SecretEncryptedItem) { 11 | fmt.Fprintf(writer, "%v\t %v\n", "Expression", item.GetExpression()) 12 | } 13 | 14 | func tabulateSecretDecryptedItem(writer *tabwriter.Writer, item *sdk.SecretDecryptedItem) { 15 | fmt.Fprintf(writer, "%v\t %v\n", "Value", item.GetPlainText()) 16 | } 17 | -------------------------------------------------------------------------------- /pkg/formatter/stylish.template.go: -------------------------------------------------------------------------------- 1 | package formatter 2 | 3 | import ( 4 | "fmt" 5 | "text/tabwriter" 6 | 7 | "bunnyshell.com/sdk" 8 | ) 9 | 10 | func tabulateTemplateCollection(writer *tabwriter.Writer, data *sdk.PaginatedTemplateCollection) { 11 | fmt.Fprintf(writer, "%v\t %v\t %v\t %v\t %v\t %v\n", "TemplateID", "OrganizationID", "TemplatesRepositoryID", "Key", "Name", "Git SHA") 12 | 13 | if data.Embedded != nil { 14 | for _, item := range data.Embedded.Item { 15 | fmt.Fprintf( 16 | writer, 17 | "%v\t %v\t %v\t %v\t %v\t %v\n", 18 | item.GetId(), 19 | item.GetOrganization(), 20 | item.GetTemplatesRepository(), 21 | item.GetKey(), 22 | item.GetName(), 23 | item.GetGitSha(), 24 | ) 25 | } 26 | } 27 | } 28 | 29 | func tabulateTemplateItem(writer *tabwriter.Writer, item *sdk.TemplateItem) { 30 | fmt.Fprintf(writer, "%v\t %v\n", "TemplateID", item.GetId()) 31 | fmt.Fprintf(writer, "%v\t %v\n", "OrganizationID", item.GetOrganization()) 32 | fmt.Fprintf(writer, "%v\t %v\n", "TemplatesRepositoryID", item.GetTemplatesRepository()) 33 | fmt.Fprintf(writer, "%v\t %v\n", "Key", item.GetKey()) 34 | fmt.Fprintf(writer, "%v\t %v\n", "Name", item.GetName()) 35 | fmt.Fprintf(writer, "%v\t %v\n", "Short Description", item.GetShortDescription()) 36 | fmt.Fprintf(writer, "%v\t %v\n", "Git SHA", item.GetGitSha()) 37 | 38 | for index, tag := range item.GetTags() { 39 | if index == 0 { 40 | fmt.Fprintf(writer, "\n") 41 | fmt.Fprintf(writer, "%v\t %v\n", "Tags", tag) 42 | } else { 43 | fmt.Fprintf(writer, "\t %v\n", tag) 44 | } 45 | } 46 | 47 | tabulateTemplateVariableFromItem(writer, item) 48 | } 49 | -------------------------------------------------------------------------------- /pkg/formatter/stylish.template.repository.go: -------------------------------------------------------------------------------- 1 | package formatter 2 | 3 | import ( 4 | "fmt" 5 | "text/tabwriter" 6 | 7 | "bunnyshell.com/sdk" 8 | ) 9 | 10 | func tabulateTemplatesRepositoryCollection(writer *tabwriter.Writer, data *sdk.PaginatedTemplatesRepositoryCollection) { 11 | fmt.Fprintf(writer, "%v\t %v\t %v\t %v\t %v\t %v\n", "TemplateRepositoryID", "OrganizationID", "Name", "Repository", "Branch", "LastSyncSHA") 12 | 13 | if data.Embedded != nil { 14 | for _, item := range data.Embedded.Item { 15 | fmt.Fprintf( 16 | writer, 17 | "%v\t %v\t %v\t %v\t %v\t %v\n", 18 | item.GetId(), 19 | item.GetOrganization(), 20 | item.GetName(), 21 | item.GetGitRepositoryUrl(), 22 | item.GetGitRef(), 23 | item.GetLastSyncSha(), 24 | ) 25 | } 26 | } 27 | } 28 | 29 | func tabulateTemplatesRepositoryItem(writer *tabwriter.Writer, item *sdk.TemplatesRepositoryItem) { 30 | fmt.Fprintf(writer, "%v\t %v\n", "TemplateRepositoryID", item.GetId()) 31 | fmt.Fprintf(writer, "%v\t %v\n", "OrganizationID", item.GetOrganization()) 32 | fmt.Fprintf(writer, "%v\t %v\n", "Name", item.GetName()) 33 | fmt.Fprintf(writer, "%v\t %v\n", "Repository", item.GetGitRepositoryUrl()) 34 | fmt.Fprintf(writer, "%v\t %v\n", "Branch", item.GetGitRef()) 35 | fmt.Fprintf(writer, "%v\t %v\n", "LastSyncSHA", item.GetLastSyncSha()) 36 | } 37 | -------------------------------------------------------------------------------- /pkg/formatter/stylish.variable.groups.go: -------------------------------------------------------------------------------- 1 | package formatter 2 | 3 | import ( 4 | "fmt" 5 | "text/tabwriter" 6 | 7 | "bunnyshell.com/sdk" 8 | ) 9 | 10 | func tabulateEnvironCollection(w *tabwriter.Writer, data *sdk.PaginatedEnvironItemCollection) { 11 | fmt.Fprintf(w, "%v\t %v\t %v\t %v\t %v\n", "EnvVarID", "EnvironmentID", "OrganizationID", "Group", "Name") 12 | 13 | if data.Embedded != nil { 14 | for _, item := range data.Embedded.Item { 15 | fmt.Fprintf( 16 | w, 17 | "%v\t %v\t %v\t %v\t %v\n", 18 | item.GetId(), 19 | item.GetEnvironment(), 20 | item.GetOrganization(), 21 | item.GetGroupName(), 22 | item.GetName(), 23 | ) 24 | } 25 | } 26 | } 27 | 28 | func tabulateEnvironItem(w *tabwriter.Writer, item *sdk.EnvironItemItem) { 29 | fmt.Fprintf(w, "%v\t %v\n", "EnvironmentVariableID", item.GetId()) 30 | fmt.Fprintf(w, "%v\t %v\n", "EnvironmentID", item.GetEnvironment()) 31 | fmt.Fprintf(w, "%v\t %v\n", "OrganizationID", item.GetOrganization()) 32 | fmt.Fprintf(w, "%v\t %v\n", "Group", item.GetGroupName()) 33 | fmt.Fprintf(w, "%v\t %v\n", "Name", item.GetName()) 34 | fmt.Fprintf(w, "%v\t %v\n", "Value", item.GetValue()) 35 | fmt.Fprintf(w, "%v\t %v\n", "Secret", item.GetSecret()) 36 | } 37 | -------------------------------------------------------------------------------- /pkg/helper/git/git_spec.go: -------------------------------------------------------------------------------- 1 | package git 2 | 3 | import ( 4 | "errors" 5 | "net/url" 6 | "strings" 7 | ) 8 | 9 | var errEmptySpec = errors.New("empty spec") 10 | 11 | func ParseGitSec(spec string) (string, string, error) { 12 | if len(spec) == 0 { 13 | return "", "", errEmptySpec 14 | } 15 | 16 | if spec[0] == '@' { 17 | return "", spec[1:], nil 18 | } 19 | 20 | info, err := url.Parse(spec) 21 | if err != nil { 22 | return "", "", err 23 | } 24 | 25 | if !strings.Contains(info.Path, "@") { 26 | return spec, "", nil 27 | } 28 | 29 | chunks := strings.SplitN(info.Path, "@", 2) 30 | info.Path = chunks[0] 31 | 32 | return info.String(), chunks[1], nil 33 | } 34 | -------------------------------------------------------------------------------- /pkg/helper/rdev/data.go: -------------------------------------------------------------------------------- 1 | package rdev 2 | 3 | import ( 4 | "path/filepath" 5 | ) 6 | 7 | type Data struct { 8 | Directory string 9 | } 10 | 11 | func (d Data) Basename() string { 12 | return filepath.Base(d.Directory) 13 | } 14 | -------------------------------------------------------------------------------- /pkg/helper/rdev/generate/rdev.yaml: -------------------------------------------------------------------------------- 1 | profiles: 2 | # a unique name for the remote dev profile 3 | name: 4 | # The command to run when starting the container 5 | command: [yarn, install] 6 | # Sync paths between the container and the host 7 | syncPaths: 8 | - 9 | # absolute path within the container 10 | remotePath: /var/www 11 | # absolute path or relative to the rdev.yaml file 12 | localPath: .. 13 | portForwards: 14 | - "9001<9001" 15 | - "9003>9003" 16 | environment: 17 | ENV: dev 18 | resources: 19 | # https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/ 20 | limits: 21 | cpu: 123m 22 | memory: 351Mi 23 | requests: 24 | cpu: 101m 25 | memory: 349Mi 26 | -------------------------------------------------------------------------------- /pkg/helper/rdev/loader.go: -------------------------------------------------------------------------------- 1 | package rdev 2 | 3 | import ( 4 | "embed" 5 | "io/fs" 6 | "path/filepath" 7 | "text/template" 8 | ) 9 | 10 | var ( 11 | rootDir = "generate" 12 | 13 | //go:embed generate/* 14 | files embed.FS 15 | 16 | templates map[string]*template.Template 17 | ) 18 | 19 | func GetTemplate(path string) (*template.Template, error) { 20 | if templates == nil { 21 | templates = make(map[string]*template.Template) 22 | } 23 | 24 | if templates[path] == nil { 25 | parsedTemplate, err := template.ParseFS(files, rootDir+"/"+path) 26 | if err != nil { 27 | return nil, err 28 | } 29 | 30 | templates[path] = parsedTemplate 31 | } 32 | 33 | return templates[path], nil 34 | } 35 | 36 | func getAllFilenames() ([]string, error) { 37 | return getFilenamesIn(rootDir) 38 | } 39 | 40 | func getFilenamesIn(rootDir string) ([]string, error) { 41 | entries := make([]string, 0) 42 | 43 | if err := fs.WalkDir(files, rootDir, func(fileName string, d fs.DirEntry, err error) error { 44 | if err != nil { 45 | return err 46 | } 47 | 48 | if d.IsDir() { 49 | return nil 50 | } 51 | 52 | relativePath, err := filepath.Rel(rootDir, fileName) 53 | if err != nil { 54 | return err 55 | } 56 | 57 | entries = append(entries, relativePath) 58 | 59 | return nil 60 | }); err != nil { 61 | return nil, err 62 | } 63 | 64 | return entries, nil 65 | } 66 | -------------------------------------------------------------------------------- /pkg/helper/template/data.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "path/filepath" 5 | ) 6 | 7 | type Data struct { 8 | Directory string 9 | } 10 | 11 | func (d Data) Basename() string { 12 | return filepath.Base(d.Directory) 13 | } 14 | -------------------------------------------------------------------------------- /pkg/helper/template/generate/README.md: -------------------------------------------------------------------------------- 1 | # Bunnyshell Template for {{ .Basename }} 2 | 3 | ## Topic 4 | 5 | This template is for {{ .Basename }} 6 | 7 | ## What's it about 8 | 9 | Building fast, so we can easily test. 10 | -------------------------------------------------------------------------------- /pkg/helper/template/generate/bunnyshell.yaml: -------------------------------------------------------------------------------- 1 | # https://documentation.bunnyshell.com/docs/templates-prepare-repository-for-custom-templates#bunnyshellyaml 2 | kind: Environment 3 | name: {{ .Basename }} 4 | type: primary 5 | components: [] 6 | volumes: [] 7 | -------------------------------------------------------------------------------- /pkg/helper/template/generate/template.yaml: -------------------------------------------------------------------------------- 1 | # https://documentation.bunnyshell.com/docs/templates-prepare-repository-for-custom-templates#templateyaml 2 | name: bunnyshell - {{ .Basename }} 3 | description: Bunnyshell template for {{ .Basename }} 4 | discoverable: true 5 | -------------------------------------------------------------------------------- /pkg/helper/template/loader.go: -------------------------------------------------------------------------------- 1 | package template 2 | 3 | import ( 4 | "embed" 5 | "io/fs" 6 | "path/filepath" 7 | "text/template" 8 | ) 9 | 10 | var ( 11 | rootDir = "generate" 12 | 13 | //go:embed generate/* 14 | files embed.FS 15 | 16 | templates map[string]*template.Template 17 | ) 18 | 19 | func GetTemplate(path string) (*template.Template, error) { 20 | if templates == nil { 21 | templates = make(map[string]*template.Template) 22 | } 23 | 24 | if templates[path] == nil { 25 | parsedTemplate, err := template.ParseFS(files, rootDir+"/"+path) 26 | if err != nil { 27 | return nil, err 28 | } 29 | 30 | templates[path] = parsedTemplate 31 | } 32 | 33 | return templates[path], nil 34 | } 35 | 36 | func getAllFilenames() ([]string, error) { 37 | return getFilenamesIn(rootDir) 38 | } 39 | 40 | func getFilenamesIn(rootDir string) ([]string, error) { 41 | entries := make([]string, 0) 42 | 43 | if err := fs.WalkDir(files, rootDir, func(fileName string, d fs.DirEntry, err error) error { 44 | if err != nil { 45 | return err 46 | } 47 | 48 | if d.IsDir() { 49 | return nil 50 | } 51 | 52 | relativePath, err := filepath.Rel(rootDir, fileName) 53 | if err != nil { 54 | return err 55 | } 56 | 57 | entries = append(entries, relativePath) 58 | 59 | return nil 60 | }); err != nil { 61 | return nil, err 62 | } 63 | 64 | return entries, nil 65 | } 66 | -------------------------------------------------------------------------------- /pkg/interactive/question.go: -------------------------------------------------------------------------------- 1 | package interactive 2 | 3 | import "github.com/AlecAivazis/survey/v2" 4 | 5 | type Input struct { 6 | survey.Input 7 | 8 | validate survey.Validator 9 | } 10 | 11 | func NewInput(message string) *Input { 12 | return &Input{ 13 | Input: survey.Input{ 14 | Message: message, 15 | }, 16 | } 17 | } 18 | 19 | func (question *Input) AskString() (string, error) { 20 | var answer string 21 | 22 | return answer, askPrompt(&question.Input, &answer, question.validate) 23 | } 24 | 25 | func (question *Input) SetValidate(validate survey.Validator) *Input { 26 | question.validate = validate 27 | 28 | return question 29 | } 30 | -------------------------------------------------------------------------------- /pkg/interactive/validations.go: -------------------------------------------------------------------------------- 1 | package interactive 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "strings" 7 | 8 | "github.com/AlecAivazis/survey/v2" 9 | ) 10 | 11 | func All(funcs ...survey.Validator) survey.Validator { 12 | return func(input interface{}) error { 13 | for _, callable := range funcs { 14 | err := callable(input) 15 | if err != nil { 16 | return err 17 | } 18 | } 19 | 20 | return nil 21 | } 22 | } 23 | 24 | func Lowercase() survey.Validator { 25 | return func(input interface{}) error { 26 | str, ok := input.(string) 27 | if !ok { 28 | return ErrInvalidValue 29 | } 30 | 31 | if strings.ToLower(str) != input { 32 | return fmt.Errorf("%w: must be lowercase", ErrInvalidValue) 33 | } 34 | 35 | return nil 36 | } 37 | } 38 | 39 | func AssertBetween(min int32, max int32) survey.Validator { 40 | return func(input interface{}) error { 41 | str, ok := input.(string) 42 | if !ok { 43 | return ErrInvalidValue 44 | } 45 | 46 | i, err := strconv.ParseInt(str, 10, 32) 47 | if err != nil { 48 | return fmt.Errorf("%w: must be an integer", ErrInvalidValue) 49 | } 50 | 51 | val := int32(i) 52 | if val < min || val > max { 53 | return fmt.Errorf("%w: must be between %d and %d", ErrInvalidValue, min, max) 54 | } 55 | 56 | return nil 57 | } 58 | } 59 | 60 | func AssertMinimumLength(length int) survey.Validator { 61 | return survey.MinLength(length) 62 | } 63 | -------------------------------------------------------------------------------- /pkg/interactive/vars.go: -------------------------------------------------------------------------------- 1 | package interactive 2 | 3 | import ( 4 | "errors" 5 | 6 | "bunnyshell.com/cli/pkg/config" 7 | ) 8 | 9 | var ( 10 | ErrInvalidValue = errors.New("invalid value") 11 | ErrRequiredValue = errors.New("required value") 12 | ErrNonInteractive = errors.New("refusing to run with non-interactive flag") 13 | ) 14 | 15 | func getSettings() *config.Settings { 16 | return config.GetSettings() 17 | } 18 | -------------------------------------------------------------------------------- /pkg/k8s/bridge/resource.spec.go: -------------------------------------------------------------------------------- 1 | package bridge 2 | 3 | import ( 4 | "errors" 5 | "regexp" 6 | "strings" 7 | 8 | "bunnyshell.com/sdk" 9 | ) 10 | 11 | var resourceSpecRegex = regexp.MustCompile(`^(?P[a-z0-9-]+)/(?P[a-z0-9-]+)/(?P[a-z0-9-]+)$`) 12 | 13 | var ErrInvalidResourceSpec = errors.New("invalid resource spec") 14 | 15 | type ResourceSpec struct { 16 | Namespace string 17 | Kind string 18 | Name string 19 | } 20 | 21 | func NewResourceSpec(spec string) *ResourceSpec { 22 | match := resourceSpecRegex.FindStringSubmatch(spec) 23 | 24 | if len(match) == 0 { 25 | return nil 26 | } 27 | 28 | return &ResourceSpec{ 29 | Namespace: match[resourceSpecRegex.SubexpIndex("namespace")], 30 | Kind: match[resourceSpecRegex.SubexpIndex("kind")], 31 | Name: match[resourceSpecRegex.SubexpIndex("name")], 32 | } 33 | } 34 | 35 | func (r *ResourceSpec) Match(resource sdk.ComponentResourceItem) bool { 36 | return r.Namespace == resource.GetNamespace() && 37 | r.Kind == strings.ToLower(resource.GetKind()) && 38 | r.Name == resource.GetName() 39 | } 40 | 41 | func (r *ResourceSpec) MatchString(spec string) bool { 42 | resourceSpec := NewResourceSpec(spec) 43 | 44 | return r.Namespace == resourceSpec.Namespace && 45 | r.Kind == resourceSpec.Kind && 46 | r.Name == resourceSpec.Name 47 | } 48 | 49 | func (r *ResourceSpec) String() string { 50 | return r.Namespace + "/" + r.Kind + "/" + r.Name 51 | } 52 | -------------------------------------------------------------------------------- /pkg/k8s/bridge/vars.go: -------------------------------------------------------------------------------- 1 | package bridge 2 | 3 | import "errors" 4 | 5 | var ( 6 | ErrNotLoaded = errors.New("bunnyshell not loaded") 7 | 8 | ErrResourceNotFound = errors.New("resource %s not found") 9 | 10 | ErrNoComponentResources = errors.New("no component resources available") 11 | ) 12 | -------------------------------------------------------------------------------- /pkg/k8s/port_forward.go: -------------------------------------------------------------------------------- 1 | package k8s 2 | 3 | type PortForward struct { 4 | Interface string 5 | 6 | RemotePort int 7 | LocalPort int 8 | 9 | StopChannel chan struct{} 10 | ReadyChannel chan struct{} 11 | } 12 | 13 | func NewPortForward(iface string, localPort int, remotePort int) *PortForward { 14 | return &PortForward{ 15 | Interface: iface, 16 | 17 | RemotePort: remotePort, 18 | LocalPort: localPort, 19 | 20 | StopChannel: make(chan struct{}), 21 | ReadyChannel: make(chan struct{}, 1), 22 | } 23 | } 24 | 25 | func (p *PortForward) Close() { 26 | if p.StopChannel != nil { 27 | close(p.StopChannel) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /pkg/lib/apiclient.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "io" 5 | "os" 6 | ) 7 | 8 | func DownloadEnvironmentKubeConfig(kubeConfigPath, environmentID string) error { 9 | kubeConfigFile, err := os.Create(kubeConfigPath) 10 | if err != nil { 11 | return err 12 | } 13 | defer kubeConfigFile.Close() 14 | 15 | ctx, cancel := GetContext() 16 | defer cancel() 17 | 18 | request := GetAPI().EnvironmentAPI.EnvironmentKubeConfig(ctx, environmentID) 19 | 20 | _, resp, err := request.Execute() 21 | if err != nil && err.Error() != "undefined response type" { 22 | return err 23 | } 24 | 25 | _, err = io.Copy(kubeConfigFile, resp.Body) 26 | 27 | return err 28 | } 29 | -------------------------------------------------------------------------------- /pkg/lib/command.go: -------------------------------------------------------------------------------- 1 | package lib 2 | 3 | import ( 4 | "errors" 5 | 6 | "bunnyshell.com/cli/pkg/config" 7 | "github.com/spf13/cobra" 8 | ) 9 | 10 | var ErrNotStylish = errors.New("only stylish format is supported") 11 | 12 | func OnlyStylish(cmd *cobra.Command, args []string) error { 13 | if config.GetSettings().IsStylish() { 14 | return nil 15 | } 16 | 17 | return ErrNotStylish 18 | } 19 | -------------------------------------------------------------------------------- /pkg/net/client.go: -------------------------------------------------------------------------------- 1 | package net 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/briandowns/spinner" 7 | ) 8 | 9 | type SpinnerTransport struct { 10 | Disabled bool 11 | 12 | Proxied http.RoundTripper 13 | } 14 | 15 | var DefaultSpinnerTransport = SpinnerTransport{ 16 | Disabled: false, 17 | Proxied: http.DefaultTransport, 18 | } 19 | 20 | func (st SpinnerTransport) RoundTrip(req *http.Request) (*http.Response, error) { 21 | if !st.Disabled { 22 | spinner := MakeSpinner() 23 | 24 | spinner.Start() 25 | 26 | defer spinner.Stop() 27 | } 28 | 29 | return st.Proxied.RoundTrip(req) 30 | } 31 | 32 | func GetCLIClient() *http.Client { 33 | return &http.Client{ 34 | Transport: DefaultSpinnerTransport, 35 | } 36 | } 37 | 38 | func PauseSpinner() func() { 39 | prev := DefaultSpinnerTransport.Disabled 40 | DefaultSpinnerTransport.Disabled = true 41 | 42 | return func() { 43 | DefaultSpinnerTransport.Disabled = prev 44 | } 45 | } 46 | 47 | func MakeSpinner() *spinner.Spinner { 48 | s := spinner.New(spinner.CharSets[defaultSpinner], defaultDuration) 49 | s.Suffix = " Fetching API data..." 50 | 51 | return s 52 | } 53 | -------------------------------------------------------------------------------- /pkg/net/vars.go: -------------------------------------------------------------------------------- 1 | package net 2 | 3 | import "time" 4 | 5 | const ( 6 | defaultDuration = 150 * time.Millisecond 7 | defaultSpinner = 69 // ●∙∙ 8 | ) 9 | -------------------------------------------------------------------------------- /pkg/port_forward/main.go: -------------------------------------------------------------------------------- 1 | package port_forward 2 | 3 | import "regexp" 4 | 5 | var PortMappingExp = regexp.MustCompile("^(?P[1-9][0-9]*)?(:(?P[1-9][0-9]*))?$") 6 | -------------------------------------------------------------------------------- /pkg/port_forward/workspace/vars.go: -------------------------------------------------------------------------------- 1 | package workspace 2 | 3 | const ( 4 | workspacePerm = 0o700 5 | 6 | kubeConfigPerm = 0o600 7 | 8 | kubeConfigFilename = "kube-config.yaml" 9 | ) 10 | -------------------------------------------------------------------------------- /pkg/progress/event.go: -------------------------------------------------------------------------------- 1 | package progress 2 | 3 | import ( 4 | "time" 5 | 6 | "bunnyshell.com/cli/pkg/api/pipeline" 7 | "bunnyshell.com/cli/pkg/net" 8 | "bunnyshell.com/sdk" 9 | ) 10 | 11 | func EventToPipeline(event *sdk.EventItem, options *Options) (*sdk.PipelineItem, error) { 12 | resume := net.PauseSpinner() 13 | defer resume() 14 | 15 | spinner := net.MakeSpinner() 16 | 17 | spinner.Start() 18 | defer spinner.Stop() 19 | 20 | event, err := handleWorkflow(event, options, spinner) 21 | if err != nil { 22 | return nil, err 23 | } 24 | 25 | return handlePipeline(event, options) 26 | } 27 | 28 | func handlePipeline(event *sdk.EventItem, options *Options) (*sdk.PipelineItem, error) { 29 | listOptions := pipeline.NewListOptions() 30 | listOptions.Event = event.GetId() 31 | 32 | for { 33 | collection, err := pipeline.List(listOptions) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | if !collection.HasEmbedded() { 39 | time.Sleep(options.Interval) 40 | 41 | continue 42 | } 43 | 44 | itemOptions := pipeline.NewItemOptions(collection.Embedded.GetItem()[0].GetId()) 45 | 46 | return pipeline.Get(itemOptions) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /pkg/progress/pipeline.go: -------------------------------------------------------------------------------- 1 | package progress 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/api/pipeline" 5 | "bunnyshell.com/cli/pkg/net" 6 | "bunnyshell.com/sdk" 7 | ) 8 | 9 | func Pipeline(pipelineID string, options *Options) error { 10 | if options == nil { 11 | options = NewOptions() 12 | } 13 | 14 | resume := net.PauseSpinner() 15 | defer resume() 16 | 17 | return progress(*options, generatorFromID(pipelineID)) 18 | } 19 | 20 | func progress(options Options, generate PipelineSyncer) error { 21 | wizard := NewPipeline(options) 22 | 23 | wizard.Start() 24 | defer wizard.Stop() 25 | 26 | return wizard.Update(generate) 27 | } 28 | 29 | func generatorFromID(id string) PipelineSyncer { 30 | itemOptions := pipeline.NewItemOptions(id) 31 | 32 | return func() (*sdk.PipelineItem, error) { 33 | return pipeline.Get(itemOptions) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /pkg/progress/vars.go: -------------------------------------------------------------------------------- 1 | package progress 2 | 3 | import ( 4 | "errors" 5 | "time" 6 | 7 | "github.com/fatih/color" 8 | ) 9 | 10 | type ( 11 | UpdateStatus int 12 | 13 | PipelineStatus int 14 | ) 15 | 16 | const ( 17 | Failed UpdateStatus = iota 18 | Success 19 | 20 | Synced 21 | ) 22 | 23 | const ( 24 | PipelineWorking PipelineStatus = iota 25 | PipelineFinished 26 | PipelineFailed 27 | PipelineUnknownState 28 | ) 29 | 30 | const ( 31 | StatusSuccess = "success" 32 | StatusFailed = "failed" 33 | StatusInProgress = "in_progress" 34 | StatusPending = "pending" 35 | ) 36 | 37 | const ( 38 | defaultSpinnerUpdate = 150 * time.Millisecond 39 | defaultProgressSet = 69 // ∙∙● 40 | ) 41 | 42 | var statusMap = map[PipelineStatus]string{ 43 | PipelineWorking: color.New(color.FgCyan).Sprintf("»"), 44 | PipelineFinished: color.New(color.FgGreen).Sprintf("✔"), 45 | PipelineFailed: color.New(color.FgRed).Sprintf("✘"), 46 | PipelineUnknownState: color.New(color.FgYellow).Sprintf("?"), 47 | } 48 | 49 | var ErrPipeline = errors.New("pipeline has encountered an error") 50 | -------------------------------------------------------------------------------- /pkg/progress/workflow.go: -------------------------------------------------------------------------------- 1 | package progress 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "bunnyshell.com/cli/pkg/api/event" 8 | "bunnyshell.com/sdk" 9 | "github.com/briandowns/spinner" 10 | ) 11 | 12 | func handleWorkflow(eventItem *sdk.EventItem, options *Options, spinner *spinner.Spinner) (*sdk.EventItem, error) { 13 | for { 14 | if eventItem.GetType() != "env_queued" { 15 | return eventItem, nil 16 | } 17 | 18 | delegatedEvent, err := delegateEvent(eventItem.GetId(), options) 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | spinner.FinalMSG = fmt.Sprintf("Delegating to EventID: %s\n", delegatedEvent.GetId()) 24 | spinner.Restart() 25 | spinner.FinalMSG = "" 26 | 27 | eventItem = delegatedEvent 28 | } 29 | } 30 | 31 | func delegateEvent(eventID string, options *Options) (*sdk.EventItem, error) { 32 | itemOptions := event.NewItemOptions(eventID) 33 | 34 | for { 35 | eventItem, err := event.Get(itemOptions) 36 | if err != nil { 37 | return nil, err 38 | } 39 | 40 | switch eventItem.GetStatus() { 41 | case "fail": 42 | return nil, fmt.Errorf("event has failed") 43 | 44 | case "aborted": 45 | return nil, fmt.Errorf("event was aborted") 46 | 47 | case "delegated": 48 | delegatedID := eventItem.GetDelegated() 49 | 50 | return event.Get(event.NewItemOptions(delegatedID)) 51 | 52 | default: 53 | time.Sleep(options.Interval) 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /pkg/remote_development/action/action.go: -------------------------------------------------------------------------------- 1 | package action 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "bunnyshell.com/cli/pkg/remote_development/workspace" 8 | "bunnyshell.com/dev/pkg/remote" 9 | "bunnyshell.com/sdk" 10 | ) 11 | 12 | var ErrResourceKindNotSupported = errors.New("resource kind not supported") 13 | 14 | type Action struct { 15 | workspace *workspace.Workspace 16 | } 17 | 18 | func NewAction( 19 | environment sdk.EnvironmentItem, 20 | ) *Action { 21 | return &Action{ 22 | workspace: workspace.NewWorkspace(environment.GetId()), 23 | } 24 | } 25 | 26 | func (action *Action) GetRemoteDev(resource sdk.ComponentResourceItem, overrideClusterServer string) (*remote.RemoteDevelopment, error) { 27 | kubeConfigFile, err := action.workspace.DownloadKubeConfig(overrideClusterServer) 28 | if err != nil { 29 | return nil, err 30 | } 31 | 32 | remoteDev := remote.NewRemoteDevelopment(). 33 | WithKubernetesClient(kubeConfigFile). 34 | WithNamespaceName(resource.GetNamespace()) 35 | 36 | switch kind := resource.GetKind(); kind { 37 | case "Deployment": 38 | return remoteDev.WithDeploymentName(resource.GetName()), nil 39 | case "StatefulSet": 40 | return remoteDev.WithStatefulSetName(resource.GetName()), nil 41 | case "DaemonSet": 42 | return remoteDev.WithDaemonSetName(resource.GetName()), nil 43 | default: 44 | return nil, fmt.Errorf("%w: %s", ErrResourceKindNotSupported, kind) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /pkg/remote_development/action/down.go: -------------------------------------------------------------------------------- 1 | package action 2 | 3 | import ( 4 | "bunnyshell.com/sdk" 5 | ) 6 | 7 | type DownParameters struct { 8 | Resource sdk.ComponentResourceItem 9 | 10 | OverrideClusterServer string 11 | } 12 | 13 | type Down struct { 14 | Action 15 | } 16 | 17 | func NewDown( 18 | environment sdk.EnvironmentItem, 19 | ) *Down { 20 | return &Down{ 21 | Action: *NewAction(environment), 22 | } 23 | } 24 | 25 | func (down *Down) Run(parameters *DownParameters) error { 26 | remoteDev, err := down.Action.GetRemoteDev(parameters.Resource, parameters.OverrideClusterServer) 27 | if err != nil { 28 | return err 29 | } 30 | 31 | return remoteDev.Down() 32 | } 33 | -------------------------------------------------------------------------------- /pkg/remote_development/action/down/down.cobra.go: -------------------------------------------------------------------------------- 1 | package down 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | "github.com/spf13/pflag" 6 | ) 7 | 8 | func (down *Options) UpdateFlagSet( 9 | command *cobra.Command, 10 | flags *pflag.FlagSet, 11 | ) { 12 | flags.StringVarP(&down.resourcePath, "resource", "s", down.resourcePath, "The cluster resource to use (namespace/kind/name format).") 13 | flags.StringVar(&down.overrideClusterServer, "override-kubeconfig-cluster-server", down.overrideClusterServer, "Override kubeconfig cluster server with :port, host:port or scheme://host:port") 14 | 15 | down.manager.UpdateFlagSet(command, flags) 16 | } 17 | -------------------------------------------------------------------------------- /pkg/remote_development/action/down/down.go: -------------------------------------------------------------------------------- 1 | package down 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/k8s/bridge" 5 | "bunnyshell.com/cli/pkg/remote_development/action" 6 | "bunnyshell.com/cli/pkg/remote_development/config" 7 | ) 8 | 9 | type Options struct { 10 | ManualSelectSingleResource bool 11 | 12 | manager *config.Manager 13 | 14 | resourceLoader *bridge.ResourceLoader 15 | 16 | resourcePath string 17 | 18 | overrideClusterServer string 19 | } 20 | 21 | func NewOptions( 22 | manager *config.Manager, 23 | resourceLoader *bridge.ResourceLoader, 24 | ) *Options { 25 | return &Options{ 26 | manager: manager, 27 | 28 | resourceLoader: resourceLoader, 29 | 30 | overrideClusterServer: "", 31 | } 32 | } 33 | 34 | func (down *Options) ToParameters() (*action.DownParameters, error) { 35 | down.resourceLoader.ManualSelectSingleResource = down.ManualSelectSingleResource 36 | 37 | if err := down.loadResource(); err != nil { 38 | return nil, err 39 | } 40 | 41 | parameters := &action.DownParameters{ 42 | Resource: *down.resourceLoader.GetResource(), 43 | OverrideClusterServer: down.overrideClusterServer, 44 | } 45 | 46 | return parameters, nil 47 | } 48 | 49 | func (down *Options) loadResource() error { 50 | if !down.resourceLoader.IsLoaded() { 51 | return ErrResourceLoaderNotHydrated 52 | } 53 | 54 | if down.resourceLoader.GetResource() != nil { 55 | return nil 56 | } 57 | 58 | if down.resourcePath != "" { 59 | return down.resourceLoader.SelectResourceFromString(down.resourcePath) 60 | } 61 | 62 | return down.resourceLoader.SelectResource() 63 | } 64 | -------------------------------------------------------------------------------- /pkg/remote_development/action/down/vars.go: -------------------------------------------------------------------------------- 1 | package down 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | var ErrResourceLoaderNotHydrated = errors.New("resourceLoader needs to be hydrated") 8 | -------------------------------------------------------------------------------- /pkg/remote_development/action/up/profile.go: -------------------------------------------------------------------------------- 1 | package up 2 | 3 | import "bunnyshell.com/sdk" 4 | 5 | type NamedProfile struct { 6 | Name string 7 | 8 | Profile *sdk.ProfileItem 9 | } 10 | -------------------------------------------------------------------------------- /pkg/remote_development/action/up/syncmode.go: -------------------------------------------------------------------------------- 1 | package up 2 | 3 | import ( 4 | remoteDevMutagenConfig "bunnyshell.com/dev/pkg/mutagen/config" 5 | "github.com/thediveo/enumflag/v2" 6 | ) 7 | 8 | type SyncMode enumflag.Flag 9 | 10 | const ( 11 | None SyncMode = iota 12 | TwoWaySafe 13 | TwoWayResolved 14 | OneWaySafe 15 | OneWayReplica 16 | ) 17 | 18 | var SyncModeToMutagenMode = map[SyncMode]remoteDevMutagenConfig.Mode{ 19 | None: remoteDevMutagenConfig.None, 20 | TwoWaySafe: remoteDevMutagenConfig.TwoWaySafe, 21 | TwoWayResolved: remoteDevMutagenConfig.TwoWayResolved, 22 | OneWaySafe: remoteDevMutagenConfig.OneWaySafe, 23 | OneWayReplica: remoteDevMutagenConfig.OneWayReplica, 24 | } 25 | 26 | var SyncModeIds = map[SyncMode][]string{ 27 | None: {string(remoteDevMutagenConfig.None)}, 28 | TwoWaySafe: {string(remoteDevMutagenConfig.TwoWaySafe)}, 29 | TwoWayResolved: {string(remoteDevMutagenConfig.TwoWayResolved)}, 30 | OneWaySafe: {string(remoteDevMutagenConfig.OneWaySafe)}, 31 | OneWayReplica: {string(remoteDevMutagenConfig.OneWayReplica)}, 32 | } 33 | 34 | var SyncModeList = []string{ 35 | string(remoteDevMutagenConfig.None), 36 | string(remoteDevMutagenConfig.TwoWaySafe), 37 | string(remoteDevMutagenConfig.TwoWayResolved), 38 | string(remoteDevMutagenConfig.OneWaySafe), 39 | string(remoteDevMutagenConfig.OneWayReplica), 40 | } 41 | -------------------------------------------------------------------------------- /pkg/remote_development/action/up/utils.go: -------------------------------------------------------------------------------- 1 | package up 2 | -------------------------------------------------------------------------------- /pkg/remote_development/action/up/vars.go: -------------------------------------------------------------------------------- 1 | package up 2 | 3 | import ( 4 | "errors" 5 | "time" 6 | ) 7 | 8 | const defaultWaitTimeout = 120 * time.Second 9 | 10 | var ( 11 | ErrNoRDevConfig = errors.New("component does not have a remote development config") 12 | 13 | ErrEmptyList = errors.New("empty list") 14 | 15 | ErrOneSyncPathSupported = errors.New("only one sync path is supported") 16 | 17 | ErrTooManySimpleConfig = errors.New("too many simple resources") 18 | 19 | ErrTooManySimpleResources = errors.New("too many simple containers") 20 | 21 | ErrUnknownProfileType = errors.New("unknown profile type") 22 | ErrUnknownConfigurationType = errors.New("unknown configuration type") 23 | 24 | ErrResourceLoaderNotHydrated = errors.New("resourceLoader needs to be hydrated") 25 | ) 26 | -------------------------------------------------------------------------------- /pkg/remote_development/action/vars.go: -------------------------------------------------------------------------------- 1 | package action 2 | 3 | import "errors" 4 | 5 | var ( 6 | ErrOneSyncPathSupported = errors.New("only one sync path is supported") 7 | 8 | ErrRemoteDevNotInitialized = errors.New("call Up.Run() successfully before calling other methods") 9 | ) 10 | -------------------------------------------------------------------------------- /pkg/remote_development/bridge.go: -------------------------------------------------------------------------------- 1 | package remote_development 2 | 3 | import "bunnyshell.com/dev/pkg/ssh" 4 | 5 | func GetSSHConfigFilePath() (string, error) { 6 | return ssh.GetConfigFilePath() 7 | } 8 | -------------------------------------------------------------------------------- /pkg/remote_development/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import "fmt" 4 | 5 | type Config struct { 6 | Profiles NamedProfiles `json:"profiles" yaml:"profiles"` 7 | } 8 | 9 | func (config *Config) getProfile(name string) (*Profile, error) { 10 | profile, ok := config.Profiles[name] 11 | if !ok { 12 | return nil, fmt.Errorf("%w: %s", ErrUnknownProfile, name) 13 | } 14 | 15 | return &profile, nil 16 | } 17 | 18 | func (config *Config) profileNames() []string { 19 | profiles := []string{} 20 | 21 | for name := range config.Profiles { 22 | profiles = append(profiles, name) 23 | } 24 | 25 | return profiles 26 | } 27 | -------------------------------------------------------------------------------- /pkg/remote_development/config/manager.cobra.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | "github.com/spf13/pflag" 8 | ) 9 | 10 | type ShellCompletion func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) 11 | 12 | func (manager *Manager) UpdateFlagSet(command *cobra.Command, flags *pflag.FlagSet) { 13 | configFileParam := "rdev-configFile" 14 | configDirParam := "rdev-configDir" 15 | profileNameParam := "rdev-profile" 16 | 17 | flags.StringVar( 18 | &manager.configFileParam, 19 | configFileParam, 20 | manager.configFileParam, 21 | fmt.Sprintf( 22 | "Remote Dev config file\n"+ 23 | "An absolute path ignores --%s", 24 | configDirParam, 25 | ), 26 | ) 27 | 28 | flags.StringVar( 29 | &manager.configDirParam, 30 | configDirParam, 31 | manager.configDirParam, 32 | "Remote Dev config directory\n"+ 33 | "Using ... will look through all parent directories", 34 | ) 35 | 36 | flags.StringVar( 37 | &manager.profileName, 38 | profileNameParam, 39 | manager.profileName, 40 | fmt.Sprintf( 41 | "Remote Dev profile name. Loaded from --%s", 42 | configFileParam, 43 | ), 44 | ) 45 | 46 | _ = command.RegisterFlagCompletionFunc(profileNameParam, manager.profileNamesCompletion()) 47 | } 48 | 49 | func (manager *Manager) profileNamesCompletion() ShellCompletion { 50 | return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { 51 | if manager.Load() != nil { 52 | return []string{}, cobra.ShellCompDirectiveDefault 53 | } 54 | 55 | return manager.config.profileNames(), cobra.ShellCompDirectiveDefault 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /pkg/remote_development/config/types.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type SyncPath struct { 4 | RemotePath string `yaml:"remotePath"` 5 | LocalPath string `yaml:"localPath"` 6 | } 7 | 8 | type Environ map[string]string 9 | 10 | type ( 11 | PortForward string 12 | PortForwardList []PortForward 13 | ) 14 | 15 | type Resource struct { 16 | CPU string `yaml:"cpu,omitempty"` 17 | Memory string `yaml:"memory,omitempty"` 18 | } 19 | 20 | type ResourceList struct { 21 | Limits Resource `yaml:"limits,omitempty"` 22 | Requests Resource `yaml:"requests,omitempty"` 23 | } 24 | 25 | type Profile struct { 26 | Name string `yaml:"-"` 27 | 28 | Command []string `yaml:"command,omitempty"` 29 | 30 | SyncPaths []SyncPath `yaml:"syncPaths,omitempty"` 31 | 32 | PortForwards PortForwardList `yaml:"portForwards,omitempty"` 33 | 34 | Environ Environ `yaml:"environment,omitempty"` 35 | 36 | Resources ResourceList `yaml:"resources,omitempty"` 37 | } 38 | 39 | type NamedProfiles map[string]Profile 40 | -------------------------------------------------------------------------------- /pkg/remote_development/config/vars.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | const ( 8 | defaultConfigDirParam = ".../.bunnyshell" 9 | defaultConfigFileParam = "rdev.yaml" 10 | ) 11 | 12 | var ( 13 | MainManager = NewManager() 14 | 15 | ErrUnknownProfile = errors.New("profile not found") 16 | ErrConfigLoad = errors.New("unable to load config") 17 | ErrInvalidValue = errors.New("invalid value") 18 | ) 19 | -------------------------------------------------------------------------------- /pkg/remote_development/workspace.go: -------------------------------------------------------------------------------- 1 | package remote_development 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | 7 | "bunnyshell.com/cli/pkg/lib" 8 | "bunnyshell.com/cli/pkg/util" 9 | ) 10 | 11 | const ( 12 | KubeConfigFilename = "kube-config.yaml" 13 | ) 14 | 15 | // @deprecated Use bunnyshell.com/cli/pkg/remote_development/workspace 16 | func (r *RemoteDevelopment) ensureEnvironmentWorkspaceDir() error { 17 | workspace, err := util.GetWorkspaceDir() 18 | if err != nil { 19 | return err 20 | } 21 | 22 | r.WithEnvironmentWorkspaceDir(filepath.Join(workspace, r.environmentResource.Environment.GetId())) 23 | 24 | return os.MkdirAll(r.environmentWorkspaceDir, 0755) 25 | } 26 | 27 | // @deprecated Use bunnyshell.com/cli/pkg/remote_development/workspace 28 | func (r *RemoteDevelopment) ensureEnvironmentKubeConfig() error { 29 | kubeConfigPath := filepath.Join(r.environmentWorkspaceDir, KubeConfigFilename) 30 | if err := lib.DownloadEnvironmentKubeConfig(kubeConfigPath, r.environmentResource.Environment.GetId()); err != nil { 31 | return err 32 | } 33 | 34 | r.WithKubeConfigPath(kubeConfigPath) 35 | 36 | return nil 37 | } 38 | -------------------------------------------------------------------------------- /pkg/remote_development/workspace/vars.go: -------------------------------------------------------------------------------- 1 | package workspace 2 | 3 | const ( 4 | workspacePerm = 0o700 5 | 6 | kubeConfigPerm = 0o600 7 | 8 | kubeConfigFilename = "kube-config.yaml" 9 | ) 10 | -------------------------------------------------------------------------------- /pkg/util/network.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | ) 7 | 8 | func GetAvailableEphemeralPort(iface string) (int, error) { 9 | address := fmt.Sprintf("%s:0", iface) 10 | 11 | listen, err := net.Listen("tcp", address) 12 | if err != nil { 13 | return 0, err 14 | } 15 | defer listen.Close() 16 | 17 | tcp, _ := listen.Addr().(*net.TCPAddr) 18 | 19 | return tcp.Port, nil 20 | } 21 | -------------------------------------------------------------------------------- /pkg/util/os.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import "os" 4 | 5 | func FileExists(path string) (bool, error) { 6 | _, err := os.Stat(path) 7 | if err == nil { 8 | return true, nil 9 | } 10 | 11 | if os.IsNotExist(err) { 12 | return false, nil 13 | } 14 | 15 | return false, err 16 | } 17 | 18 | func IsStdinPresent() (bool, error) { 19 | fi, err := os.Stdin.Stat() 20 | if err != nil { 21 | return false, err 22 | } 23 | 24 | return (fi.Mode() & os.ModeCharDevice) == 0, nil 25 | } 26 | -------------------------------------------------------------------------------- /pkg/util/spinner.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "github.com/briandowns/spinner" 5 | ) 6 | 7 | func MakeSpinner(suffix string) *spinner.Spinner { 8 | spinner := spinner.New(spinner.CharSets[9], defaultDuration) 9 | spinner.Suffix = suffix 10 | 11 | return spinner 12 | } 13 | -------------------------------------------------------------------------------- /pkg/util/vars.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "errors" 5 | "time" 6 | ) 7 | 8 | const ( 9 | StrTrue = "true" 10 | StrFalse = "false" 11 | ) 12 | 13 | var ( 14 | defaultDuration = 100 * time.Millisecond 15 | 16 | ErrInvalidValue = errors.New("invalid value") 17 | ) 18 | -------------------------------------------------------------------------------- /pkg/util/workspace.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | ) 7 | 8 | const workspaceDirname = ".bunnyshell" 9 | 10 | func GetWorkspaceDirAndShort() (string, string, error) { 11 | home, err := os.UserHomeDir() 12 | if err != nil { 13 | return "", "", err 14 | } 15 | 16 | if home == "/" { 17 | return "/bunnyshell", "/bunnyshell", nil 18 | } 19 | 20 | // @review $HOME is more linuxy check os.UserHomeDir and update ? 21 | return filepath.Join(home, workspaceDirname), filepath.Join("$HOME", workspaceDirname), nil 22 | } 23 | 24 | func GetWorkspaceDir() (string, error) { 25 | home, _, err := GetWorkspaceDirAndShort() 26 | 27 | return home, err 28 | } 29 | -------------------------------------------------------------------------------- /pkg/wizard/k8s/pod.go: -------------------------------------------------------------------------------- 1 | package k8s 2 | 3 | import ( 4 | "fmt" 5 | 6 | "bunnyshell.com/cli/pkg/api/component" 7 | "bunnyshell.com/cli/pkg/interactive" 8 | "bunnyshell.com/sdk" 9 | ) 10 | 11 | type PodListOptions struct { 12 | Component string 13 | } 14 | 15 | func PodList(options *PodListOptions) ([]sdk.ComponentResourceItem, error) { 16 | resources, err := component.Resources(component.NewResourceOptions(options.Component)) 17 | if err != nil { 18 | return nil, err 19 | } 20 | 21 | pods := []sdk.ComponentResourceItem{} 22 | 23 | for _, resource := range resources { 24 | if resource.GetKind() == "Pod" { 25 | pods = append(pods, resource) 26 | } 27 | } 28 | 29 | return pods, nil 30 | } 31 | 32 | func PodSelect(options *PodListOptions) (*sdk.ComponentResourceItem, error) { 33 | pods, err := PodList(options) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | if len(pods) == 0 { 39 | return nil, errEmptyList 40 | } 41 | 42 | if len(pods) == 1 { 43 | return &pods[0], nil 44 | } 45 | 46 | index, _, err := interactive.Choose("Choose a pod", podToSelectorItems(pods)) 47 | if err != nil { 48 | return nil, err 49 | } 50 | 51 | return &pods[index], nil 52 | } 53 | 54 | func podToSelectorItems(pods []sdk.ComponentResourceItem) []string { 55 | items := []string{} 56 | 57 | for _, resource := range pods { 58 | items = append(items, fmt.Sprintf( 59 | "%s / %s", 60 | resource.GetNamespace(), 61 | resource.GetName(), 62 | )) 63 | } 64 | 65 | return items 66 | } 67 | -------------------------------------------------------------------------------- /pkg/wizard/k8s/var.go: -------------------------------------------------------------------------------- 1 | package k8s 2 | 3 | import "errors" 4 | 5 | var errEmptyList = errors.New("empty list") 6 | -------------------------------------------------------------------------------- /pkg/wizard/vars.go: -------------------------------------------------------------------------------- 1 | package wizard 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | const ( 8 | selectPage = iota 9 | 10 | paginationItemsPerPage = 30 11 | ) 12 | 13 | var ( 14 | paginationOptions = map[int]string{ 15 | selectPage: "Select Page", 16 | } 17 | 18 | paginationDisplaySize = paginationItemsPerPage + len(paginationOptions) 19 | 20 | ErrEmptyListing = errors.New("empty listing") 21 | ) 22 | -------------------------------------------------------------------------------- /pkg/wizard/wizard.go: -------------------------------------------------------------------------------- 1 | package wizard 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/config" 5 | ) 6 | 7 | type Wizard struct { 8 | profile *config.Profile 9 | } 10 | 11 | func New(profile *config.Profile) *Wizard { 12 | return &Wizard{ 13 | profile: profile, 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /pkg/wizard/wizard.pagination.go: -------------------------------------------------------------------------------- 1 | package wizard 2 | 3 | import ( 4 | "bunnyshell.com/cli/pkg/interactive" 5 | ) 6 | 7 | // @see lib/pagination.go 8 | type Pagination interface { 9 | GetPage() int32 10 | 11 | GetItemsPerPage() int32 12 | 13 | GetTotalItems() int32 14 | } 15 | 16 | func getPaginationInfo(pagination Pagination) (int32, int32) { 17 | page := pagination.GetPage() 18 | pages := 1 + (pagination.GetTotalItems()-1)/pagination.GetItemsPerPage() 19 | 20 | return page, pages 21 | } 22 | 23 | func addPagination(items []string, page int32, pages int32) []string { 24 | if page > pages { 25 | return items 26 | } 27 | 28 | if pages == 1 { 29 | return items 30 | } 31 | 32 | items = append(items, paginationOptions[selectPage]) 33 | 34 | return items 35 | } 36 | 37 | func chooseOrNavigate(question string, items []string, page int32, pages int32) (*int, *int32, error) { 38 | selector := addPagination(items, page, pages) 39 | 40 | index, answer, err := interactive.ChooseWithSize(paginationDisplaySize, question, selector) 41 | if err != nil { 42 | return nil, nil, err 43 | } 44 | 45 | if index >= len(items) { 46 | if answer == paginationOptions[selectPage] { 47 | page, err = interactive.AskInt32("Select a page", interactive.AssertBetween(1, pages)) 48 | 49 | return nil, &page, err 50 | } 51 | 52 | return nil, nil, interactive.ErrInvalidValue 53 | } 54 | 55 | return &index, nil, nil 56 | } 57 | --------------------------------------------------------------------------------