From a05016c07578726469fc94bdfc654bc9b633f62e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20D=C3=B6ll?= Date: Sun, 22 Dec 2024 20:58:59 +0000 Subject: [PATCH] chore: init repo --- README.md | 36 +----------- cmd/init.go | 40 +++++++++++++ cmd/root.go | 53 +++++++++++++++++ example.go | 1 - gen.go | 3 - go.mod | 30 +++++++++- go.sum | 42 +++++++++++++ internal/cfg/cfg.go | 37 ++++++++++++ internal/cfg/cfg_test.go | 28 +++++++++ main.go | 17 ++++++ pkg/spec/spec.go | 123 +++++++++++++++++++++++++++++++++++++++ 11 files changed, 369 insertions(+), 41 deletions(-) create mode 100644 cmd/init.go create mode 100644 cmd/root.go delete mode 100644 example.go delete mode 100644 gen.go create mode 100644 internal/cfg/cfg.go create mode 100644 internal/cfg/cfg_test.go create mode 100644 main.go create mode 100644 pkg/spec/spec.go diff --git a/README.md b/README.md index e18a0cc..b895009 100644 --- a/README.md +++ b/README.md @@ -1,35 +1 @@ -# :partying_face: Template Go - -[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/ZEISS/template-go?quickstart=1) - -> This is a GitHub Template Repository. You can use the green button to create a new repository based on this template. Read more about [GitHub Template Repositories](https://help.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-from-a-template). - -## Get Started - -This template supports `Makefile` to run tooling. - -> `make` is choosen as it is available on most systems. - -```bash -# show `help` -make help -``` - -Other available targets are - -```bash -build Build the binary file. -clean Remove previous build. -fmt Run go fmt against code. -generate Generate code. -help Display this help screen. -lint Run lint. -mocks Generate mocks. -release Release the project. -test Run tests. -vet Run go vet against code. -``` - -The convention is to use `make` to run the build. - -Happy coding! +# `g` diff --git a/cmd/init.go b/cmd/init.go new file mode 100644 index 0000000..4a0c201 --- /dev/null +++ b/cmd/init.go @@ -0,0 +1,40 @@ +package cmd + +import ( + "context" + "os" + "path/filepath" + + "github.com/zeiss/g/pkg/spec" + + "github.com/spf13/cobra" + "github.com/zeiss/pkg/filex" +) + +var InitCmd = &cobra.Command{ + Use: "init", + Short: "Initialize a new config", + RunE: func(cmd *cobra.Command, args []string) error { + return runInit(cmd.Context()) + }, +} + +func runInit(_ context.Context) error { + if err := spec.Write(spec.Default(), spec.DefaultFilename, config.Force); err != nil { + return err + } + + cwd, err := config.Cwd() + if err != nil { + return err + } + + path := filepath.Clean(filepath.Join(cwd, spec.DefaultFilename)) + + err = filex.MkdirAll(path, os.ModePerm) + if err != nil { + return err + } + + return nil +} diff --git a/cmd/root.go b/cmd/root.go new file mode 100644 index 0000000..bb50ae4 --- /dev/null +++ b/cmd/root.go @@ -0,0 +1,53 @@ +package cmd + +import ( + "context" + "fmt" + + "github.com/zeiss/g/internal/cfg" + + "github.com/spf13/cobra" +) + +var config = cfg.Default() + +func init() { + RootCmd.AddCommand(InitCmd) + + RootCmd.PersistentFlags().BoolVarP(&config.Verbose, "verbose", "v", config.Verbose, "verbose output") + RootCmd.PersistentFlags().StringVarP(&config.Template, "template", "t", config.Template, "template to use") + RootCmd.PersistentFlags().BoolVarP(&config.Force, "force", "f", config.Force, "force overwrite") + + RootCmd.Flags().StringVarP(&config.Prefix, "prefix", "p", config.Prefix, "prefix to use") + + RootCmd.SilenceErrors = true + RootCmd.SilenceUsage = true +} + +var RootCmd = &cobra.Command{ + Use: "g", + Short: "g is a tiny scaffolding tool", + RunE: func(cmd *cobra.Command, args []string) error { + return runRoot(cmd.Context(), args...) + }, + PreRunE: func(cmd *cobra.Command, args []string) error { + return preRunRoot(cmd.Context(), args...) + }, +} + +func preRunRoot(ctx context.Context, args ...string) error { + cwd, err := config.Cwd() + if err != nil { + return err + } + + if len(args) == 0 { + return fmt.Errorf("no folder name provided") + } + + return nil +} + +func runRoot(ctx context.Context, args ...string) error { + return nil +} diff --git a/example.go b/example.go deleted file mode 100644 index f7ec372..0000000 --- a/example.go +++ /dev/null @@ -1 +0,0 @@ -package example diff --git a/gen.go b/gen.go deleted file mode 100644 index 16b59d5..0000000 --- a/gen.go +++ /dev/null @@ -1,3 +0,0 @@ -package example - -//go:generate echo "Generating ..." diff --git a/go.mod b/go.mod index 0fecf9d..871df3f 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,29 @@ -module example +module github.com/zeiss/g -go 1.23 +go 1.23.0 + +toolchain go1.23.4 + +require ( + github.com/go-playground/validator/v10 v10.23.0 + github.com/pkg/errors v0.9.1 + github.com/spf13/cobra v1.8.1 + github.com/stretchr/testify v1.10.0 + github.com/zeiss/pkg v0.1.20-0.20241222204231-aedc4fdc2e9d + gopkg.in/yaml.v3 v3.0.1 +) + +require ( + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/spf13/pflag v1.0.5 // indirect + golang.org/x/crypto v0.31.0 // indirect + golang.org/x/net v0.33.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect +) diff --git a/go.sum b/go.sum index e69de29..94fa932 100644 --- a/go.sum +++ b/go.sum @@ -0,0 +1,42 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o= +github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/zeiss/pkg v0.1.20-0.20241222204231-aedc4fdc2e9d h1:f1g+rkmkCFHgzL7t1+0XSx00RG/NFg13LOr3J3BWyxw= +github.com/zeiss/pkg v0.1.20-0.20241222204231-aedc4fdc2e9d/go.mod h1:oINcb9td16FNEIRgOllOo2Ma7BfgZieWFmoqAgcyuRI= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/cfg/cfg.go b/internal/cfg/cfg.go new file mode 100644 index 0000000..20dd5a5 --- /dev/null +++ b/internal/cfg/cfg.go @@ -0,0 +1,37 @@ +package cfg + +import "os" + +// Config is the configuration for the application. +type Config struct { + // Verbose enables verbose logging. + Verbose bool + // Template is the template to use. + Template string + // Force forces the creation of the file. + Force bool + // Prefix is the prefix to use. + Prefix string +} + +// Cwd is the current working directory. +func (c *Config) Cwd() (string, error) { + p, err := os.Getwd() + if err != nil { + return "", err + } + + return p, nil +} + +// New returns a new Config. +func New() *Config { + return &Config{} +} + +// Default returns the default configuration. +func Default() *Config { + return &Config{ + Verbose: false, + } +} diff --git a/internal/cfg/cfg_test.go b/internal/cfg/cfg_test.go new file mode 100644 index 0000000..3e6a12f --- /dev/null +++ b/internal/cfg/cfg_test.go @@ -0,0 +1,28 @@ +package cfg_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/zeiss/g/internal/cfg" +) + +func TestNew(t *testing.T) { + t.Parallel() + + c := cfg.New() + assert.NotNil(t, c) + assert.False(t, c.Verbose) + assert.Equal(t, "", c.Template) + assert.False(t, c.Force) +} + +func TestDefault(t *testing.T) { + t.Parallel() + + c := cfg.Default() + assert.NotNil(t, c) + assert.False(t, c.Verbose) + assert.Equal(t, "", c.Template) + assert.False(t, c.Force) +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..8a0228f --- /dev/null +++ b/main.go @@ -0,0 +1,17 @@ +package main + +import ( + "log" + "os" + + "github.com/zeiss/g/cmd" +) + +func main() { + log.SetFlags(0) + log.SetOutput(os.Stderr) + + if err := cmd.RootCmd.Execute(); err != nil { + log.Fatal(err) + } +} diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go new file mode 100644 index 0000000..c992a1b --- /dev/null +++ b/pkg/spec/spec.go @@ -0,0 +1,123 @@ +package spec + +import ( + "fmt" + "os" + "path/filepath" + "sync" + + "github.com/go-playground/validator/v10" + "github.com/pkg/errors" + "github.com/zeiss/pkg/filex" + "gopkg.in/yaml.v3" +) + +const ( + // DefaultVersion is the default version of the specification. + DefaultVersion = 1 + // DefaultFilename is the default filename of the specification. + DefaultFilename = ".g.yml" +) + +var validate = validator.New() + +// Spec is the specification for the scaffolding tool. +type Spec struct { + // Version is the version of the specification. + Version int `validate:"required" yaml:"version"` + // Name is the name of the template. + Name string `validate:"required" yaml:"name"` + // Description is the description of the template. + Description string `yaml:"description"` + // Templates is the list of templates to use. + Templates []Template `yaml:"templates"` + // PreRun is the pre hook to run. + PreRun []string `yaml:"preRun"` + // PostRun is the post hook to run. + PostRun []string `yaml:"postRun"` + // Vars is the list of variables to use. + Vars map[string]interface{} `yaml:"vars"` + + sync.Mutex `yaml:"-"` +} + +// Template is a template to use. +type Template struct { + // Source is the source of the template. + Source string `validate:"required" yaml:"source"` + // Destination is the destination of the template. + Destination string `validate:"required" yaml:"destination"` +} + +// TemplateMap is a map of templates. +func (s *Spec) TemplateMap() map[string]string { + m := make(map[string]string) + for _, t := range s.Templates { + m[t.Source] = t.Destination + } + + return m +} + +// UnmarshalYAML implements the yaml.Unmarshaler interface. +func (s *Spec) UnmarshalYAML(data []byte) error { + ss := struct { + Version int `yaml:"version"` + Name string `yaml:"name"` + Description string `yaml:"description"` + Templates []Template `yaml:"templates"` + PreRun []string `yaml:"preRun"` + PostRun []string `yaml:"postRun"` + }{} + + if err := yaml.Unmarshal(data, &ss); err != nil { + return errors.WithStack(err) + } + + s.Version = ss.Version + s.Name = ss.Name + s.Description = ss.Description + s.Templates = ss.Templates + s.PreRun = ss.PreRun + s.PostRun = ss.PostRun + + err := validate.Struct(s) + if err != nil { + return err + } + + return err +} + +// Default returns the default specification. +func Default() *Spec { + return &Spec{ + Version: DefaultVersion, + } +} + +// Write writes the specification to the given file. +func Write(s *Spec, file string, force bool) error { + b, err := yaml.Marshal(s) + if err != nil { + return err + } + + ok, _ := filex.FileExists(filepath.Clean(file)) + if ok && !force { + return fmt.Errorf("%s already exists, use --force to overwrite", file) + } + + f, err := os.Create(filepath.Clean(file)) + if err != nil { + return err + } + defer func() { _ = f.Close() }() + + _, err = f.Write(b) + if err != nil { + return err + } + + return nil +}