From 6df451062619b039d6c4e2df8eca54d0d2fa45d6 Mon Sep 17 00:00:00 2001 From: Eric Hripko Date: Tue, 23 Feb 2021 09:45:56 +0000 Subject: [PATCH] Enable configuration of Go integration (#3) Also improve docs and temporarily use private cache mounts --- .golangci.yml | 2 + README.md | 67 ++- codecov.yml | 3 + pkg/plugins/golang/golang.go | 89 +++- pkg/plugins/golang/golang_test.go | 78 ++- tests/e2e_test.go | 57 +++ .../.circleci/config.yml | 36 ++ tests/go-cli-template-master/.dockerignore | 1 + .../.github/workflows/gcp-preview.yml | 88 ++++ .../.github/workflows/go.yml | 44 ++ .../.github/workflows/release.yml | 35 ++ tests/go-cli-template-master/.gitignore | 3 + tests/go-cli-template-master/.goreleaser.yml | 28 ++ tests/go-cli-template-master/Dockerfile | 18 + tests/go-cli-template-master/LICENSE | 21 + tests/go-cli-template-master/Makefile | 51 ++ tests/go-cli-template-master/appveyor.yml | 26 + tests/go-cli-template-master/cmd/cmd.go | 10 + .../go-cli-template-master/cmd/option/cmd.go | 3 + .../cmd/option/config.go | 99 ++++ .../go-cli-template-master/cmd/option/flag.go | 308 ++++++++++++ .../go-cli-template-master/cmd/option/root.go | 21 + .../cmd/option/serve.go | 21 + .../go-cli-template-master/cmd/option/sum.go | 44 ++ .../cmd/option/sum_test.go | 47 ++ tests/go-cli-template-master/cmd/root.go | 123 +++++ .../go-cli-template-master/cmd/selfupdate.go | 32 ++ tests/go-cli-template-master/cmd/serve.go | 52 ++ tests/go-cli-template-master/cmd/sum.go | 94 ++++ tests/go-cli-template-master/cmd/sum_test.go | 73 +++ tests/go-cli-template-master/cmd/version.go | 23 + .../domain/model/numbers.go | 50 ++ .../domain/model/sum_history.go | 11 + .../domain/repository/sum_history.go | 9 + tests/go-cli-template-master/domain/sum.go | 17 + .../go-cli-template-master/domain/sum_test.go | 326 +++++++++++++ tests/go-cli-template-master/go.mod | 24 + tests/go-cli-template-master/go.sum | 450 ++++++++++++++++++ .../handler/export_test.go | 4 + .../handler/sum-history.go | 38 ++ .../handler/sum-history_test.go | 120 +++++ tests/go-cli-template-master/handler/sum.go | 49 ++ .../handler/sum_test.go | 112 +++++ tests/go-cli-template-master/handler/util.go | 20 + .../infra/repoimpl/memory_sum_history.go | 36 ++ tests/go-cli-template-master/infra/server.go | 41 ++ tests/go-cli-template-master/main.go | 7 + tests/go-cli-template-master/pack.yaml | 3 + tests/go-cli-template-master/readme.md | 115 +++++ tests/go-cli-template-master/registry/wire.go | 38 ++ tests/go-cli-template-master/renovate.json | 10 + .../scripts/request-to-sum-api.sh | 1 + .../go-cli-template-master/scripts/serve.http | 9 + tests/go-cli-template-master/test.txt | 1 + tests/go-cli-template-master/testdata/sum.txt | 2 + tests/go-cli-template-master/usecase/sum.go | 55 +++ .../usecase/sum_test.go | 83 ++++ tests/go-cli-template-master/util/error.go | 54 +++ .../go-cli-template-master/util/error_test.go | 42 ++ .../go-cli-template-master/util/selfupdate.go | 40 ++ .../util/selfupdate_test.go | 32 ++ tests/go-cli-template-master/util/util.go | 32 ++ .../go-cli-template-master/util/util_test.go | 47 ++ 63 files changed, 3432 insertions(+), 43 deletions(-) create mode 100644 codecov.yml create mode 100644 tests/e2e_test.go create mode 100644 tests/go-cli-template-master/.circleci/config.yml create mode 100644 tests/go-cli-template-master/.dockerignore create mode 100644 tests/go-cli-template-master/.github/workflows/gcp-preview.yml create mode 100644 tests/go-cli-template-master/.github/workflows/go.yml create mode 100644 tests/go-cli-template-master/.github/workflows/release.yml create mode 100644 tests/go-cli-template-master/.gitignore create mode 100644 tests/go-cli-template-master/.goreleaser.yml create mode 100644 tests/go-cli-template-master/Dockerfile create mode 100644 tests/go-cli-template-master/LICENSE create mode 100644 tests/go-cli-template-master/Makefile create mode 100644 tests/go-cli-template-master/appveyor.yml create mode 100644 tests/go-cli-template-master/cmd/cmd.go create mode 100644 tests/go-cli-template-master/cmd/option/cmd.go create mode 100644 tests/go-cli-template-master/cmd/option/config.go create mode 100644 tests/go-cli-template-master/cmd/option/flag.go create mode 100644 tests/go-cli-template-master/cmd/option/root.go create mode 100644 tests/go-cli-template-master/cmd/option/serve.go create mode 100644 tests/go-cli-template-master/cmd/option/sum.go create mode 100644 tests/go-cli-template-master/cmd/option/sum_test.go create mode 100644 tests/go-cli-template-master/cmd/root.go create mode 100644 tests/go-cli-template-master/cmd/selfupdate.go create mode 100644 tests/go-cli-template-master/cmd/serve.go create mode 100644 tests/go-cli-template-master/cmd/sum.go create mode 100644 tests/go-cli-template-master/cmd/sum_test.go create mode 100644 tests/go-cli-template-master/cmd/version.go create mode 100644 tests/go-cli-template-master/domain/model/numbers.go create mode 100644 tests/go-cli-template-master/domain/model/sum_history.go create mode 100644 tests/go-cli-template-master/domain/repository/sum_history.go create mode 100644 tests/go-cli-template-master/domain/sum.go create mode 100644 tests/go-cli-template-master/domain/sum_test.go create mode 100644 tests/go-cli-template-master/go.mod create mode 100644 tests/go-cli-template-master/go.sum create mode 100644 tests/go-cli-template-master/handler/export_test.go create mode 100644 tests/go-cli-template-master/handler/sum-history.go create mode 100644 tests/go-cli-template-master/handler/sum-history_test.go create mode 100644 tests/go-cli-template-master/handler/sum.go create mode 100644 tests/go-cli-template-master/handler/sum_test.go create mode 100644 tests/go-cli-template-master/handler/util.go create mode 100644 tests/go-cli-template-master/infra/repoimpl/memory_sum_history.go create mode 100644 tests/go-cli-template-master/infra/server.go create mode 100644 tests/go-cli-template-master/main.go create mode 100644 tests/go-cli-template-master/pack.yaml create mode 100644 tests/go-cli-template-master/readme.md create mode 100644 tests/go-cli-template-master/registry/wire.go create mode 100644 tests/go-cli-template-master/renovate.json create mode 100644 tests/go-cli-template-master/scripts/request-to-sum-api.sh create mode 100644 tests/go-cli-template-master/scripts/serve.http create mode 100644 tests/go-cli-template-master/test.txt create mode 100644 tests/go-cli-template-master/testdata/sum.txt create mode 100644 tests/go-cli-template-master/usecase/sum.go create mode 100644 tests/go-cli-template-master/usecase/sum_test.go create mode 100644 tests/go-cli-template-master/util/error.go create mode 100644 tests/go-cli-template-master/util/error_test.go create mode 100644 tests/go-cli-template-master/util/selfupdate.go create mode 100644 tests/go-cli-template-master/util/selfupdate_test.go create mode 100644 tests/go-cli-template-master/util/util.go create mode 100644 tests/go-cli-template-master/util/util_test.go diff --git a/.golangci.yml b/.golangci.yml index a3072cd..edb4db0 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,3 +1,5 @@ run: # Exclude tests tests: false + skip-files: + - tests diff --git a/README.md b/README.md index d2705c1..8d8ec39 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # pack.yaml + [![Go Report](https://goreportcard.com/badge/github.com/EricHripko/pack.yaml)](https://goreportcard.com/report/github.com/EricHripko/pack.yaml) ![CI](https://github.com/EricHripko/pack.yaml/actions/workflows/ci.yml/badge.svg) @@ -14,10 +15,64 @@ footprint. `pack.yaml` takes advantage of the plugin system to provide deep integrations with various language and build ecosystems. As of today, the following -functionality is available: +functionality is available. + +### Go + +```text + => [internal] load build definition from pack.yaml 0.0s + => => transferring dockerfile: 98B 0.0s + => [internal] load .dockerignore 0.0s + => => transferring context: 50B 0.0s + => resolve image config for docker.io/erichripko/pack.yaml:latest 0.0s + => CACHED docker-image://docker.io/erichripko/pack.yaml:latest 0.0s + => [internal] load build definition from pack.yaml 0.0s + => => transferring dockerfile: 98B 0.0s + => [internal] load context 0.0s + => => transferring context: 109.58kB 0.0s + => load metadata for docker.io/library/golang:1.14 0.5s + => load metadata for gcr.io/distroless/base:debug 0.5s + => Base build image is golang:1.14 7.9s + => => resolve docker.io/library/golang:1.14 0.0s + => => sha256:0ecb575e629cd60aa802266a3bc6847dcf4073a 50.40MB / 50.40MB 0.0s + => => sha256:feab2c490a3cea21cc051ff29c33cc9857418ed 10.00MB / 10.00MB 0.0s + => => sha256:1517911a35d7939f446084c1d4c31afc552678e 68.72MB / 68.72MB 0.0s + => => sha256:48bbd1746d63c372e12f884178053851d87f3 124.14MB / 124.14MB 0.0s + => => sha256:1a7173b5b9a3af3e29a5837e0b2027e1c438fd1b8 2.36kB / 2.36kB 0.0s + => => sha256:6a39a02f74ffee82a169f2d836134236dc6f69e59 1.79kB / 1.79kB 0.0s + => => sha256:21a5635903d69da3c3d928ed429e3610eecdf878c 7.03kB / 7.03kB 0.0s + => => sha256:7467d1831b6947c294d92ee957902c3cd448b17c5 7.83MB / 7.83MB 0.0s + => => sha256:f15a0f46f8c38f4ca7daecf160ba9cdb3ddeafd 51.83MB / 51.83MB 0.0s + => => sha256:944903612fdd2364b4647cf3c231c41103d1fd378add4 126B / 126B 0.0s + => => extracting sha256:48bbd1746d63c372e12f884178053851d87f3ea4b415f3 7.1s + => => extracting sha256:944903612fdd2364b4647cf3c231c41103d1fd378add43 0.0s + => Base runtime image is gcr.io/distroless/base:debug 0.1s + => Create build output directory 0.6s + => Build github.com/mpppk/cli-template 8.8s + => CACHED Create output directory 0.0s + => Install application(s) 0.1s + => exporting to image 0.1s + => => exporting layers 0.1s + => => writing image sha256:86d32b9ed657253d9713fa1d6e0859219fc607fbbca 0.0s +``` + +[Go](https://golang.org/) - runs `go build` on your project and takes full +advantage of the build cache for best performance. Integration supports the +following dependency management methods: + +- [go mod](https://golang.org/ref/mod) - automatically picks up the version + of Go from `go.mod` and relies on module cache for best performance. + +The following additional configuration is supported by the integration: -- [Go](https://golang.org/) - runs `go build` on your project and takes full - advantage of the build cache for best performance. Following dependency - management methods are supported: - - [go mod](https://golang.org/ref/mod) - automatically picks up the version - of Go from `go.mod` and relies on module cache for best performance. +```yaml +# syntax = erichripko/pack.yaml +go: + # Version of Go to use for the project. + version: "1.14" + # List of Go build tags to set. + tags: ["wireinject"] + # How the dependencies are specified. + # Supported values are: modules + dependencyMode: modules +``` diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..9260cc5 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,3 @@ +# Exclude tests +ignore: + - test diff --git a/pkg/plugins/golang/golang.go b/pkg/plugins/golang/golang.go index 4d3857f..e543218 100644 --- a/pkg/plugins/golang/golang.go +++ b/pkg/plugins/golang/golang.go @@ -4,9 +4,11 @@ import ( "context" "fmt" "regexp" + "strings" "github.com/EricHripko/pack.yaml/pkg/cib" "github.com/EricHripko/pack.yaml/pkg/packer2llb" + "github.com/mitchellh/mapstructure" "github.com/moby/buildkit/client/llb" "github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb" @@ -15,6 +17,7 @@ import ( "github.com/pkg/errors" fsutil "github.com/tonistiigi/fsutil/types" "golang.org/x/mod/modfile" + "golang.org/x/sync/errgroup" ) // Regular expression for detecting a Go project. @@ -27,31 +30,55 @@ var ( ) // DependencyMode describes all the supported methods for dependency resolution. -type DependencyMode int +type DependencyMode string const ( // DMUnknown represents an unrecognised dependency method. - DMUnknown = iota + DMUnknown = "unknown" // DMGoMod represents a go.mod/go.sum project. - DMGoMod + DMGoMod = "modules" ) +// Config for the Go plugin. +type Config struct { + // Version of Go used. + Version string + // Method for declaring dependencies. + DependencyMode DependencyMode + // Build tags. + Tags []string +} + // Plugin for Go ecosystem. type Plugin struct { // General configuration supplied by the user. config *cib.Config - // Mode for dependency resolution. - dependencyMode DependencyMode - // Version of Go used. - version string + // Configuration for the plugin. + pluginConfig *Config // Name of the project. name string } +// NewPlugin creates a new Go plugin with correct defaults. +func NewPlugin() *Plugin { + return &Plugin{ + config: cib.NewConfig(), + pluginConfig: &Config{ + DependencyMode: DMUnknown, + }, + } +} + // Detect if this is a Go project and identify the context. func (p *Plugin) Detect(ctx context.Context, src client.Reference, config *cib.Config) error { // Save config p.config = config + if other, ok := p.config.Other["go"]; ok { + err := mapstructure.Decode(other, p.pluginConfig) + if err != nil { + return err + } + } // Look for go files err := cib.WalkRecursive(ctx, src, func(file *fsutil.Stat) error { @@ -65,27 +92,40 @@ func (p *Plugin) Detect(ctx context.Context, src client.Reference, config *cib.C } // Identify dependency method - goMod, err := src.ReadFile(ctx, client.ReadRequest{Filename: "go.mod"}) - if err == nil { - _, err = src.ReadFile(ctx, client.ReadRequest{Filename: "go.sum"}) - if err == nil { - p.dependencyMode = DMGoMod + if p.pluginConfig.DependencyMode == DMUnknown { + goModGroup := new(errgroup.Group) + goModGroup.Go(func() error { + _, err := src.ReadFile(ctx, client.ReadRequest{Filename: "go.mod"}) + return err + }) + goModGroup.Go(func() error { + _, err := src.ReadFile(ctx, client.ReadRequest{Filename: "go.sum"}) + return err + }) + if err := goModGroup.Wait(); err == nil { + p.pluginConfig.DependencyMode = DMGoMod } } // Pick up the project context from the dependency metadata - switch p.dependencyMode { + switch p.pluginConfig.DependencyMode { case DMUnknown: return ErrUnknownDep case DMGoMod: - goMod, err := modfile.ParseLax("go.mod", goMod, nil) + data, err := src.ReadFile(ctx, client.ReadRequest{Filename: "go.mod"}) + if err != nil { + return errors.Wrap(err, "fail to read go.mod") + } + goMod, err := modfile.ParseLax("go.mod", data, nil) if err != nil { return errors.Wrap(err, "fail to parse go.mod") } if goMod.Go == nil || goMod.Module == nil { return ErrModIncomplete } - p.version = goMod.Go.Version + if p.pluginConfig.Version == "" { + p.pluginConfig.Version = goMod.Go.Version + } p.name = goMod.Module.Mod.Path } @@ -106,7 +146,7 @@ const ( // Build the image for this Go project. func (p *Plugin) Build(ctx context.Context, platform *specs.Platform, build cib.Service) (*llb.State, *dockerfile2llb.Image, error) { // Choose base image - base := "golang:" + p.version + base := "golang:" + p.pluginConfig.Version state, _, err := build.From( base, platform, @@ -127,27 +167,34 @@ func (p *Plugin) Build(ctx context.Context, platform *specs.Platform, build cib. llb.WithCustomName("Create build output directory"), ) // Build + args := []string{"go", "install", "-v"} + if len(p.pluginConfig.Tags) > 0 { + args = append(args, "-tags") + args = append(args, strings.Join(p.pluginConfig.Tags, ",")) + } + args = append(args, "./...") + run := []llb.RunOption{ // Mount source code llb.AddMount(dirSrc, src, llb.Readonly), // Install executables llb.AddEnv("GOBIN", dirInstall), - llb.Args([]string{"go", "install", "-v", "./..."}), + llb.Args(args), // Cache build outputs llb.AddMount( dirGoBuildCache, llb.Scratch(), - llb.AsPersistentCacheDir("go-build", llb.CacheMountShared), + llb.AsPersistentCacheDir("go-build", llb.CacheMountPrivate), ), llb.AddEnv("GOCACHE", dirGoBuildCache), llb.WithCustomNamef("Build %s", p.name), } - if p.dependencyMode == DMGoMod { + if p.pluginConfig.DependencyMode == DMGoMod { // Cache modules run = append(run, llb.AddMount( dirGoModCache, llb.Scratch(), - llb.AsPersistentCacheDir("go-mod", llb.CacheMountShared), + llb.AsPersistentCacheDir("go-mod", llb.CacheMountPrivate), )) } buildState := state.Dir(dirSrc).Run(run...).Root() @@ -185,5 +232,5 @@ func (p *Plugin) Build(ctx context.Context, platform *specs.Platform, build cib. func init() { // Register the plugin with the frontend. - packer2llb.Register(&Plugin{}) + packer2llb.Register(NewPlugin()) } diff --git a/pkg/plugins/golang/golang_test.go b/pkg/plugins/golang/golang_test.go index 71513b7..f11f3eb 100644 --- a/pkg/plugins/golang/golang_test.go +++ b/pkg/plugins/golang/golang_test.go @@ -33,13 +33,27 @@ func (suite *golangTestSuite) SetupTest() { suite.ctx = context.Background() suite.build = cib_mock.NewMockService(suite.ctrl) suite.src = cib_mock.NewMockReference(suite.ctrl) - suite.plugin = &Plugin{} + suite.plugin = NewPlugin() } func (suite *golangTestSuite) TearDownTest() { suite.ctrl.Finish() } +func (suite *golangTestSuite) TestInvalidConfig() { + // Arrange + cfg := cib.NewConfig() + cfg.Other["go"] = map[string]interface{}{ + "tags": "tag1", + } + + // Act + err := suite.plugin.Detect(suite.ctx, suite.src, cfg) + + // Assert + require.NotNil(suite.T(), err) +} + func (suite *golangTestSuite) TestDetectNotFound() { // Arrange req := client.ReadDirRequest{Path: "."} @@ -51,7 +65,7 @@ func (suite *golangTestSuite) TestDetectNotFound() { Return(files, nil) // Act - err := suite.plugin.Detect(suite.ctx, suite.src, nil) + err := suite.plugin.Detect(suite.ctx, suite.src, cib.NewConfig()) // Assert require.Nil(suite.T(), err) @@ -68,15 +82,41 @@ func (suite *golangTestSuite) TestDetectFoundGoSource() { Return(files, nil) suite.src.EXPECT(). ReadFile(suite.ctx, gomock.Any()). - Return(nil, errors.New("not found")) + Return(nil, errors.New("not found")). + Times(2) // Act - err := suite.plugin.Detect(suite.ctx, suite.src, nil) + err := suite.plugin.Detect(suite.ctx, suite.src, cib.NewConfig()) // Assert require.Same(suite.T(), ErrUnknownDep, err) } +func (suite *golangTestSuite) TestDetectGoModNotFound() { + // Arrange + req := client.ReadDirRequest{Path: "."} + files := []*fsutil.Stat{ + {Path: "hello.go"}, + } + suite.src.EXPECT(). + ReadDir(suite.ctx, req). + Return(files, nil) + suite.src.EXPECT(). + ReadFile(suite.ctx, gomock.Any()). + Return(nil, errors.New("not found")) + cfg := cib.NewConfig() + cfg.Other["go"] = map[string]interface{}{ + "dependencyMode": "modules", + } + + // Act + err := suite.plugin.Detect(suite.ctx, suite.src, cfg) + + // Assert + require.NotNil(suite.T(), err) + require.Contains(suite.T(), err.Error(), "fail to read go.mod") +} + func (suite *golangTestSuite) TestDetectGoModFails() { // Arrange req := client.ReadDirRequest{Path: "."} @@ -90,10 +130,10 @@ func (suite *golangTestSuite) TestDetectGoModFails() { suite.src.EXPECT(). ReadFile(suite.ctx, gomock.Any()). Return(goMod, nil). - Times(2) + Times(3) // Act - err := suite.plugin.Detect(suite.ctx, suite.src, nil) + err := suite.plugin.Detect(suite.ctx, suite.src, cib.NewConfig()) // Assert require.NotNil(suite.T(), err) @@ -112,10 +152,10 @@ func (suite *golangTestSuite) TestDetectGoModIncomplete() { suite.src.EXPECT(). ReadFile(suite.ctx, gomock.Any()). Return(goMod, nil). - Times(2) + Times(3) // Act - err := suite.plugin.Detect(suite.ctx, suite.src, nil) + err := suite.plugin.Detect(suite.ctx, suite.src, cib.NewConfig()) // Assert require.Same(suite.T(), ErrModIncomplete, err) @@ -138,18 +178,24 @@ go 1.15 suite.src.EXPECT(). ReadFile(suite.ctx, gomock.Any()). Return(goMod, nil). - Times(2) + Times(3) + tags := []string{"tag1", "tag2"} + cfg := cib.NewConfig() + cfg.Other["go"] = map[string]interface{}{ + "tags": tags, + } // Act - err := suite.plugin.Detect(suite.ctx, suite.src, nil) + err := suite.plugin.Detect(suite.ctx, suite.src, cfg) // Assert require.Same(suite.T(), packer2llb.ErrActivate, err) + require.Equal(suite.T(), tags, suite.plugin.pluginConfig.Tags) } func (suite *golangTestSuite) TestBuildFailsFrom1() { // Arrange - suite.plugin.version = "1.14" + suite.plugin.pluginConfig.Version = "1.14" platform := &specs.Platform{OS: "linux", Architecture: "amd64"} expected := errors.New("something went wrong") @@ -166,7 +212,7 @@ func (suite *golangTestSuite) TestBuildFailsFrom1() { func (suite *golangTestSuite) TestBuildFailsSrc() { // Arrange - suite.plugin.version = "1.14" + suite.plugin.pluginConfig.Version = "1.14" platform := &specs.Platform{OS: "linux", Architecture: "amd64"} suite.build.EXPECT(). @@ -187,8 +233,7 @@ func (suite *golangTestSuite) TestBuildFailsSrc() { func (suite *golangTestSuite) TestBuildFailsFrom2() { // Arrange - suite.plugin.version = "1.14" - suite.plugin.config = cib.NewConfig() + suite.plugin.pluginConfig.Version = "1.14" platform := &specs.Platform{OS: "linux", Architecture: "amd64"} suite.build.EXPECT(). @@ -212,9 +257,10 @@ func (suite *golangTestSuite) TestBuildFailsFrom2() { func (suite *golangTestSuite) TestBuildSucceeds() { // Arrange - suite.plugin.version = "1.14" - suite.plugin.config = cib.NewConfig() suite.plugin.config.Debug = false + suite.plugin.pluginConfig.DependencyMode = DMGoMod + suite.plugin.pluginConfig.Version = "1.14" + suite.plugin.pluginConfig.Tags = []string{"tag1", "tag2"} platform := &specs.Platform{OS: "linux", Architecture: "amd64"} suite.build.EXPECT(). diff --git a/tests/e2e_test.go b/tests/e2e_test.go new file mode 100644 index 0000000..e8c4918 --- /dev/null +++ b/tests/e2e_test.go @@ -0,0 +1,57 @@ +package e2e + +import ( + "bytes" + "os" + "os/exec" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" +) + +func build(t *testing.T, project string) { + // Arrange + var stderr bytes.Buffer + var stdout bytes.Buffer + cmd := exec.Command( + "docker", + "build", + "-t", + project, + "-f", "pack.yaml", + ".", + ) + cmd.Dir = filepath.Join(".", project) + cmd.Stderr = &stderr + cmd.Stdout = &stdout + cmd.Env = append(os.Environ(), "DOCKER_BUILDKIT=1") + + // Act + err := cmd.Run() + + // Assert + if err != nil { + t.Log("stdout: ", stdout.String()) + t.Log("stderr: ", stderr.String()) + t.Log(err) + t.Fail() + } +} + +func TestGoCli(t *testing.T) { + // Arrange + project := "go-cli-template-master" + build(t, project) + + var stdout bytes.Buffer + cmd := exec.Command("docker", "run", "--rm", project) + cmd.Stdout = &stdout + + // Act + err := cmd.Run() + + // Assert + require.Nil(t, err) + require.Contains(t, stdout.String(), "cli-template") +} diff --git a/tests/go-cli-template-master/.circleci/config.yml b/tests/go-cli-template-master/.circleci/config.yml new file mode 100644 index 0000000..dc82a3a --- /dev/null +++ b/tests/go-cli-template-master/.circleci/config.yml @@ -0,0 +1,36 @@ +defaults: &defaults + docker: + - image: circleci/golang:1.16 + environment: + GO111MODULE: "on" + +version: 2 +jobs: + test: + <<: *defaults + steps: + - checkout + - restore_cache: + keys: + - go-module-cache-v1-{{ checksum "~/project/go.sum" }} + - go-module-cache-v1- + - run: go mod download + - save_cache: + key: go-module-cache-v1-{{ checksum "~/project/go.sum" }} + paths: + - ~/go/pkg/mod/cache + - run: make setup + - run: make build + - run: make test + - run: make codecov + +workflows: + version: 2 + test_and_release: + jobs: + - test: + filters: + branches: + only: /.*/ + tags: + only: /.*/ diff --git a/tests/go-cli-template-master/.dockerignore b/tests/go-cli-template-master/.dockerignore new file mode 100644 index 0000000..1d1fe94 --- /dev/null +++ b/tests/go-cli-template-master/.dockerignore @@ -0,0 +1 @@ +Dockerfile \ No newline at end of file diff --git a/tests/go-cli-template-master/.github/workflows/gcp-preview.yml b/tests/go-cli-template-master/.github/workflows/gcp-preview.yml new file mode 100644 index 0000000..2d0ca36 --- /dev/null +++ b/tests/go-cli-template-master/.github/workflows/gcp-preview.yml @@ -0,0 +1,88 @@ +name: Build and Deploy to Cloud Run + +on: + pull_request: + types: [synchronize, opened, reopened, closed] + +env: + PROJECT: mpppk-workspace + HOSTNAME: asia.gcr.io + REGION: asia-northeast1 + SERVICE: preview-${{ github.event.number }} + +jobs: + setup-build-publish-deploy: + name: Setup, Build, Publish, and Deploy + if: github.event.action == 'opened' || github.event.action == 'reopened' || github.event.action == 'synchronize' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + # Setup gcloud CLI + - uses: GoogleCloudPlatform/github-actions/setup-gcloud@master + with: + version: '274.0.0' + service_account_key: ${{ secrets.GCP_KEY }} + + # Configure gcloud + - run: | + gcloud config set project ${PROJECT} + gcloud config set run/platform managed + gcloud config set run/region ${REGION} + gcloud auth configure-docker + + # Build the Docker image + - name: Build + run: | + docker build -t ${HOSTNAME}/${PROJECT}/${SERVICE} . + + # Publish the Docker image to GCR + - name: Publish + run: | + docker push ${HOSTNAME}/${PROJECT}/${SERVICE} + + # Deploy the Docker image to the Cloud Run + - name: Deploy + run: | + gcloud run deploy ${SERVICE} --image ${HOSTNAME}/${PROJECT}/${SERVICE} --allow-unauthenticated + PREVIEW_URL=$(gcloud run services describe ${SERVICE} --format 'value(status.url)') + gcloud run services update ${SERVICE} --set-env-vars BASE_URL=${PREVIEW_URL} + + # Notify to Statuses + - name: Notify + run: | + PREVIEW_URL=$(gcloud run services describe ${SERVICE} --format 'value(status.url)') + curl -X POST \ + --url ${{ github.event.pull_request._links.statuses.href }} \ + -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ + -H "Content-Type: application/json" \ + -d "{ + \"state\": \"success\", + \"target_url\": \"${PREVIEW_URL}\", + \"description\": \"Deploy preview ready!\", + \"context\": \"deploy/preview\" + }" + + cleanup-preview: + name: Cleanup the Preview + if: github.event.action == 'closed' + runs-on: ubuntu-latest + steps: + # Setup gcloud CLI + - uses: GoogleCloudPlatform/github-actions/setup-gcloud@master + with: + version: '274.0.0' + service_account_key: ${{ secrets.GCP_KEY }} + + # Configure gcloud + - run: | + gcloud config set project ${PROJECT} + gcloud config set run/platform managed + gcloud config set run/region ${REGION} + gcloud auth configure-docker + + - name: Delete the Cloud Run Service + run: gcloud --quiet run services delete ${SERVICE} + + - name: Delete the Docker image in GCR + run: gcloud container images delete ${HOSTNAME}/${PROJECT}/${SERVICE} \ No newline at end of file diff --git a/tests/go-cli-template-master/.github/workflows/go.yml b/tests/go-cli-template-master/.github/workflows/go.yml new file mode 100644 index 0000000..ea2c0ba --- /dev/null +++ b/tests/go-cli-template-master/.github/workflows/go.yml @@ -0,0 +1,44 @@ +name: Go +on: [push] +jobs: + + build: + name: Lint, Test, Build + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: + - ubuntu-latest + - windows-latest + - macOS-latest + goversion: + - 1.14 + steps: + + - name: Set up Go + uses: actions/setup-go@v1 + with: + go-version: ${{ matrix.goversion }} + id: go + + - name: Check out code into the Go module directory + uses: actions/checkout@v1 + + - name: setup env + run: | + echo "::set-env name=GOPATH::$(go env GOPATH)" + echo "::add-path::$(go env GOPATH)/bin" + env: + ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true' + + - name: Setup + run: make setup + + - name: Lint + run: make lint + + - name: Test + run: make test + + - name: Build + run: make build diff --git a/tests/go-cli-template-master/.github/workflows/release.yml b/tests/go-cli-template-master/.github/workflows/release.yml new file mode 100644 index 0000000..c5e9512 --- /dev/null +++ b/tests/go-cli-template-master/.github/workflows/release.yml @@ -0,0 +1,35 @@ +name: release +on: + push: + tags: + - "v[0-9]+.[0-9]+.[0-9]+" +jobs: + goreleaser: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Unshallow + run: git fetch --prune --unshallow + + - name: Set up Go + uses: actions/setup-go@v1 + with: + go-version: 1.14 + + - name: setup env + run: | + echo "::set-env name=GOPATH::$(go env GOPATH)" + echo "::add-path::$(go env GOPATH)/bin" + env: + ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true' + + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v1 + with: + version: latest + args: release --rm-dist + key: ${{ secrets.YOUR_PRIVATE_KEY }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/tests/go-cli-template-master/.gitignore b/tests/go-cli-template-master/.gitignore new file mode 100644 index 0000000..770cc7d --- /dev/null +++ b/tests/go-cli-template-master/.gitignore @@ -0,0 +1,3 @@ +.idea/ +vendor/ +registry/wire_gen.go \ No newline at end of file diff --git a/tests/go-cli-template-master/.goreleaser.yml b/tests/go-cli-template-master/.goreleaser.yml new file mode 100644 index 0000000..b3d9377 --- /dev/null +++ b/tests/go-cli-template-master/.goreleaser.yml @@ -0,0 +1,28 @@ +before: + hooks: + - make setup + - git checkout -- go.mod + - git checkout -- go.sum + - make generate +builds: +- goos: + - darwin + - linux + - windows + env: + - CGO_ENABLED=0 + - GO111MODULE=on +archives: +- format_overrides: + - goos: windows + format: zip +checksum: + name_template: 'checksums.txt' +snapshot: + name_template: "{{ .Tag }}-next" +changelog: + sort: asc + filters: + exclude: + - '^docs:' + - '^test:' diff --git a/tests/go-cli-template-master/Dockerfile b/tests/go-cli-template-master/Dockerfile new file mode 100644 index 0000000..b4face5 --- /dev/null +++ b/tests/go-cli-template-master/Dockerfile @@ -0,0 +1,18 @@ +FROM golang:1-buster AS builder +ENV GO111MODULE on +RUN mkdir /src +WORKDIR /src +COPY go.mod . +COPY go.sum . +RUN go mod download + +COPY . /src +WORKDIR /src +RUN make setup +RUN make build + +FROM alpine:3.13 +RUN mkdir /lib64 +RUN ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2 +COPY --from=builder /src/cli-template /usr/local/bin +ENTRYPOINT ["/usr/local/bin/cli-template", "serve"] diff --git a/tests/go-cli-template-master/LICENSE b/tests/go-cli-template-master/LICENSE new file mode 100644 index 0000000..ee521b3 --- /dev/null +++ b/tests/go-cli-template-master/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright © 2018 mpppk + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/tests/go-cli-template-master/Makefile b/tests/go-cli-template-master/Makefile new file mode 100644 index 0000000..854d6a2 --- /dev/null +++ b/tests/go-cli-template-master/Makefile @@ -0,0 +1,51 @@ +SHELL = /bin/bash + +.PHONY: setup +setup: + go get github.com/google/wire/cmd/wire + go get github.com/goreleaser/goreleaser + +.PHONY: lint +lint: generate + go vet ./... + goreleaser check + +.PHONY: test +test: generate + go test ./... + +.PHONY: integration-test +integration-test: + go test -tags=integration ./... + +.PHONY: coverage +coverage: generate + go test -race -coverprofile=coverage.txt -covermode=atomic ./... + +.PHONY: codecov +codecov: coverage + bash <(curl -s https://codecov.io/bash) + +.PHONY: wire +wire: + go generate -tags=wireinject ./... + +.PHONY: generate +generate: wire + go generate ./... + +.PHONY: build +build: generate + go build + +.PHONY: cross-build-snapshot +cross-build: + goreleaser --rm-dist --snapshot + +.PHONY: install +install: + go install + +.PHONY: circleci +circleci: + circleci build -e GITHUB_TOKEN=$GITHUB_TOKEN \ No newline at end of file diff --git a/tests/go-cli-template-master/appveyor.yml b/tests/go-cli-template-master/appveyor.yml new file mode 100644 index 0000000..0c5c567 --- /dev/null +++ b/tests/go-cli-template-master/appveyor.yml @@ -0,0 +1,26 @@ +version: "{build}" + +platform: x64 + +clone_folder: c:\gopath\src\github.com\mpppk\cli-template + +environment: + GOPATH: c:\gopath + GO111MODULE: on + +install: + - set PATH=%GOPATH%/bin;%PATH% + - echo %PATH% + - echo %GOPATH% + - git submodule update --init --recursive + - go version + - go env + - go get github.com/google/wire/cmd/wire + - go get -v -t -d ./... + - go generate -tags=wireinject ./... + - go vet ./... + +build_script: + - go test -v ./... + - go build + diff --git a/tests/go-cli-template-master/cmd/cmd.go b/tests/go-cli-template-master/cmd/cmd.go new file mode 100644 index 0000000..5fac496 --- /dev/null +++ b/tests/go-cli-template-master/cmd/cmd.go @@ -0,0 +1,10 @@ +package cmd + +import ( + "github.com/spf13/afero" + "github.com/spf13/cobra" +) + +type cmdGenerator func(fs afero.Fs) (*cobra.Command, error) + +var cmdGenerators []cmdGenerator diff --git a/tests/go-cli-template-master/cmd/option/cmd.go b/tests/go-cli-template-master/cmd/option/cmd.go new file mode 100644 index 0000000..f2d5efb --- /dev/null +++ b/tests/go-cli-template-master/cmd/option/cmd.go @@ -0,0 +1,3 @@ +package option + +var DefaultStringValue = "__DEFAULT_STRING_VALUE__" diff --git a/tests/go-cli-template-master/cmd/option/config.go b/tests/go-cli-template-master/cmd/option/config.go new file mode 100644 index 0000000..61e8357 --- /dev/null +++ b/tests/go-cli-template-master/cmd/option/config.go @@ -0,0 +1,99 @@ +// Package option provides utilities of option handling +package option + +import ( + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +// BaseFlag represents base command line flag +type BaseFlag struct { + IsPersistent bool + IsRequired bool + Shorthand string + Name string + Usage string + ViperName string +} + +// Flag represents flag which has base flag +type Flag interface { + getBaseFlag() *BaseFlag +} + +func (f *BaseFlag) getBaseFlag() *BaseFlag { + return f +} + +func (f *BaseFlag) getViperName() string { + if f.ViperName == "" { + return f.Name + } + return f.ViperName +} + +func getFlagSet(cmd *cobra.Command, flagConfig *BaseFlag) (flagSet *pflag.FlagSet) { + if flagConfig.IsPersistent { + return cmd.PersistentFlags() + } else { + return cmd.Flags() + } +} + +func markAttributes(cmd *cobra.Command, flagConfig *StringFlag) error { + if err := markAsFileName(cmd, flagConfig); err != nil { + return err + } + if err := markAsDirName(cmd, flagConfig); err != nil { + return err + } + if err := markAsRequired(cmd, flagConfig.BaseFlag); err != nil { + return err + } + return nil +} + +func markAsFileName(cmd *cobra.Command, stringFlag *StringFlag) error { + if stringFlag.IsFileName { + if stringFlag.IsPersistent { + if err := cmd.MarkPersistentFlagFilename(stringFlag.Name); err != nil { + return err + } + } else { + if err := cmd.MarkFlagFilename(stringFlag.Name); err != nil { + return err + } + } + } + return nil +} + +func markAsDirName(cmd *cobra.Command, stringFlag *StringFlag) error { + if stringFlag.IsDirName { + if stringFlag.IsPersistent { + if err := cmd.MarkPersistentFlagDirname(stringFlag.Name); err != nil { + return err + } + } else { + if err := cmd.MarkFlagDirname(stringFlag.Name); err != nil { + return err + } + } + } + return nil +} + +func markAsRequired(cmd *cobra.Command, flagConfig *BaseFlag) error { + if flagConfig.IsRequired { + if flagConfig.IsPersistent { + if err := cmd.MarkPersistentFlagRequired(flagConfig.Name); err != nil { + return err + } + } else { + if err := cmd.MarkFlagRequired(flagConfig.Name); err != nil { + return err + } + } + } + return nil +} diff --git a/tests/go-cli-template-master/cmd/option/flag.go b/tests/go-cli-template-master/cmd/option/flag.go new file mode 100644 index 0000000..73bdb8e --- /dev/null +++ b/tests/go-cli-template-master/cmd/option/flag.go @@ -0,0 +1,308 @@ +package option + +import ( + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +// StringFlag represents flag which can be specified as string +type StringFlag struct { + *BaseFlag + Value string + IsDirName bool + IsFileName bool +} + +// BoolFlag represents flag which can be specified as bool +type BoolFlag struct { + *BaseFlag + Value bool +} + +// IntFlag represents flag which can be specified as int +type IntFlag struct { + *BaseFlag + Value int +} + +// Int8Flag represents flag which can be specified as int8 +type Int8Flag struct { + *BaseFlag + Value int8 +} + +// Int16Flag represents flag which can be specified as int16 +type Int16Flag struct { + *BaseFlag + Value int16 +} + +// Int32Flag represents flag which can be specified as int32 +type Int32Flag struct { + *BaseFlag + Value int32 +} + +// Int64Flag represents flag which can be specified as int64 +type Int64Flag struct { + *BaseFlag + Value int64 +} + +// UintFlag represents flag which can be specified as uint +type UintFlag struct { + *BaseFlag + Value uint +} + +// Uint8Flag represents flag which can be specified as uint8 +type Uint8Flag struct { + *BaseFlag + Value uint8 +} + +// Uint16Flag represents flag which can be specified as uint16 +type Uint16Flag struct { + *BaseFlag + Value uint16 +} + +// Uint32Flag represents flag which can be specified as uint32 +type Uint32Flag struct { + *BaseFlag + Value uint32 +} + +// Uint64Flag represents flag which can be specified as uint64 +type Uint64Flag struct { + *BaseFlag + Value uint64 +} + +// Float32Flag represents flag which can be specified as float32 +type Float32Flag struct { + *BaseFlag + Value float32 +} + +// Float64Flag represents flag which can be specified as float64 +type Float64Flag struct { + *BaseFlag + Value float64 +} + +// RegisterFlags register flags to provided cmd and viper +func RegisterFlags(cmd *cobra.Command, flags []Flag) error { + for _, flag := range flags { + if err := RegisterFlag(cmd, flag); err != nil { + return err + } + } + return nil +} + +// RegisterFlag register flag to provided cmd and viper +func RegisterFlag(cmd *cobra.Command, flag Flag) error { + baseFlag := flag.getBaseFlag() + flagSet := getFlagSet(cmd, baseFlag) + + var rerr error + switch f := flag.(type) { + case *StringFlag: + rerr = RegisterStringFlag(cmd, f) + case *BoolFlag: + rerr = RegisterBoolFlag(cmd, f) + case *IntFlag: + rerr = RegisterIntFlag(cmd, f) + case *Int8Flag: + rerr = RegisterInt8Flag(cmd, f) + case *Int16Flag: + rerr = RegisterInt16Flag(cmd, f) + case *Int32Flag: + rerr = RegisterInt32Flag(cmd, f) + case *Int64Flag: + rerr = RegisterInt64Flag(cmd, f) + case *UintFlag: + rerr = RegisterUintFlag(cmd, f) + case *Uint8Flag: + rerr = RegisterUint8Flag(cmd, f) + case *Uint16Flag: + rerr = RegisterUint16Flag(cmd, f) + case *Uint32Flag: + rerr = RegisterUint32Flag(cmd, f) + case *Uint64Flag: + rerr = RegisterUint64Flag(cmd, f) + case *Float32Flag: + rerr = RegisterFloat32Flag(cmd, f) + case *Float64Flag: + rerr = RegisterFloat64Flag(cmd, f) + } + + if rerr != nil { + return rerr + } + + if err := markAsRequired(cmd, baseFlag); err != nil { + return err + } + + if err := viper.BindPFlag(baseFlag.getViperName(), flagSet.Lookup(baseFlag.Name)); err != nil { + return err + } + return nil +} + +// RegisterStringFlag register string flag to provided cmd and viper +func RegisterStringFlag(cmd *cobra.Command, flagConfig *StringFlag) error { + flagSet := getFlagSet(cmd, flagConfig.BaseFlag) + if flagConfig.Shorthand == "" { + flagSet.String(flagConfig.Name, flagConfig.Value, flagConfig.Usage) + } else { + flagSet.StringP(flagConfig.Name, flagConfig.Shorthand, flagConfig.Value, flagConfig.Usage) + } + + return markAttributes(cmd, flagConfig) +} + +// RegisterBoolFlag register bool flag to provided cmd and viper +func RegisterBoolFlag(cmd *cobra.Command, flagConfig *BoolFlag) error { + flagSet := getFlagSet(cmd, flagConfig.BaseFlag) + if flagConfig.Shorthand == "" { + flagSet.Bool(flagConfig.Name, flagConfig.Value, flagConfig.Usage) + } else { + flagSet.BoolP(flagConfig.Name, flagConfig.Shorthand, flagConfig.Value, flagConfig.Usage) + } + return nil +} + +// RegisterIntFlag register int flag to provided cmd and viper +func RegisterIntFlag(cmd *cobra.Command, flagConfig *IntFlag) error { + flagSet := getFlagSet(cmd, flagConfig.BaseFlag) + if flagConfig.Shorthand == "" { + flagSet.Int(flagConfig.Name, flagConfig.Value, flagConfig.Usage) + } else { + flagSet.IntP(flagConfig.Name, flagConfig.Shorthand, flagConfig.Value, flagConfig.Usage) + } + return nil +} + +// RegisterInt8Flag register int8 flag to provided cmd and viper +func RegisterInt8Flag(cmd *cobra.Command, flagConfig *Int8Flag) error { + flagSet := getFlagSet(cmd, flagConfig.BaseFlag) + if flagConfig.Shorthand == "" { + flagSet.Int8(flagConfig.Name, flagConfig.Value, flagConfig.Usage) + } else { + flagSet.Int8P(flagConfig.Name, flagConfig.Shorthand, flagConfig.Value, flagConfig.Usage) + } + return nil +} + +// RegisterInt16Flag register int16 flag to provided cmd and viper +func RegisterInt16Flag(cmd *cobra.Command, flagConfig *Int16Flag) error { + flagSet := getFlagSet(cmd, flagConfig.BaseFlag) + if flagConfig.Shorthand == "" { + flagSet.Int16(flagConfig.Name, flagConfig.Value, flagConfig.Usage) + } else { + flagSet.Int16P(flagConfig.Name, flagConfig.Shorthand, flagConfig.Value, flagConfig.Usage) + } + return nil +} + +// RegisterInt32Flag register int32 flag to provided cmd and viper +func RegisterInt32Flag(cmd *cobra.Command, flagConfig *Int32Flag) error { + flagSet := getFlagSet(cmd, flagConfig.BaseFlag) + if flagConfig.Shorthand == "" { + flagSet.Int32(flagConfig.Name, flagConfig.Value, flagConfig.Usage) + } else { + flagSet.Int32P(flagConfig.Name, flagConfig.Shorthand, flagConfig.Value, flagConfig.Usage) + } + return nil +} + +// RegisterInt64Flag register int64 flag to provided cmd and viper +func RegisterInt64Flag(cmd *cobra.Command, flagConfig *Int64Flag) error { + flagSet := getFlagSet(cmd, flagConfig.BaseFlag) + if flagConfig.Shorthand == "" { + flagSet.Int64(flagConfig.Name, flagConfig.Value, flagConfig.Usage) + } else { + flagSet.Int64P(flagConfig.Name, flagConfig.Shorthand, flagConfig.Value, flagConfig.Usage) + } + return nil +} + +// RegisterUintFlag register int flag to provided cmd and viper +func RegisterUintFlag(cmd *cobra.Command, flagConfig *UintFlag) error { + flagSet := getFlagSet(cmd, flagConfig.BaseFlag) + if flagConfig.Shorthand == "" { + flagSet.Uint(flagConfig.Name, flagConfig.Value, flagConfig.Usage) + } else { + flagSet.UintP(flagConfig.Name, flagConfig.Shorthand, flagConfig.Value, flagConfig.Usage) + } + return nil +} + +// RegisterUint8Flag register int8 flag to provided cmd and viper +func RegisterUint8Flag(cmd *cobra.Command, flagConfig *Uint8Flag) error { + flagSet := getFlagSet(cmd, flagConfig.BaseFlag) + if flagConfig.Shorthand == "" { + flagSet.Uint8(flagConfig.Name, flagConfig.Value, flagConfig.Usage) + } else { + flagSet.Uint8P(flagConfig.Name, flagConfig.Shorthand, flagConfig.Value, flagConfig.Usage) + } + return nil +} + +// RegisterUint16Flag register int16 flag to provided cmd and viper +func RegisterUint16Flag(cmd *cobra.Command, flagConfig *Uint16Flag) error { + flagSet := getFlagSet(cmd, flagConfig.BaseFlag) + if flagConfig.Shorthand == "" { + flagSet.Uint16(flagConfig.Name, flagConfig.Value, flagConfig.Usage) + } else { + flagSet.Uint16P(flagConfig.Name, flagConfig.Shorthand, flagConfig.Value, flagConfig.Usage) + } + return nil +} + +// RegisterUint32Flag register int32 flag to provided cmd and viper +func RegisterUint32Flag(cmd *cobra.Command, flagConfig *Uint32Flag) error { + flagSet := getFlagSet(cmd, flagConfig.BaseFlag) + if flagConfig.Shorthand == "" { + flagSet.Uint32(flagConfig.Name, flagConfig.Value, flagConfig.Usage) + } else { + flagSet.Uint32P(flagConfig.Name, flagConfig.Shorthand, flagConfig.Value, flagConfig.Usage) + } + return nil +} + +// RegisterUint64Flag register int64 flag to provided cmd and viper +func RegisterUint64Flag(cmd *cobra.Command, flagConfig *Uint64Flag) error { + flagSet := getFlagSet(cmd, flagConfig.BaseFlag) + if flagConfig.Shorthand == "" { + flagSet.Uint64(flagConfig.Name, flagConfig.Value, flagConfig.Usage) + } else { + flagSet.Uint64P(flagConfig.Name, flagConfig.Shorthand, flagConfig.Value, flagConfig.Usage) + } + return nil +} + +// RegisterFloat32Flag register int32 flag to provided cmd and viper +func RegisterFloat32Flag(cmd *cobra.Command, flagConfig *Float32Flag) error { + flagSet := getFlagSet(cmd, flagConfig.BaseFlag) + if flagConfig.Shorthand == "" { + flagSet.Float32(flagConfig.Name, flagConfig.Value, flagConfig.Usage) + } else { + flagSet.Float32P(flagConfig.Name, flagConfig.Shorthand, flagConfig.Value, flagConfig.Usage) + } + return nil +} + +// RegisterFloat64Flag register int64 flag to provided cmd and viper +func RegisterFloat64Flag(cmd *cobra.Command, flagConfig *Float64Flag) error { + flagSet := getFlagSet(cmd, flagConfig.BaseFlag) + if flagConfig.Shorthand == "" { + flagSet.Float64(flagConfig.Name, flagConfig.Value, flagConfig.Usage) + } else { + flagSet.Float64P(flagConfig.Name, flagConfig.Shorthand, flagConfig.Value, flagConfig.Usage) + } + return nil +} diff --git a/tests/go-cli-template-master/cmd/option/root.go b/tests/go-cli-template-master/cmd/option/root.go new file mode 100644 index 0000000..5d48926 --- /dev/null +++ b/tests/go-cli-template-master/cmd/option/root.go @@ -0,0 +1,21 @@ +package option + +import ( + "fmt" + + "github.com/spf13/viper" +) + +// RootCmdConfig is config for root command +type RootCmdConfig struct { + Verbose bool +} + +// NewRootCmdConfigFromViper generate config for sum command from viper +func NewRootCmdConfigFromViper() (*RootCmdConfig, error) { + var conf RootCmdConfig + if err := viper.Unmarshal(&conf); err != nil { + return nil, fmt.Errorf("failed to unmarshal config from viper: %w", err) + } + return &conf, nil +} diff --git a/tests/go-cli-template-master/cmd/option/serve.go b/tests/go-cli-template-master/cmd/option/serve.go new file mode 100644 index 0000000..054008b --- /dev/null +++ b/tests/go-cli-template-master/cmd/option/serve.go @@ -0,0 +1,21 @@ +package option + +import ( + "fmt" + + "github.com/spf13/viper" +) + +// ServeCmdConfig is config for serve command +type ServeCmdConfig struct { + Port string +} + +// NewServeCmdConfigFromViper generate config for serve command from viper +func NewServeCmdConfigFromViper() (*ServeCmdConfig, error) { + var conf ServeCmdConfig + if err := viper.Unmarshal(&conf); err != nil { + return nil, fmt.Errorf("failed to unmarshal config from viper: %w", err) + } + return &conf, nil +} diff --git a/tests/go-cli-template-master/cmd/option/sum.go b/tests/go-cli-template-master/cmd/option/sum.go new file mode 100644 index 0000000..8457925 --- /dev/null +++ b/tests/go-cli-template-master/cmd/option/sum.go @@ -0,0 +1,44 @@ +package option + +import ( + "fmt" + "github.com/mpppk/cli-template/util" + + "github.com/spf13/viper" +) + +// SumCmdConfig is config for sum command +type SumCmdConfig struct { + Norm bool + Out string + Numbers []int +} + +// NewSumCmdConfigFromViper generate config for sum command from viper +func NewSumCmdConfigFromViper(args []string) (*SumCmdConfig, error) { + var conf SumCmdConfig + if err := viper.Unmarshal(&conf); err != nil { + return nil, fmt.Errorf("failed to unmarshal config from viper: %w", err) + } + + if err := conf.validate(); err != nil { + return nil, fmt.Errorf("failed to create sum cmd config: %w", err) + } + + numbers, err := util.ConvertStringSliceToIntSlice(args) + if err != nil { + return nil, fmt.Errorf("failed to convert args to numbers. args=%q: %w", args, err) + } + + conf.Numbers = numbers + return &conf, nil +} + +// HasOut returns whether or not config has Out property +func (c *SumCmdConfig) HasOut() bool { + return c.Out != "" +} + +func (c *SumCmdConfig) validate() error { + return nil +} diff --git a/tests/go-cli-template-master/cmd/option/sum_test.go b/tests/go-cli-template-master/cmd/option/sum_test.go new file mode 100644 index 0000000..1793897 --- /dev/null +++ b/tests/go-cli-template-master/cmd/option/sum_test.go @@ -0,0 +1,47 @@ +package option_test + +import ( + "testing" + + "github.com/mpppk/cli-template/cmd/option" +) + +func TestSumCmdConfig_HasOut(t *testing.T) { + type fields struct { + Norm bool + Out string + } + tests := []struct { + name string + fields fields + want bool + }{ + { + name: "should return true if Out is not default value", + fields: fields{ + Norm: false, + Out: "test.txt", + }, + want: true, + }, + { + name: "should return false if out is default value", + fields: fields{ + Norm: false, + Out: "", + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &option.SumCmdConfig{ + Norm: tt.fields.Norm, + Out: tt.fields.Out, + } + if got := c.HasOut(); got != tt.want { + t.Errorf("SumCmdConfig.HasOut() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/tests/go-cli-template-master/cmd/root.go b/tests/go-cli-template-master/cmd/root.go new file mode 100644 index 0000000..dd2ac2c --- /dev/null +++ b/tests/go-cli-template-master/cmd/root.go @@ -0,0 +1,123 @@ +package cmd + +import ( + "fmt" + "os" + + "github.com/mpppk/cli-template/util" + + "github.com/mpppk/cli-template/cmd/option" + + "github.com/spf13/afero" + + "github.com/mitchellh/go-homedir" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +var cfgFile string + +// NewRootCmd generate root cmd +func NewRootCmd(fs afero.Fs) (*cobra.Command, error) { + pPreRunE := func(cmd *cobra.Command, args []string) error { + conf, err := option.NewRootCmdConfigFromViper() + if err != nil { + return err + } + util.InitializeLog(conf.Verbose) + return nil + } + + cmd := &cobra.Command{ + Use: "cli-template", + Short: "cli-template", + SilenceErrors: true, + SilenceUsage: true, + PersistentPreRunE: pPreRunE, + } + + if err := registerSubCommands(fs, cmd); err != nil { + return nil, err + } + + if err := registerFlags(cmd); err != nil { + return nil, err + } + + return cmd, nil +} + +func registerSubCommands(fs afero.Fs, cmd *cobra.Command) error { + var subCmds []*cobra.Command + for _, cmdGen := range cmdGenerators { + subCmd, err := cmdGen(fs) + if err != nil { + return err + } + subCmds = append(subCmds, subCmd) + } + cmd.AddCommand(subCmds...) + return nil +} + +func registerFlags(cmd *cobra.Command) error { + flags := []option.Flag{ + &option.StringFlag{ + BaseFlag: &option.BaseFlag{ + Name: "config", + IsPersistent: true, + Usage: "config file (default is $HOME/.cli-template.yaml)", + }}, + &option.BoolFlag{ + BaseFlag: &option.BaseFlag{ + Name: "verbose", + Shorthand: "v", + IsPersistent: true, + Usage: "Show more logs", + }}, + } + return option.RegisterFlags(cmd, flags) +} + +// Execute adds all child commands to the root command and sets flags appropriately. +// This is called by main.main(). It only needs to happen once to the rootCmd. +func Execute() { + rootCmd, err := NewRootCmd(afero.NewOsFs()) + if err != nil { + panic(err) + } + if err := rootCmd.Execute(); err != nil { + fmt.Print(util.PrettyPrintError(err)) + os.Exit(1) + } +} + +func init() { + cobra.OnInitialize(initConfig) +} + +// initConfig reads in config file and ENV variables if set. +func initConfig() { + if cfgFile != "" { + // Use config file from the flag. + viper.SetConfigFile(cfgFile) + } else { + // Find home directory. + home, err := homedir.Dir() + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + // Search config in home directory with name ".cli-template" (without extension). + viper.AddConfigPath(home) + viper.SetConfigName(".cli-template") + } + + viper.AutomaticEnv() // read in environment variables that match + + // If a config file is found, read it in. + if err := viper.ReadInConfig(); err == nil { + fmt.Println("Using config file:", viper.ConfigFileUsed()) + } +} diff --git a/tests/go-cli-template-master/cmd/selfupdate.go b/tests/go-cli-template-master/cmd/selfupdate.go new file mode 100644 index 0000000..aa8a5a3 --- /dev/null +++ b/tests/go-cli-template-master/cmd/selfupdate.go @@ -0,0 +1,32 @@ +package cmd + +import ( + "github.com/mpppk/cli-template/util" + "github.com/spf13/afero" + "github.com/spf13/cobra" +) + +func newSelfUpdateCmd(fs afero.Fs) (*cobra.Command, error) { + cmd := &cobra.Command{ + Use: "selfupdate", + Short: "Update cli-template", + //Long: `Update cli-template`, + Run: func(cmd *cobra.Command, args []string) { + updated, err := util.Do() + if err != nil { + cmd.Println("Binary update failed:", err) + return + } + if updated { + cmd.Println("Current binary is the latest version", util.Version) + } else { + cmd.Println("Successfully updated to version", util.Version) + } + }, + } + return cmd, nil +} + +func init() { + cmdGenerators = append(cmdGenerators, newSelfUpdateCmd) +} diff --git a/tests/go-cli-template-master/cmd/serve.go b/tests/go-cli-template-master/cmd/serve.go new file mode 100644 index 0000000..8e289b7 --- /dev/null +++ b/tests/go-cli-template-master/cmd/serve.go @@ -0,0 +1,52 @@ +package cmd + +import ( + "github.com/mpppk/cli-template/cmd/option" + "github.com/mpppk/cli-template/registry" + "github.com/spf13/afero" + "github.com/spf13/viper" + + "github.com/spf13/cobra" +) + +func newServeCmd(fs afero.Fs) (*cobra.Command, error) { + cmd := &cobra.Command{ + Use: "serve", + Short: "Run server", + Long: ``, + RunE: func(cmd *cobra.Command, args []string) error { + conf, err := option.NewServeCmdConfigFromViper() + if err != nil { + return err + } + e := registry.InitializeServer(nil) + e.Logger.Fatal(e.Start(":" + conf.Port)) + return nil + }, + } + if err := registerServeCommandFlags(cmd); err != nil { + return nil, err + } + return cmd, nil +} + +func registerServeCommandFlags(cmd *cobra.Command) error { + flags := []option.Flag{ + &option.Uint16Flag{ + BaseFlag: &option.BaseFlag{ + Name: "port", + Usage: "server port", + }, + Value: 1323, + }, + } + + if err := viper.BindEnv("port"); err != nil { + return err + } + return option.RegisterFlags(cmd, flags) +} + +func init() { + cmdGenerators = append(cmdGenerators, newServeCmd) +} diff --git a/tests/go-cli-template-master/cmd/sum.go b/tests/go-cli-template-master/cmd/sum.go new file mode 100644 index 0000000..5412993 --- /dev/null +++ b/tests/go-cli-template-master/cmd/sum.go @@ -0,0 +1,94 @@ +package cmd + +import ( + "fmt" + "log" + "strconv" + + "github.com/mpppk/cli-template/registry" + + "github.com/mpppk/cli-template/cmd/option" + "github.com/spf13/afero" + + "github.com/spf13/cobra" +) + +func newSumCmd(fs afero.Fs) (*cobra.Command, error) { + cmd := &cobra.Command{ + Use: "sum", + Short: "Print sum of arguments", + Long: ``, + Args: cobra.MinimumNArgs(2), + Example: "cli-template sum -- -1 2 -> 1", + PreRunE: func(cmd *cobra.Command, args []string) error { + for _, arg := range args { + if _, err := strconv.Atoi(arg); err != nil { + return fmt.Errorf("failed to convert args to int from %q: %w", arg, err) + } + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + conf, err := option.NewSumCmdConfigFromViper(args) + if err != nil { + return err + } + + useCase := registry.InitializeSumUseCase(nil) + + var result int + if conf.Norm { + log.Println("start L1 Norm calculation") + r := useCase.CalcL1Norm(conf.Numbers) + log.Println("finish L1 Norm calculation") + result = r + } else { + log.Println("start sum calculation") + r := useCase.CalcSum(conf.Numbers) + log.Println("finish sum calculation") + result = r + } + + if conf.HasOut() { + s := strconv.Itoa(result) + if err := afero.WriteFile(fs, conf.Out, []byte(s), 777); err != nil { + return fmt.Errorf("failed to write file to %s: %w", conf.Out, err) + } + log.Println("result is written to " + conf.Out) + } else { + cmd.Println(result) + } + + return nil + }, + } + + if err := registerSumCommandFlags(cmd); err != nil { + return nil, err + } + + return cmd, nil +} + +func registerSumCommandFlags(cmd *cobra.Command) error { + flags := []option.Flag{ + &option.BoolFlag{ + BaseFlag: &option.BaseFlag{ + Name: "norm", + Usage: "Calc L1 norm instead of sum", + }, + Value: false, + }, + &option.StringFlag{ + BaseFlag: &option.BaseFlag{ + Name: "out", + Usage: "Output file path", + }, + }, + } + return option.RegisterFlags(cmd, flags) +} + +func init() { + cmdGenerators = append(cmdGenerators, newSumCmd) +} diff --git a/tests/go-cli-template-master/cmd/sum_test.go b/tests/go-cli-template-master/cmd/sum_test.go new file mode 100644 index 0000000..6f26634 --- /dev/null +++ b/tests/go-cli-template-master/cmd/sum_test.go @@ -0,0 +1,73 @@ +package cmd_test + +import ( + "bytes" + "fmt" + "github.com/spf13/afero" + "strings" + "testing" + + "github.com/mpppk/cli-template/cmd" +) + +func TestSum(t *testing.T) { + cases := []struct { + command string + want string + }{ + {command: "sum -- -1 2", want: "1\n"}, + {command: "sum --norm -- -1 2", want: "3\n"}, + } + + for _, c := range cases { + buf := new(bytes.Buffer) + rootCmd, err := cmd.NewRootCmd(afero.NewMemMapFs()) + if err != nil { + t.Errorf("failed to create rootCmd: %s", err) + } + rootCmd.SetOut(buf) + cmdArgs := strings.Split(c.command, " ") + rootCmd.SetArgs(cmdArgs) + if err := rootCmd.Execute(); err != nil { + t.Errorf("failed to execute rootCmd: %s", err) + } + + get := buf.String() + if c.want != get { + t.Errorf("unexpected response: want:%q, get:%q", c.want, get) + } + } +} + +func TestSumWithOutFile(t *testing.T) { + testFilePath := "test.txt" + cases := []struct { + command string + want string + }{ + {command: fmt.Sprintf("sum --out %s -- -1 2", testFilePath), want: "1"}, + } + + for _, c := range cases { + fs := afero.NewMemMapFs() + rootCmd, err := cmd.NewRootCmd(fs) + if err != nil { + t.Errorf("failed to create rootCmd: %s", err) + } + cmdArgs := strings.Split(c.command, " ") + rootCmd.SetArgs(cmdArgs) + if err := rootCmd.Execute(); err != nil { + t.Errorf("failed to execute rootCmd: %s", err) + } + byteContents, err := afero.ReadFile(fs, testFilePath) + if err != nil { + t.Fatal(err) + } + get := string(byteContents) + if c.want != get { + t.Errorf("unexpected response: want:%q, get:%q", c.want, get) + } + } +} + + diff --git a/tests/go-cli-template-master/cmd/version.go b/tests/go-cli-template-master/cmd/version.go new file mode 100644 index 0000000..6402093 --- /dev/null +++ b/tests/go-cli-template-master/cmd/version.go @@ -0,0 +1,23 @@ +package cmd + +import ( + "github.com/mpppk/cli-template/util" + "github.com/spf13/afero" + "github.com/spf13/cobra" +) + +func newVersionCmd(fs afero.Fs) (*cobra.Command, error) { + cmd := &cobra.Command{ + Use: "version", + Short: "Show version", + //Long: ``, + Run: func(cmd *cobra.Command, args []string) { + cmd.Println(util.Version) + }, + } + return cmd, nil +} + +func init() { + cmdGenerators = append(cmdGenerators, newVersionCmd) +} diff --git a/tests/go-cli-template-master/domain/model/numbers.go b/tests/go-cli-template-master/domain/model/numbers.go new file mode 100644 index 0000000..c000319 --- /dev/null +++ b/tests/go-cli-template-master/domain/model/numbers.go @@ -0,0 +1,50 @@ +package model + +import ( + "github.com/mpppk/cli-template/util" + "math" +) + +// Numbers represents numbers +type Numbers []int + +// NewNumbers is constructor for Numbers +func NewNumbers(nums []int) Numbers { + return nums +} + +// CalcSum calc sum of numbers +func (n Numbers) CalcSum() int { + return Sum(n) +} + +// CalcL1Norm calc L1 norm of numbers +func (n Numbers) CalcL1Norm() int { + return L1Norm(n) +} + +// Sum returns sum of numbers +func Sum(numbers []int) (sum int) { + for _, number := range numbers { + sum += number + } + return +} + +// L1Norm returns L1 norm of numbers +func L1Norm(numbers []int) (l1norm int) { + var absNumbers []int + for _, number := range numbers { + absNumbers = append(absNumbers, int(math.Abs(float64(number)))) + } + return Sum(absNumbers) +} + +// SumFromString returns sum numbers which be converted from strings +func SumFromString(stringNumbers []string) (sum int, err error) { + numbers, err := util.ConvertStringSliceToIntSlice(stringNumbers) + if err != nil { + return 0, err + } + return Sum(numbers), nil +} diff --git a/tests/go-cli-template-master/domain/model/sum_history.go b/tests/go-cli-template-master/domain/model/sum_history.go new file mode 100644 index 0000000..40b6c4b --- /dev/null +++ b/tests/go-cli-template-master/domain/model/sum_history.go @@ -0,0 +1,11 @@ +package model + +import "time" + +// SumHistory represents history of sum calculation +type SumHistory struct { + IsNorm bool + Date time.Time + Numbers Numbers + Result int +} diff --git a/tests/go-cli-template-master/domain/repository/sum_history.go b/tests/go-cli-template-master/domain/repository/sum_history.go new file mode 100644 index 0000000..0bc825d --- /dev/null +++ b/tests/go-cli-template-master/domain/repository/sum_history.go @@ -0,0 +1,9 @@ +package repository + +import "github.com/mpppk/cli-template/domain/model" + +// SumHistory represents repository to manage history of sum calculation +type SumHistory interface { + Add(sumHistory *model.SumHistory) + List(limit int) []*model.SumHistory +} diff --git a/tests/go-cli-template-master/domain/sum.go b/tests/go-cli-template-master/domain/sum.go new file mode 100644 index 0000000..cc10099 --- /dev/null +++ b/tests/go-cli-template-master/domain/sum.go @@ -0,0 +1,17 @@ +package domain + +import ( + "github.com/mpppk/cli-template/domain/model" + "github.com/mpppk/cli-template/util" +) + +// NewNumbersFromStringSlice create new Numbers with numbers from string slice +func NewNumbersFromStringSlice(strNumbers []string) (model.Numbers, error) { + rawNumbers, err := util.ConvertStringSliceToIntSlice(strNumbers) + if err != nil { + return nil, err + } + return model.NewNumbers(rawNumbers), nil +} + + diff --git a/tests/go-cli-template-master/domain/sum_test.go b/tests/go-cli-template-master/domain/sum_test.go new file mode 100644 index 0000000..160ed5f --- /dev/null +++ b/tests/go-cli-template-master/domain/sum_test.go @@ -0,0 +1,326 @@ +package domain_test + +import ( + "fmt" + "io/ioutil" + "os" + "reflect" + "strings" + "testing" + + "github.com/mpppk/cli-template/domain" + + "github.com/mpppk/cli-template/domain/model" + + "github.com/mpppk/cli-template/util" +) + +func TestNewNumbers(t *testing.T) { + type args struct { + nums []int + } + tests := []struct { + name string + args args + want model.Numbers + }{ + { + name: "", + args: args{ + nums: []int{}, + }, + want: []int{}, + }, + { + name: "", + args: args{ + nums: []int{1}, + }, + want: []int{1}, + }, + { + name: "", + args: args{ + nums: []int{1, 2}, + }, + want: []int{1, 2}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := model.NewNumbers(tt.args.nums); !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewNumbers() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestNewNumbersFromStringSlice(t *testing.T) { + type args struct { + strNumbers []string + } + tests := []struct { + name string + args args + want model.Numbers + wantErr bool + }{ + { + args: args{ + strNumbers: []string{"1"}, + }, + want: []int{1}, + wantErr: false, + }, + { + args: args{ + strNumbers: []string{"1", "2"}, + }, + want: []int{1, 2}, + wantErr: false, + }, + { + args: args{ + strNumbers: []string{"-1", "2"}, + }, + want: []int{-1, 2}, + wantErr: false, + }, + { + args: args{ + strNumbers: []string{"1", "a"}, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := domain.NewNumbersFromStringSlice(tt.args.strNumbers) + if (err != nil) != tt.wantErr { + t.Errorf("NewNumbersFromStringSlice() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewNumbersFromStringSlice() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestNumbers_CalcL1Norm(t *testing.T) { + tests := []struct { + name string + n model.Numbers + want int + }{ + { + n: []int{1, 2}, + want: 3, + }, + { + n: []int{-1, 2}, + want: 3, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.n.CalcL1Norm(); got != tt.want { + t.Errorf("CalcL1Norm() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestNumbers_CalcSum(t *testing.T) { + tests := []struct { + name string + n model.Numbers + want int + }{ + { + n: []int{1, 2}, + want: 3, + }, + { + n: []int{-1, 2}, + want: 1, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.n.CalcSum(); got != tt.want { + t.Errorf("CalcSum() = %v, want %v", got, tt.want) + } + }) + } +} + +func ExampleSum() { + numbers := []int{1, -2, 3} + fmt.Println(model.Sum(numbers)) + // Output: + // 2 +} + +func TestSum(t *testing.T) { + type args struct { + numbers []int + } + tests := []struct { + name string + args args + wantSum int + }{ + { + name: "return sum of numbers", + args: args{ + numbers: []int{1, 2, 3}, + }, + wantSum: 6, + }, + { + name: "return sum of numbers", + args: args{ + numbers: []int{1, -2, 3}, + }, + wantSum: 2, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if gotSum := model.Sum(tt.args.numbers); gotSum != tt.wantSum { + t.Errorf("Sum() = %v, want %v", gotSum, tt.wantSum) + } + }) + } +} + +func TestSumFromFile(t *testing.T) { + type args struct { + numbers []int + } + + type tCase struct { + name string + args args + wantSum int + } + + f, err := os.Open("../testdata/sum.txt") + if err != nil { + t.Fatal(err) + } + contentBytes, err := ioutil.ReadAll(f) + if err != nil { + t.Fatal(err) + } + contents := string(contentBytes) + lines := strings.Split(strings.Replace(contents, "\r\n", "\n", -1), "\n") + var tests []tCase + for i, line := range lines { + strRow := strings.Split(line, " ") + row, err := util.ConvertStringSliceToIntSlice(strRow) + if err != nil { + t.Fatal(err) + } + want := row[len(row)-1] + nums := row[:len(row)-1] + tc := tCase{ + name: fmt.Sprintf("case%d", i), + args: args{ + numbers: nums, + }, + wantSum: want, + } + tests = append(tests, tc) + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if gotSum := model.Sum(tt.args.numbers); gotSum != tt.wantSum { + t.Errorf("Sum() = %v, want %v", gotSum, tt.wantSum) + } + }) + } +} + +func TestSumFromString(t *testing.T) { + type args struct { + stringNumbers []string + } + tests := []struct { + name string + args args + wantSum int + wantErr bool + }{ + { + name: "return sum of numbers", + args: args{ + stringNumbers: []string{"1", "2", "3"}, + }, + wantSum: 6, + wantErr: false, + }, + { + name: "will be error if args includes not number string", + args: args{ + stringNumbers: []string{"1", "2", "a"}, + }, + wantSum: 0, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotSum, err := model.SumFromString(tt.args.stringNumbers) + if (err != nil) != tt.wantErr { + t.Errorf("SumFromString() error = %v, wantErr %v", err, tt.wantErr) + return + } + if gotSum != tt.wantSum { + t.Errorf("SumFromString() = %v, want %v", gotSum, tt.wantSum) + } + }) + } +} + +func ExampleL1Norm() { + numbers := []int{1, -2, 3} + fmt.Println(model.L1Norm(numbers)) + // Output: + // 6 +} + +func TestL1Norm(t *testing.T) { + type args struct { + numbers []int + } + tests := []struct { + name string + args args + wantSum int + }{ + { + name: "return sum of numbers", + args: args{ + numbers: []int{1, 2, 3}, + }, + wantSum: 6, + }, + { + name: "return sum of numbers", + args: args{ + numbers: []int{1, -2, 3}, + }, + wantSum: 6, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if gotSum := model.L1Norm(tt.args.numbers); gotSum != tt.wantSum { + t.Errorf("Sum() = %v, want %v", gotSum, tt.wantSum) + } + }) + } +} diff --git a/tests/go-cli-template-master/go.mod b/tests/go-cli-template-master/go.mod new file mode 100644 index 0000000..a27cc00 --- /dev/null +++ b/tests/go-cli-template-master/go.mod @@ -0,0 +1,24 @@ +module github.com/mpppk/cli-template + +go 1.14 + +require ( + github.com/blang/semver v3.5.1+incompatible + github.com/blang/semver/v4 v4.0.0 + github.com/comail/colog v0.0.0-20160416085026-fba8e7b1f46c + github.com/go-playground/validator/v10 v10.4.1 + github.com/google/wire v0.5.0 + github.com/labstack/echo v3.3.10+incompatible + github.com/labstack/gommon v0.3.0 // indirect + github.com/mattn/go-colorable v0.1.4 // indirect + github.com/mattn/go-isatty v0.0.12 // indirect + github.com/mitchellh/go-homedir v1.1.0 + github.com/rhysd/go-github-selfupdate v1.2.3 + github.com/spf13/afero v1.5.1 + github.com/spf13/cobra v1.1.3 + github.com/spf13/pflag v1.0.5 + github.com/spf13/viper v1.7.1 + github.com/valyala/fasttemplate v1.1.0 // indirect + golang.org/x/net v0.0.0-20200202094626-16171245cfb2 // indirect + golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 // indirect +) diff --git a/tests/go-cli-template-master/go.sum b/tests/go-cli-template-master/go.sum new file mode 100644 index 0000000..df3d107 --- /dev/null +++ b/tests/go-cli-template-master/go.sum @@ -0,0 +1,450 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= +github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/comail/colog v0.0.0-20160416085026-fba8e7b1f46c h1:bzYQ6WpR+t35/y19HUkolcg7SYeWZ15IclC9Z4naGHI= +github.com/comail/colog v0.0.0-20160416085026-fba8e7b1f46c/go.mod h1:1WwgAwMKQLYG5I2FBhpVx94YTOAuB2W59IZ7REjSE6Y= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.3.0 h1:nZU+7q+yJoFmwvNgv/LnPUkwPal62+b2xXj0AU1Es7o= +github.com/go-playground/validator/v10 v10.3.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= +github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-github/v30 v30.1.0 h1:VLDx+UolQICEOKu2m4uAoMti1SxuEBAl7RSEG16L+Oo= +github.com/google/go-github/v30 v30.1.0/go.mod h1:n8jBpHl45a/rlBUtRJMOG4GhNADUQFEufcolZ95JfU8= +github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= +github.com/google/wire v0.4.0 h1:kXcsA/rIGzJImVqPdhfnr6q0xsS9gU0515q1EPpJ9fE= +github.com/google/wire v0.4.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU= +github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8= +github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf h1:WfD7VjIE6z8dIvMsI4/s+1qr5EL+zoIGev1BQj1eoJ8= +github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf/go.mod h1:hyb9oH7vZsitZCiBt0ZvifOrB+qc8PS5IiilCIb87rg= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg= +github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s= +github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0= +github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.2 h1:3mYCb7aPxS/RU7TI1y4rkEn1oKmPRjNJLNEXgw7MH2I= +github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rhysd/go-github-selfupdate v1.2.2 h1:G+mNzkc1wEtpmM6sFS/Ghkeq+ad4Yp6EZEHyp//wGEo= +github.com/rhysd/go-github-selfupdate v1.2.2/go.mod h1:khesvSyKcXDUxeySCedFh621iawCks0dS/QnHPcpCws= +github.com/rhysd/go-github-selfupdate v1.2.3 h1:iaa+J202f+Nc+A8zi75uccC8Wg3omaM7HDeimXA22Ag= +github.com/rhysd/go-github-selfupdate v1.2.3/go.mod h1:mp/N8zj6jFfBQy/XMYoWsmfzxazpPAODuqarmPDe2Rg= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.3.0 h1:Ysnmjh1Di8EaWaBv40CYR4IdaIsBc5996Gh1oZzCBKk= +github.com/spf13/afero v1.3.0/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= +github.com/spf13/afero v1.3.1 h1:GPTpEAuNr98px18yNQ66JllNil98wfRZ/5Ukny8FeQA= +github.com/spf13/afero v1.3.1/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= +github.com/spf13/afero v1.3.2 h1:GDarE4TJQI52kYSbSAmLiId1Elfj+xgSDqrUZxFhxlU= +github.com/spf13/afero v1.3.2/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= +github.com/spf13/afero v1.3.3 h1:p5gZEKLYoL7wh8VrJesMaYeNxdEd1v3cb4irOk9zB54= +github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= +github.com/spf13/afero v1.3.4 h1:8q6vk3hthlpb2SouZcnBVKboxWQWMDNF38bwholZrJc= +github.com/spf13/afero v1.3.4/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/afero v1.3.5 h1:AWZ/w4lcfxuh52NVL78p9Eh8j6r1mCTEGSRFBJyIHAE= +github.com/spf13/afero v1.3.5/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/afero v1.4.0 h1:jsLTaI1zwYO3vjrzHalkVcIHXTNmdQFepW4OI8H3+x8= +github.com/spf13/afero v1.4.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/afero v1.4.1 h1:asw9sl74539yqavKaglDM5hFpdJVK0Y5Dr/JOgQ89nQ= +github.com/spf13/afero v1.4.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/afero v1.5.1 h1:VHu76Lk0LSP1x254maIu2bplkWpfBWI+B+6fdoZprcg= +github.com/spf13/afero v1.5.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4= +github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= +github.com/spf13/cobra v1.1.2 h1:frHO75w/dH7kEc+e2KYZZKY4+PLrp39OqI77oB8m0KQ= +github.com/spf13/cobra v1.1.2/go.mod h1:ZjwqWkCg0LnXvLRIfTLdB4Y/MCO3gMHHJ2KFxQZy4xE= +github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M= +github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= +github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +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= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= +github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tcnksm/go-gitconfig v0.1.2 h1:iiDhRitByXAEyjgBqsKi9QU4o2TNtv9kPP3RgPgXBPw= +github.com/tcnksm/go-gitconfig v0.1.2/go.mod h1:/8EhP4H7oJZdIPyT+/UIsG87kTzrzM4UsLGSItWYCpE= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok= +github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= +github.com/ulikunitz/xz v0.5.9 h1:RsKRIA2MO8x56wkkcd3LbtcE/uMszhb6DpRf+3uwa3I= +github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +github.com/valyala/fasttemplate v1.1.0 h1:RZqt0yGBsps8NGvLSGW804QQqCUYYLsaOjTVHy1Ocw4= +github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d h1:9FCpayM9Egr1baVnV1SX0H87m+XB0B8S0hAMi99X/3U= +golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288 h1:JIqe8uIcRBHXDQVvZtHwp80ai3Lw3IJAeJEs55Dc1W0= +golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 h1:1/DFK4b7JH8DmkqhUk48onnSfrPzImPoVxuomtbT2nk= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.3.0 h1:FBSsiFRMz3LBeXIomRnVzrQwSDj4ibvcRexLG0LZGQk= +google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/tests/go-cli-template-master/handler/export_test.go b/tests/go-cli-template-master/handler/export_test.go new file mode 100644 index 0000000..f5d3547 --- /dev/null +++ b/tests/go-cli-template-master/handler/export_test.go @@ -0,0 +1,4 @@ +package handler + +type SumResponse = sumResponse +type SumHistoryResponse = sumHistoryResponse diff --git a/tests/go-cli-template-master/handler/sum-history.go b/tests/go-cli-template-master/handler/sum-history.go new file mode 100644 index 0000000..e16f78a --- /dev/null +++ b/tests/go-cli-template-master/handler/sum-history.go @@ -0,0 +1,38 @@ +package handler + +import ( + "net/http" + + "github.com/mpppk/cli-template/domain/model" + + "github.com/labstack/echo" +) + +type sumHistoryRequest struct { + Limit int `query:"limit" Validate:"required"` +} + +type sumHistoryResponse struct { + Result []*model.SumHistory `json:"result"` +} + +// SumHistory handle http request to list sum history +func (h *Handlers) SumHistory(c echo.Context) error { + req := new(sumHistoryRequest) + if err := c.Bind(req); err != nil { + return err + } + + if err := c.Validate(req); err != nil { + logWithJSON("invalid request", req) + return echo.NewHTTPError(http.StatusBadRequest, err.Error()) + } + + if req.Limit == 0 { + req.Limit = 10 + } + + history := h.sumUseCase.ListSumHistory(req.Limit) + + return c.JSON(http.StatusOK, sumHistoryResponse{Result: history}) +} diff --git a/tests/go-cli-template-master/handler/sum-history_test.go b/tests/go-cli-template-master/handler/sum-history_test.go new file mode 100644 index 0000000..2f41143 --- /dev/null +++ b/tests/go-cli-template-master/handler/sum-history_test.go @@ -0,0 +1,120 @@ +package handler_test + +import ( + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/mpppk/cli-template/registry" + + "github.com/mpppk/cli-template/domain/model" + + "github.com/labstack/echo" + + "github.com/mpppk/cli-template/handler" +) + +func TestSumHistory(t *testing.T) { + + history1 := &model.SumHistory{ + IsNorm: false, + Date: time.Time{}, + Numbers: model.Numbers{1, 2}, + Result: 3, + } + history2 := &model.SumHistory{ + IsNorm: true, + Date: time.Time{}, + Numbers: model.Numbers{-1, 2}, + Result: 3, + } + history := []*model.SumHistory{history1, history2} + h := registry.InitializeHandler(history) + e := registry.InitializeServer(nil) + + type params struct { + path string + } + type want struct { + res handler.SumHistoryResponse + code int + } + tests := []struct { + name string + params params + want want + wantErr bool + }{ + { + params: params{ + path: "/api/sum-history?limit=1", + }, + want: want{ + res: handler.SumHistoryResponse{Result: []*model.SumHistory{ + history2, + }}, + code: http.StatusOK, + }, + }, + { + params: params{ + path: "/api/sum-history?limit=2", + }, + want: want{ + res: handler.SumHistoryResponse{Result: []*model.SumHistory{ + history1, + history2, + }}, + code: http.StatusOK, + }, + }, + { + params: params{ + path: "/api/sum-history?limit=xxx", + }, + want: want{ + code: http.StatusBadRequest, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, tt.params.path, nil) + rec := httptest.NewRecorder() + + err := h.SumHistory(e.NewContext(req, rec)) + + if (err != nil) != tt.wantErr { + t.Errorf("Handlers.Sum() error = %v, wantErr %v", err, tt.wantErr) + return + } + + var code int + if tt.wantErr { + httpError, ok := err.(*echo.HTTPError) + if !ok { + t.Fatalf("invalid err: %#v", err) + } + code = httpError.Code + } else { + code = rec.Code + } + + if tt.want.code != code { + t.Errorf("HTTP Status Code got = %d, want %d, body = %v", rec.Code, tt.want.code, rec.Body.String()) + } + + if tt.wantErr { + return + } + + gotRes := rec.Body.String() + resJSON := toResponseJSON(t, tt.want.res) + if resJSON != gotRes { + t.Errorf("HTTP Response: got = %s, want %s", gotRes, resJSON) + } + }) + } +} diff --git a/tests/go-cli-template-master/handler/sum.go b/tests/go-cli-template-master/handler/sum.go new file mode 100644 index 0000000..4872640 --- /dev/null +++ b/tests/go-cli-template-master/handler/sum.go @@ -0,0 +1,49 @@ +package handler + +import ( + "net/http" + + "github.com/labstack/echo" + "github.com/mpppk/cli-template/usecase" +) + +// Handlers represent handlers of echo server +type Handlers struct { + sumUseCase *usecase.Sum +} + +type sumRequest struct { + A int `query:"a" Validate:"required"` + B int `query:"b" Validate:"required"` + Norm bool `query:"norm"` +} + +type sumResponse struct { + Result int `json:"result"` +} + +// New create new handlers +func New(sumUseCase *usecase.Sum) *Handlers { + return &Handlers{sumUseCase: sumUseCase} +} + +// Sum handle http request to calculate sum +func (h *Handlers) Sum(c echo.Context) error { + req := new(sumRequest) + if err := c.Bind(req); err != nil { + return err + } + + if err := c.Validate(req); err != nil { + logWithJSON("invalid request", req) + return echo.NewHTTPError(http.StatusBadRequest, err.Error()) + } + + var result int + if req.Norm { + result = h.sumUseCase.CalcL1Norm([]int{req.A, req.B}) + } else { + result = h.sumUseCase.CalcSum([]int{req.A, req.B}) + } + return c.JSON(http.StatusOK, sumResponse{Result: result}) +} diff --git a/tests/go-cli-template-master/handler/sum_test.go b/tests/go-cli-template-master/handler/sum_test.go new file mode 100644 index 0000000..194e3fc --- /dev/null +++ b/tests/go-cli-template-master/handler/sum_test.go @@ -0,0 +1,112 @@ +package handler_test + +import ( + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "testing" + + "github.com/mpppk/cli-template/infra" + "github.com/mpppk/cli-template/registry" + + "github.com/labstack/echo" + + "github.com/mpppk/cli-template/handler" +) + +func TestSum(t *testing.T) { + h := registry.InitializeHandler(nil) + e := infra.NewServer(h) + + type params struct { + path string + } + type want struct { + res handler.SumResponse + code int + } + tests := []struct { + name string + params params + want want + wantErr bool + }{ + { + params: params{ + path: "/api/sum?a=-1&b=2", + }, + want: want{ + res: handler.SumResponse{Result: 1}, + code: http.StatusOK, + }, + }, + { + params: params{ + path: "/api/sum?a=-1&b=2&norm=true", + }, + want: want{ + res: handler.SumResponse{Result: 3}, + code: http.StatusOK, + }, + }, + { + params: params{ + path: "/api/sum?a=1&b=str", + }, + want: want{ + code: http.StatusBadRequest, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, tt.params.path, nil) + rec := httptest.NewRecorder() + + err := h.Sum(e.NewContext(req, rec)) + + if (err != nil) != tt.wantErr { + t.Errorf("Handlers.Sum() error = %v, wantErr %v", err, tt.wantErr) + return + } + + var code int + if tt.wantErr { + httpError, ok := err.(*echo.HTTPError) + if !ok { + t.Fatalf("invalid err: %#v", err) + } + code = httpError.Code + } else { + code = rec.Code + } + + if tt.want.code != code { + t.Errorf("HTTP Status Code got = %d, want %d, body = %v", rec.Code, tt.want.code, rec.Body.String()) + } + + if tt.wantErr { + return + } + + gotRes := rec.Body.String() + resJSON := toResponseJSON(t, tt.want.res) + if resJSON != gotRes { + t.Errorf("HTTP Response: got = %s, want %s", gotRes, resJSON) + } + }) + } +} + +func toResponseJSON(t *testing.T, res interface{}) string { + t.Helper() + + resContents, err := json.Marshal(res) + if err != nil { + t.Fatalf("invalid test arg. res: %#v", res) + } + + return fmt.Sprintln(string(resContents)) +} diff --git a/tests/go-cli-template-master/handler/util.go b/tests/go-cli-template-master/handler/util.go new file mode 100644 index 0000000..6303a23 --- /dev/null +++ b/tests/go-cli-template-master/handler/util.go @@ -0,0 +1,20 @@ +package handler + +import ( + "encoding/json" + "log" +) + +// errorResponse represents http response when error occurred +type errorResponse struct { + Message string `json:"message"` +} + +func logWithJSON(prefix string, data interface{}) { + contents, err := json.Marshal(data) + if err != nil { + log.Printf("failed to marshal data for log: %v", err) + return + } + log.Println(prefix + ": " + string(contents)) +} diff --git a/tests/go-cli-template-master/infra/repoimpl/memory_sum_history.go b/tests/go-cli-template-master/infra/repoimpl/memory_sum_history.go new file mode 100644 index 0000000..033b227 --- /dev/null +++ b/tests/go-cli-template-master/infra/repoimpl/memory_sum_history.go @@ -0,0 +1,36 @@ +package repoimpl + +import ( + "log" + + "github.com/mpppk/cli-template/domain/model" + "github.com/mpppk/cli-template/domain/repository" +) + +// MemorySumHistory represents on memory repository of sum history +type MemorySumHistory struct { + history []*model.SumHistory +} + +// NewMemorySumHistory create new MemorySumHistory repository +func NewMemorySumHistory(v []*model.SumHistory) repository.SumHistory { + if v == nil { + v = []*model.SumHistory{} + } + return &MemorySumHistory{history: v} +} + +// Add add new sum history to repository +func (m *MemorySumHistory) Add(sumHistory *model.SumHistory) { + m.history = append(m.history, sumHistory) + log.Printf("current history: %v", m.history) +} + +// List return list of sum history +func (m *MemorySumHistory) List(limit int) []*model.SumHistory { + l := len(m.history) + if l <= limit { + return m.history + } + return m.history[l-limit : l] +} diff --git a/tests/go-cli-template-master/infra/server.go b/tests/go-cli-template-master/infra/server.go new file mode 100644 index 0000000..695d557 --- /dev/null +++ b/tests/go-cli-template-master/infra/server.go @@ -0,0 +1,41 @@ +package infra + +import ( + "fmt" + + "github.com/mpppk/cli-template/handler" + + "github.com/go-playground/validator/v10" + "github.com/labstack/echo" + "github.com/labstack/echo/middleware" +) + +type customValidator struct { + validator *validator.Validate +} + +func (cv *customValidator) Validate(i interface{}) error { + return cv.validator.Struct(i) +} + +func registerHandlers(e *echo.Echo, handlers *handler.Handlers) { + e.GET("/api/sum", handlers.Sum) + e.GET("/api/sum-history", handlers.SumHistory) +} + +func bodyDumpHandler(c echo.Context, reqBody, resBody []byte) { + fmt.Printf("Request Body: %v\n", string(reqBody)) + fmt.Printf("Response Body: %v\n", string(resBody)) +} + +// NewServer create new echo server with handlers +func NewServer(handlers *handler.Handlers) *echo.Echo { + e := echo.New() + e.Validator = &customValidator{validator: validator.New()} + e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{ + Format: fmt.Sprintln("method=${method}, uri=${uri}, status=${status}"), + })) + e.Use(middleware.BodyDump(bodyDumpHandler)) + registerHandlers(e, handlers) + return e +} diff --git a/tests/go-cli-template-master/main.go b/tests/go-cli-template-master/main.go new file mode 100644 index 0000000..21e3514 --- /dev/null +++ b/tests/go-cli-template-master/main.go @@ -0,0 +1,7 @@ +package main + +import "github.com/mpppk/cli-template/cmd" + +func main() { + cmd.Execute() +} diff --git a/tests/go-cli-template-master/pack.yaml b/tests/go-cli-template-master/pack.yaml new file mode 100644 index 0000000..73c5b02 --- /dev/null +++ b/tests/go-cli-template-master/pack.yaml @@ -0,0 +1,3 @@ +# syntax = erichripko/pack.yaml +go: + tags: ["wireinject"] diff --git a/tests/go-cli-template-master/readme.md b/tests/go-cli-template-master/readme.md new file mode 100644 index 0000000..da390c5 --- /dev/null +++ b/tests/go-cli-template-master/readme.md @@ -0,0 +1,115 @@ +# golang CLI Template +[![CircleCI](https://circleci.com/gh/mpppk/cli-template.svg?style=svg)](https://circleci.com/gh/mpppk/cli-template) +[![Build status](https://ci.appveyor.com/api/projects/status/qv1fyq6fm8ni4cne?svg=true)](https://ci.appveyor.com/project/mpppk/cli-template) +![GitHub Actions](https://github.com/mpppk/cli-template/workflows/Go/badge.svg) +[![codecov](https://codecov.io/gh/mpppk/cli-template/branch/master/graph/badge.svg)](https://codecov.io/gh/mpppk/cli-template) +[![GoDoc](https://godoc.org/github.com/mpppk/cli-template?status.svg)](https://godoc.org/github.com/mpppk/cli-template) + +*golang project template for building CLI* + +## Setup +### Setup by Command +1. `git clone https://github.com/mpppk/cli-template your_awesome_tool` +1. Replace all strings `cli-template` in this repository to `your_awesome_tool` + +### Setup on GitHub +Click "Use this template" button on GitHub project page. + +![](https://github.com/mpppk/cli-template/wiki/images/template-button.png) + +## Project structure +### Application layers +* `/cmd` includes golang files which implements command and sub commands. +* `/domain` includes golang files which implements domain model. This package should not depend on other layers. +* `/handler` includes golang files which implements handlers of http request. +* `/usecase` includes golang files which implements application use cases. This package is only allowed to depend on *domain* layer. + +### Others +* `/util` includes golang files which implements utilities. All layers can depend on this package. This package should not depend on other packages. +* `/scripts` includes scripts +* `/testdata` includes data files for tests. (see https://golang.org/cmd/go/#hdr-Test_packages) + +For more detail, see [golang-standards/project-layout](https://github.com/golang-standards/project-layout). + +## Add new sub command +If you want to create new sub command, Add new go file to `/cmd`. + +For more details, see [spf13/cobra](https://github.com/spf13/cobra). + +## Task runner +Use [Makefile](https://github.com/mpppk/cli-template/blob/master/Makefile). + +## Error handling +Use [golang.org/x/xerrors](https://godoc.org/golang.org/x/xerrors). + +## Documentation +Write [godoc](https://blog.golang.org/godoc-documenting-go-code)([example code](https://github.com/mpppk/cli-template/blob/master/pkg/sum/sum.go#L9)) + or *Example test*([example code](https://github.com/mpppk/cli-template/blob/master/pkg/sum/sum_test.go#L13-L18https://github.com/mpppk/cli-template/blob/master/pkg/sum/sum_test.go#L13-L18)). + +## Testing +Don't write test in same package, instead put to `package-name_test` package. +For example, test of [domain/sum.go](https://github.com/mpppk/cli-template/blob/master/domain/sum_test.go) is in `domain_test` package, not `domain` package. +To use unexported variables or functions in test, expose these by `export_test.go` file. +(ex. [/internal/option/root_export_test.go](https://github.com/mpppk/cli-template/blob/master/internal/option/root_export_test.go)) + +For more details, see [this article(Japanese)](https://tech.mercari.com/entry/2018/08/08/080000). + +### cmd test +Recommended way is to wrap cobra.Command instance by func (unlike the code generated by cobra add). +For example, see [cmd/sum_test.go](https://github.com/mpppk/cli-template/blob/master/cmd/sum_test.go). + +### with file system +This template depends [spf13/afero](https://github.com/spf13/afero). +`afero.OsFs` is used in packages and `afero.MemMapFs` is used in tests. +For example, see [cmd/sum_test.go#TestSumWithOutFile](https://github.com/mpppk/cli-template/blob/master/cmd/sum_test.go) + +## Auto release via Circle CI powered by [goreleaser](https://github.com/goreleaser/goreleaser) +Create version tag (e.g. v0.0.1) and push to GitHub. +Then goreleaser will release the new version on Circle CI. +(Before push tag, you must provide GitHub token to Circle CI as environment variable) + +For more details, see [my article (Japanese)](https://qiita.com/mpppk/items/ab328356ca14938a1208). + +## Build & Run Docker image + +```bash +$ docker build -t cli-template . +... +$ docker run cli-template sum 1 2 +3 +``` + +## SaaS integration +### Circle CI +This template includes [.circleci/config.yml](https://github.com/mpppk/cli-template/blob/master/.circleci/config.yml). + +### AppVeyor +This template includes [appveyor.yml](https://github.com/mpppk/cli-template/blob/master/appveyor.yml). + +### CodeCov +Makefile includes [codecov task](https://github.com/mpppk/cli-template/blob/master/Makefile) which send coverage to CodeCov. +Circle CI also send coverage to CodeCov by its job. + +### Renovate +This template includes [renovate.json](https://github.com/mpppk/cli-template/blob/master/renovate.json). + +## README template + +-------- + +# cli-template + +## Installation + +### MacOS + +### Linux +Download from [GitHub Releases](https://github.com/mpppk/cli-template/releases) + +### Windows +Download from [GitHub Releases](https://github.com/mpppk/cli-template/releases) + +## Usage + +*Write usage of your awesome tool here* + diff --git a/tests/go-cli-template-master/registry/wire.go b/tests/go-cli-template-master/registry/wire.go new file mode 100644 index 0000000..9d3da66 --- /dev/null +++ b/tests/go-cli-template-master/registry/wire.go @@ -0,0 +1,38 @@ +//+build wireinject + +package registry + +//go:generate wire + +import ( + "github.com/labstack/echo" + "github.com/mpppk/cli-template/domain/model" + "github.com/mpppk/cli-template/handler" + "github.com/mpppk/cli-template/infra" + "github.com/mpppk/cli-template/infra/repoimpl" + "github.com/mpppk/cli-template/usecase" +) +import "github.com/google/wire" + +// InitializeHandler initialize handlers with memorySumHistoryRepository +func InitializeHandler(v []*model.SumHistory) *handler.Handlers { + wire.Build(handler.New, usecase.NewSum, repoimpl.NewMemorySumHistory) + return &handler.Handlers{} +} + +// InitializeSumUseCase initialize sum use case with memorySumHistoryRepository +func InitializeSumUseCase(v []*model.SumHistory) *usecase.Sum { + wire.Build(repoimpl.NewMemorySumHistory, usecase.NewSum) + return &usecase.Sum{} +} + +// InitializeServer initialize echo server with memorySumHistoryRepository +func InitializeServer(v []*model.SumHistory) *echo.Echo { + wire.Build( + handler.New, + repoimpl.NewMemorySumHistory, + usecase.NewSum, + infra.NewServer, + ) + return &echo.Echo{} +} diff --git a/tests/go-cli-template-master/renovate.json b/tests/go-cli-template-master/renovate.json new file mode 100644 index 0000000..b44d921 --- /dev/null +++ b/tests/go-cli-template-master/renovate.json @@ -0,0 +1,10 @@ +{ + "extends": ["config:base"], + "automerge": true, + "major": { + "automerge": false + }, + "ignoreDeps": [ + "semver" + ] +} diff --git a/tests/go-cli-template-master/scripts/request-to-sum-api.sh b/tests/go-cli-template-master/scripts/request-to-sum-api.sh new file mode 100644 index 0000000..2bf0567 --- /dev/null +++ b/tests/go-cli-template-master/scripts/request-to-sum-api.sh @@ -0,0 +1 @@ +curl "http://localhost:1323/api/sum?a=-1&b=2&norm=true" \ No newline at end of file diff --git a/tests/go-cli-template-master/scripts/serve.http b/tests/go-cli-template-master/scripts/serve.http new file mode 100644 index 0000000..82e5f9a --- /dev/null +++ b/tests/go-cli-template-master/scripts/serve.http @@ -0,0 +1,9 @@ +# curl "http://localhost:1323/api/sum?a=-1&b=a2&norm=true" +GET http://localhost:1323/api/sum?a=-1&b=2&norm=true + +### + +GET http://localhost:1323/api/sum-history + +### + diff --git a/tests/go-cli-template-master/test.txt b/tests/go-cli-template-master/test.txt new file mode 100644 index 0000000..56a6051 --- /dev/null +++ b/tests/go-cli-template-master/test.txt @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/tests/go-cli-template-master/testdata/sum.txt b/tests/go-cli-template-master/testdata/sum.txt new file mode 100644 index 0000000..3812812 --- /dev/null +++ b/tests/go-cli-template-master/testdata/sum.txt @@ -0,0 +1,2 @@ +1 -2 3 2 +4 5 -6 3 \ No newline at end of file diff --git a/tests/go-cli-template-master/usecase/sum.go b/tests/go-cli-template-master/usecase/sum.go new file mode 100644 index 0000000..c72bedb --- /dev/null +++ b/tests/go-cli-template-master/usecase/sum.go @@ -0,0 +1,55 @@ +package usecase + +import ( + "log" + "time" + + "github.com/mpppk/cli-template/domain/model" + "github.com/mpppk/cli-template/domain/repository" +) + +// Sum represents usecases related sum calculation +type Sum struct { + sumHistoryRepository repository.SumHistory +} + +// NewSum create use case related sum +func NewSum(sumHistoryRepository repository.SumHistory) *Sum { + return &Sum{ + sumHistoryRepository: sumHistoryRepository, + } +} + +// CalcSum is use case to calculate sum +func (s *Sum) CalcSum(numbers []int) int { + result := model.NewNumbers(numbers).CalcSum() + now := time.Now() + log.Printf("start saving history of sum result. date=%v, numbers=%d, result=%v\n", now, numbers, result) + s.sumHistoryRepository.Add(&model.SumHistory{ + Date: time.Now(), // FIXME + Numbers: numbers, + Result: result, + }) + log.Printf("finish saving history of sum result. date=%v, numbers=%d, result=%v\n", now, numbers, result) + return result +} + +// CalcL1Norm is use case to calculate L1 norm +func (s *Sum) CalcL1Norm(numbers []int) int { + result := model.NewNumbers(numbers).CalcL1Norm() + now := time.Now() + log.Printf("start saving history of norm result. date=%v, numbers=%d, result=%v\n", now, numbers, result) + s.sumHistoryRepository.Add(&model.SumHistory{ + IsNorm: true, + Date: now, // FIXME + Numbers: numbers, + Result: result, + }) + log.Printf("finish saving history of norm result. date=%v, numbers=%d, result=%v\n", now, numbers, result) + return result +} + +// ListSumHistory lists history of sum +func (s *Sum) ListSumHistory(limit int) []*model.SumHistory { + return s.sumHistoryRepository.List(limit) +} diff --git a/tests/go-cli-template-master/usecase/sum_test.go b/tests/go-cli-template-master/usecase/sum_test.go new file mode 100644 index 0000000..3e1f266 --- /dev/null +++ b/tests/go-cli-template-master/usecase/sum_test.go @@ -0,0 +1,83 @@ +package usecase_test + +import ( + "testing" + + "github.com/mpppk/cli-template/registry" +) + +func TestCalcSum(t *testing.T) { + type args struct { + strNumbers []int + } + tests := []struct { + name string + args args + want int + wantErr bool + }{ + { + name: "", + args: args{ + strNumbers: []int{1, 2}, + }, + want: 3, + wantErr: false, + }, + { + name: "", + args: args{ + strNumbers: []int{-1, 2}, + }, + want: 1, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + sumUseCase := registry.InitializeSumUseCase(nil) + got := sumUseCase.CalcSum(tt.args.strNumbers) + if got != tt.want { + t.Errorf("CalcSumFromStringSlice() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestCalcL1Norm(t *testing.T) { + type args struct { + strNumbers []int + } + tests := []struct { + name string + args args + want int + wantErr bool + }{ + { + name: "", + args: args{ + strNumbers: []int{1, 2}, + }, + want: 3, + wantErr: false, + }, + { + name: "", + args: args{ + strNumbers: []int{-1, 2}, + }, + want: 3, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + sumUseCase := registry.InitializeSumUseCase(nil) + got := sumUseCase.CalcL1Norm(tt.args.strNumbers) + if got != tt.want { + t.Errorf("CalcSumFromStringSlice() got = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/tests/go-cli-template-master/util/error.go b/tests/go-cli-template-master/util/error.go new file mode 100644 index 0000000..de162b4 --- /dev/null +++ b/tests/go-cli-template-master/util/error.go @@ -0,0 +1,54 @@ +package util + +import ( + "errors" + "fmt" + "strings" +) + +// PrettyPrintError print error as pretty +func PrettyPrintError(err error) string { + messages := extractMessagesFromError(err) + return joinErrorMessages(messages) +} + +func joinErrorMessages(messages []string) (message string) { + for i := len(messages) - 1; i >= 0; i-- { + prefix := " " + if i == len(messages)-1 { + prefix = "Error: " + } + message = message + fmt.Sprintln(prefix+strings.TrimSuffix(messages[i], ": ")) + } + return +} + +func extractMessagesFromError(err error) (messages []string) { + errs := unwrapErrors(err) + beforeErrMsg := "" + for i := len(errs) - 1; i >= 0; i-- { + e := errs[i] + eMsg := "" + if beforeErrMsg == "" { + eMsg = e.Error() + } else { + eMsgs := strings.Split(e.Error(), beforeErrMsg) + eMsg = eMsgs[0] + } + messages = append(messages, eMsg) + beforeErrMsg = e.Error() + } + return +} + +func unwrapErrors(err error) (errs []error) { + for { + errs = append(errs, err) + if e := errors.Unwrap(err); e == nil { + break + } else { + err = e + } + } + return +} diff --git a/tests/go-cli-template-master/util/error_test.go b/tests/go-cli-template-master/util/error_test.go new file mode 100644 index 0000000..7dce0f3 --- /dev/null +++ b/tests/go-cli-template-master/util/error_test.go @@ -0,0 +1,42 @@ +package util_test + +import ( + "errors" + "fmt" + "testing" + + "github.com/mpppk/cli-template/util" +) + +func TestPrettyPrintError(t *testing.T) { + type args struct { + err error + } + tests := []struct { + name string + args args + want string + }{ + { + name: "", + args: args{ + err: errors.New("sample error"), + }, + want: fmt.Sprintln("Error: sample error"), + }, + { + name: "", + args: args{ + err: fmt.Errorf("a: %w", errors.New("b")), + }, + want: fmt.Sprintln("Error: a") + fmt.Sprintln(" b"), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := util.PrettyPrintError(tt.args.err); got != tt.want { + t.Errorf("PrettyPrintError() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/tests/go-cli-template-master/util/selfupdate.go b/tests/go-cli-template-master/util/selfupdate.go new file mode 100644 index 0000000..3ca1bb4 --- /dev/null +++ b/tests/go-cli-template-master/util/selfupdate.go @@ -0,0 +1,40 @@ +// Package selfupdate provides function to update binary +package util + +import ( + "fmt" + + semverv3 "github.com/blang/semver" + "github.com/blang/semver/v4" + "github.com/rhysd/go-github-selfupdate/selfupdate" +) + +// Version represents version of this tool +const Version = "0.2.5" +const slug = "mpppk/cli-template" + +// Do execute updating binary +func Do() (bool, error) { + v := toV3PRVersion(semver.MustParse(Version)) + latest, err := selfupdate.UpdateSelf(v, slug) + if err != nil { + return false, fmt.Errorf("Binary update failed: %w", err) + } + return !latest.Version.Equals(v), nil +} + +func toV3PRVersion(v semver.Version) semverv3.Version { + var v3PRVersions []semverv3.PRVersion + for _, version := range v.Pre { + v3PRVersions = append(v3PRVersions, semverv3.PRVersion(version)) + } + + v3v := semverv3.Version{ + Major: v.Major, + Minor: v.Minor, + Patch: v.Patch, + Pre: v3PRVersions, + Build: v.Build, + } + return v3v +} diff --git a/tests/go-cli-template-master/util/selfupdate_test.go b/tests/go-cli-template-master/util/selfupdate_test.go new file mode 100644 index 0000000..8a5b068 --- /dev/null +++ b/tests/go-cli-template-master/util/selfupdate_test.go @@ -0,0 +1,32 @@ +package util + +import ( + "reflect" + "testing" + + semverv3 "github.com/blang/semver" + "github.com/blang/semver/v4" +) + +func Test_toV3PRVersion(t *testing.T) { + type args struct { + v semver.Version + } + tests := []struct { + name string + args args + want semverv3.Version + }{ + { + args: args{semver.MustParse("1.2.3")}, + want: semverv3.MustParse("1.2.3"), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := toV3PRVersion(tt.args.v); !reflect.DeepEqual(got, tt.want) { + t.Errorf("toV3PRVersion() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/tests/go-cli-template-master/util/util.go b/tests/go-cli-template-master/util/util.go new file mode 100644 index 0000000..132a863 --- /dev/null +++ b/tests/go-cli-template-master/util/util.go @@ -0,0 +1,32 @@ +// Package util provides some utilities +package util + +import ( + "fmt" + "strconv" + + "github.com/comail/colog" +) + +// ConvertStringSliceToIntSlice converts string slices to int slices. +func ConvertStringSliceToIntSlice(stringSlice []string) (intSlice []int, err error) { + for _, s := range stringSlice { + num, err := strconv.Atoi(s) + if err != nil { + return nil, fmt.Errorf("failed to convert string slice to int slice: %w", err) + } + intSlice = append(intSlice, num) + } + return +} + +// InitializeLog initialize log settings +func InitializeLog(verbose bool) { + colog.Register() + colog.SetDefaultLevel(colog.LDebug) + colog.SetMinLevel(colog.LInfo) + + if verbose { + colog.SetMinLevel(colog.LDebug) + } +} diff --git a/tests/go-cli-template-master/util/util_test.go b/tests/go-cli-template-master/util/util_test.go new file mode 100644 index 0000000..916a4f1 --- /dev/null +++ b/tests/go-cli-template-master/util/util_test.go @@ -0,0 +1,47 @@ +package util + +import ( + "reflect" + "testing" +) + +func TestConvertStringSliceToIntSlice(t *testing.T) { + type args struct { + stringSlice []string + } + tests := []struct { + name string + args args + wantIntSlice []int + wantErr bool + }{ + { + name: "can convert string slice to int slice", + args: args{ + stringSlice: []string{"1", "2", "3"}, + }, + wantIntSlice: []int{1, 2, 3}, + wantErr: false, + }, + { + name: "will be error if string can not be convert to number", + args: args{ + stringSlice: []string{"1", "2", "a"}, + }, + wantIntSlice: nil, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotIntSlice, err := ConvertStringSliceToIntSlice(tt.args.stringSlice) + if (err != nil) != tt.wantErr { + t.Errorf("ConvertStringSliceToIntSlice() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(gotIntSlice, tt.wantIntSlice) { + t.Errorf("ConvertStringSliceToIntSlice() = %v, want %v", gotIntSlice, tt.wantIntSlice) + } + }) + } +}