From 24d49d429a2f521aabbab7c0b919ce256cc361bb Mon Sep 17 00:00:00 2001 From: Chris Suszynski Date: Fri, 20 Sep 2024 12:56:55 +0200 Subject: [PATCH] :gift_heart: A general purpose WithCommand option. (#2) A general purpose WithCommand option. --- .github/workflows/go.yml | 14 ++++++----- .golangci.yaml | 39 ++++++++++++++++++++++++++++ types.go => app.go | 33 ------------------------ types_test.go => app_test.go | 12 +++++---- go.mod | 10 ++++---- go.sum | 12 +++++++++ options.go | 49 ++++++++++++++++++++++++++++++++++++ test/cmd/main_test.go | 8 +++--- test/internal/cli/app.go | 4 +-- 9 files changed, 127 insertions(+), 54 deletions(-) create mode 100644 .golangci.yaml rename types.go => app.go (67%) rename types_test.go => app_test.go (84%) create mode 100644 options.go diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 4decab1..280bb6c 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -6,6 +6,9 @@ on: pull_request: types: [opened, synchronize, reopened] +env: + FORCE_COLOR: true + jobs: build: @@ -14,20 +17,19 @@ jobs: strategy: matrix: go-version: - - '1.18' + - '1.22' + - '1.23' steps: - name: Set up Go ${{ matrix.go-version }} - uses: actions/setup-go@v2 + uses: actions/setup-go@v5 with: go-version: ${{ matrix.go-version }} id: go - name: Check out code into the Go module directory - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Test - run: go run gotest.tools/gotestsum@v1.8.0 --format testname -- + run: go run gotest.tools/gotestsum@v1.12.0 --format testname -- -race -count=1 -short ./... - env: - FORCE_COLOR: true diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 0000000..c34afd3 --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,39 @@ +run: + timeout: 5m + +linters: + disable-all: false + presets: + - bugs + - unused + - complexity + - format + - performance + - style + enable: + - gci + disable: + - paralleltest + - nlreturn + - exhaustivestruct + - wsl + - godox + - scopelint + - maligned + - interfacer + - golint + - ireturn + - varnamelen + - exhaustruct + - depguard + +issues: + exclude-rules: + - path: _test\.go + linters: + - wrapcheck + +linters-settings: + gomoddirectives: + # List of allowed `replace` directives. Default is empty. + replace-allow-list: [] diff --git a/types.go b/app.go similarity index 67% rename from types.go rename to app.go index 0f9d4e6..0be004d 100644 --- a/types.go +++ b/app.go @@ -2,7 +2,6 @@ package commandline import ( "errors" - "io" "os" "github.com/spf13/cobra" @@ -24,9 +23,6 @@ type CobraProvider interface { Command() *cobra.Command } -// Option is used to configure an App. -type Option func(*App) - // New creates a new App from CobraProvider. func New(cp CobraProvider) *App { return &App{ @@ -42,35 +38,6 @@ func (a *App) ExecuteOrDie(options ...Option) { } } -// WithArgs creates an option which sets args. -func WithArgs(args ...string) Option { - return func(app *App) { - app.root.SetArgs(args) - } -} - -// WithInput creates an option witch sets os.Stdin. -func WithInput(in io.Reader) Option { - return func(app *App) { - app.root.SetIn(in) - } -} - -// WithOutput creates an option witch sets os.Stdout and os.Stderr. -func WithOutput(out io.Writer) Option { - return func(app *App) { - app.root.SetOut(out) - app.root.SetErr(out) - } -} - -// WithExit creates an option which sets the exit function. -func WithExit(fn func(code int)) Option { - return func(app *App) { - app.Exit = fn - } -} - // Execute will execute the application with the provided options and return // error if any. func (a *App) Execute(options ...Option) error { diff --git a/types_test.go b/app_test.go similarity index 84% rename from types_test.go rename to app_test.go index 295c1bd..b6db0d6 100644 --- a/types_test.go +++ b/app_test.go @@ -3,7 +3,7 @@ package commandline_test import ( "bytes" "errors" - "io/ioutil" + "io" "testing" "github.com/spf13/cobra" @@ -15,9 +15,11 @@ func TestExecuteOrDie(t *testing.T) { var buf bytes.Buffer var retcode int commandline.New(new(testApp)).ExecuteOrDie( - commandline.WithOutput(&buf), - commandline.WithInput(bytes.NewBufferString("Input")), - commandline.WithArgs("arg1", "arg2"), + commandline.WithCommand(func(cmd *cobra.Command) { + cmd.SetOut(&buf) + cmd.SetIn(bytes.NewBufferString("Input")) + cmd.SetArgs([]string{"arg1", "arg2"}) + }), commandline.WithExit(func(code int) { retcode = code }), @@ -46,7 +48,7 @@ func (t testApp) Command() *cobra.Command { SilenceUsage: true, SilenceErrors: true, RunE: func(cmd *cobra.Command, args []string) error { - in, err := ioutil.ReadAll(cmd.InOrStdin()) + in, err := io.ReadAll(cmd.InOrStdin()) if err != nil { return err } diff --git a/go.mod b/go.mod index 59aa3a4..d965384 100644 --- a/go.mod +++ b/go.mod @@ -1,15 +1,15 @@ module github.com/wavesoftware/go-commandline -go 1.18 +go 1.22.0 require ( - github.com/spf13/cobra v1.5.0 + github.com/spf13/cobra v1.8.1 github.com/wavesoftware/go-retcode v1.0.0 - gotest.tools/v3 v3.3.0 + gotest.tools/v3 v3.5.1 ) require ( - github.com/google/go-cmp v0.5.5 // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect ) diff --git a/go.sum b/go.sum index 0df3105..77001c7 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,20 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -37,5 +46,8 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1N golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo= gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= diff --git a/options.go b/options.go new file mode 100644 index 0000000..a34cbd6 --- /dev/null +++ b/options.go @@ -0,0 +1,49 @@ +package commandline + +import ( + "io" + + "github.com/spf13/cobra" +) + +// Option is used to configure an App. +type Option func(*App) + +// WithArgs creates an option which sets args. +// Deprecated: use WithCommand instead. +func WithArgs(args ...string) Option { + return WithCommand(func(cmd *cobra.Command) { + cmd.SetArgs(args) + }) +} + +// WithInput creates an option witch sets os.Stdin. +// Deprecated: use WithCommand instead. +func WithInput(in io.Reader) Option { + return WithCommand(func(cmd *cobra.Command) { + cmd.SetIn(in) + }) +} + +// WithOutput creates an option witch sets os.Stdout and os.Stderr. +// Deprecated: use WithCommand instead. +func WithOutput(out io.Writer) Option { + return WithCommand(func(cmd *cobra.Command) { + cmd.SetOut(out) + cmd.SetErr(out) + }) +} + +// WithCommand will allow one to change the cobra.Command. +func WithCommand(fn func(cmd *cobra.Command)) Option { + return func(app *App) { + fn(app.root) + } +} + +// WithExit creates an option which sets the exit function. +func WithExit(fn func(code int)) Option { + return func(app *App) { + app.Exit = fn + } +} diff --git a/test/cmd/main_test.go b/test/cmd/main_test.go index 6f8484b..55e55ed 100644 --- a/test/cmd/main_test.go +++ b/test/cmd/main_test.go @@ -4,11 +4,11 @@ import ( "bytes" "testing" + "github.com/spf13/cobra" + "github.com/wavesoftware/go-commandline" main "github.com/wavesoftware/go-commandline/test/cmd" "github.com/wavesoftware/go-commandline/test/internal/cli" "gotest.tools/v3/assert" - - "github.com/wavesoftware/go-commandline" ) func TestTheMain(t *testing.T) { @@ -26,7 +26,9 @@ type state struct { func (s *state) opts() []commandline.Option { return []commandline.Option{ - commandline.WithOutput(&s.out), + commandline.WithCommand(func(cmd *cobra.Command) { + cmd.SetOut(&s.out) + }), commandline.WithExit(func(code int) { s.exitCode = code }), diff --git a/test/internal/cli/app.go b/test/internal/cli/app.go index 98702ae..49f2035 100644 --- a/test/internal/cli/app.go +++ b/test/internal/cli/app.go @@ -6,14 +6,14 @@ import ( ) // Opts is the list of commandline options to pass to the main function. -var Opts []commandline.Option +var Opts []commandline.Option //nolint:gochecknoglobals type App struct{} func (a App) Command() *cobra.Command { return &cobra.Command{ Use: "example", - Run: func(cmd *cobra.Command, args []string) { + Run: func(cmd *cobra.Command, _ []string) { cmd.Println("Hello, world!") }, }