Changes:
- Изменен пакет blueprint - Пример шаблона начал приводить к нашему дефолтному коду Added: - Добавлены модули: логер (zap), окружение
This commit is contained in:
parent
49466a901f
commit
ee3accd198
@ -19,12 +19,12 @@ var (
|
||||
|
||||
// BlueprintFile - описание модели файла.
|
||||
type BlueprintFile struct {
|
||||
ProjectName string `yaml:"ProjectName"`
|
||||
Description string `yaml:"Description,omitempty"`
|
||||
Author string `yaml:"Author,omitempty"`
|
||||
ModulesSettings map[string]any `yaml:"Modules"`
|
||||
Template TemplateSettings `yaml:"Template,omitempty"`
|
||||
Vars map[string]any `yaml:"Vars,omitempty"`
|
||||
ProjectName string `yaml:"ProjectName"`
|
||||
Description string `yaml:"Description,omitempty"`
|
||||
Author string `yaml:"Author,omitempty"`
|
||||
ModulesSettings map[string]any `yaml:"Modules"`
|
||||
Template Settings `yaml:"Template,omitempty"`
|
||||
Vars map[string]any `yaml:"Vars,omitempty"`
|
||||
}
|
||||
|
||||
func readFile(blueprintFilePath string) (blueprint *BlueprintFile, err error) {
|
||||
|
@ -3,10 +3,10 @@ package blueprint
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type Deps struct {
|
||||
}
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/bluepint/blueprint/modules/env"
|
||||
"penahub.gitlab.yandexcloud.net/pena-services/bluepint/blueprint/modules/logger"
|
||||
)
|
||||
|
||||
type BlueprintCreator struct {
|
||||
file *BlueprintFile
|
||||
@ -17,7 +17,6 @@ type BlueprintCreator struct {
|
||||
type BlueprintModule interface {
|
||||
Name() string
|
||||
SetSettings(settings any) error
|
||||
GetFunctions() any
|
||||
Execute() error
|
||||
}
|
||||
|
||||
@ -28,13 +27,19 @@ func NewBlueprintCreator(blueprintPath string) (*BlueprintCreator, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blueprintModules := []BlueprintModule{}
|
||||
blueprintModules := []BlueprintModule{
|
||||
logger.NewLoggerModule(),
|
||||
env.NewEnvModule(),
|
||||
}
|
||||
|
||||
templateModules := make(map[string]BlueprintModule, 0)
|
||||
|
||||
for _, module := range blueprintModules {
|
||||
if file.ModulesSettings[module.Name()] != nil {
|
||||
if err = module.SetSettings(file.ModulesSettings[module.Name()]); err != nil {
|
||||
return nil, fmt.Errorf("blueprintCreator: cannot set settings for module %v. %v", module.Name(), err)
|
||||
}
|
||||
templateModules[module.Name()] = module
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,11 +49,11 @@ func NewBlueprintCreator(blueprintPath string) (*BlueprintCreator, error) {
|
||||
return nil, fmt.Errorf("blueprintCreator: cannot get absolute path for execPath. %v", err)
|
||||
}
|
||||
|
||||
template := NewTemplate(TemplateSettings{
|
||||
template := NewTemplate(Settings{
|
||||
ExecPath: absExecPath,
|
||||
TemplatePath: file.Template.TemplatePath,
|
||||
Vars: file.Template.Vars,
|
||||
Functions: nil,
|
||||
Modules: templateModules,
|
||||
})
|
||||
|
||||
template.SetVar("ProjectName", file.ProjectName)
|
||||
@ -63,15 +68,15 @@ func NewBlueprintCreator(blueprintPath string) (*BlueprintCreator, error) {
|
||||
}
|
||||
|
||||
func (r *BlueprintCreator) Create() error {
|
||||
if err := r.template.Execute(); err != nil {
|
||||
return fmt.Errorf("template execute. %v", err)
|
||||
}
|
||||
|
||||
for _, module := range r.modules {
|
||||
if err := module.Execute(); err != nil {
|
||||
return fmt.Errorf("module execute (%v). %v", module.Name(), err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := r.template.Execute(); err != nil {
|
||||
return fmt.Errorf("template execute. %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
90
blueprint/modules/env/env.go
vendored
Normal file
90
blueprint/modules/env/env.go
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type EnvModule struct {
|
||||
settings Settings
|
||||
}
|
||||
|
||||
type Settings struct {
|
||||
Vars []Var `yaml:"vars"`
|
||||
}
|
||||
|
||||
type Var struct {
|
||||
Name string `yaml:"name"`
|
||||
Type string `yaml:"type"`
|
||||
Required bool `yaml:"required,omitempty"`
|
||||
Default string `yaml:"default"`
|
||||
}
|
||||
|
||||
func (r Var) code() string {
|
||||
var fieldName, req, def string
|
||||
|
||||
if r.Required {
|
||||
req = ",required"
|
||||
}
|
||||
|
||||
if r.Default != "" {
|
||||
def = fmt.Sprintf(` envDefault:"%v"`, r.Default)
|
||||
}
|
||||
|
||||
caser := cases.Title(language.English)
|
||||
|
||||
for _, item := range strings.Split(r.Name, "_") {
|
||||
fieldName += caser.String(item)
|
||||
}
|
||||
|
||||
return fmt.Sprintf(" %v %v `env:\"%v%v\"%v`", fieldName, r.Type, r.Name, req, def)
|
||||
}
|
||||
|
||||
func NewEnvModule() *EnvModule {
|
||||
return &EnvModule{}
|
||||
}
|
||||
|
||||
func (r *EnvModule) Name() string {
|
||||
return "env"
|
||||
}
|
||||
|
||||
func (r *EnvModule) SetSettings(settings any) error {
|
||||
out, err := yaml.Marshal(settings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return yaml.Unmarshal(out, &r.settings)
|
||||
}
|
||||
|
||||
func (r *EnvModule) Execute() error {
|
||||
cmd := exec.Command("go", "get", r.Import())
|
||||
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
func (r *EnvModule) Import() string {
|
||||
return `"github.com/caarlos0/env/v8"`
|
||||
}
|
||||
|
||||
func (r *EnvModule) Declaration(v string) string {
|
||||
return fmt.Sprintf(`err = env.Parse(%v)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}`, v)
|
||||
}
|
||||
|
||||
func (r *EnvModule) Struct() string {
|
||||
var varsString string
|
||||
|
||||
for _, v := range r.settings.Vars {
|
||||
varsString += fmt.Sprintf("%v\r\n", v.code())
|
||||
}
|
||||
|
||||
return fmt.Sprintf("type Config struct {\r\n%v}", varsString)
|
||||
}
|
29
blueprint/modules/env/env_test.go
vendored
Normal file
29
blueprint/modules/env/env_test.go
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
package env
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestVar_code(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
r Var
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "default",
|
||||
r: Var{
|
||||
Name: "VAR_EXAMPLE",
|
||||
Type: "string",
|
||||
Required: true,
|
||||
Default: "default value",
|
||||
},
|
||||
want: " VarExample string `env:\"VAR_EXAMPLE,required\" envDefault:\"default value\"`",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := tt.r.code(); got != tt.want {
|
||||
t.Errorf("Var.code() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
95
blueprint/modules/logger/logger.go
Normal file
95
blueprint/modules/logger/logger.go
Normal file
@ -0,0 +1,95 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type LoggerModule struct {
|
||||
settings Settings
|
||||
logger Logger
|
||||
}
|
||||
|
||||
type Settings struct {
|
||||
Name LoggerType `yaml:"name"`
|
||||
}
|
||||
|
||||
type LoggerType string
|
||||
|
||||
const (
|
||||
LoggerTypeZap = "zap"
|
||||
)
|
||||
|
||||
func (r LoggerType) Valid() bool {
|
||||
return r == LoggerTypeZap
|
||||
}
|
||||
|
||||
func NewLoggerModule() *LoggerModule {
|
||||
return &LoggerModule{}
|
||||
}
|
||||
|
||||
func (r *LoggerModule) Name() string {
|
||||
return "logger"
|
||||
}
|
||||
|
||||
func (r *LoggerModule) SetSettings(settings any) error {
|
||||
out, err := yaml.Marshal(settings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = yaml.Unmarshal(out, &r.settings); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !r.settings.Name.Valid() {
|
||||
return fmt.Errorf("invalid logger type")
|
||||
}
|
||||
|
||||
switch r.settings.Name {
|
||||
case LoggerTypeZap:
|
||||
r.logger = NewZapLogger()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *LoggerModule) GetFunctions() any {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *LoggerModule) Execute() error {
|
||||
cmd := exec.Command("go", "get", r.Import())
|
||||
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
type Logger interface {
|
||||
Import() string
|
||||
ImportCore() string
|
||||
Declaration() string
|
||||
Type() string
|
||||
Message(messageType string, message string, fields ...any) string
|
||||
}
|
||||
|
||||
func (r *LoggerModule) Import() string {
|
||||
return r.logger.Import()
|
||||
}
|
||||
|
||||
func (r *LoggerModule) ImportCore() string {
|
||||
return r.logger.ImportCore()
|
||||
}
|
||||
|
||||
func (r *LoggerModule) Init() string {
|
||||
return r.logger.Declaration()
|
||||
}
|
||||
|
||||
func (r *LoggerModule) Type() string {
|
||||
return r.logger.Type()
|
||||
}
|
||||
|
||||
func (r *LoggerModule) Message(messageType, message string, fields ...any) string {
|
||||
return r.logger.Message(messageType, message, fields...)
|
||||
}
|
59
blueprint/modules/logger/zap.go
Normal file
59
blueprint/modules/logger/zap.go
Normal file
@ -0,0 +1,59 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
type ZapLogger struct {
|
||||
}
|
||||
|
||||
func NewZapLogger() *ZapLogger {
|
||||
return &ZapLogger{}
|
||||
}
|
||||
|
||||
func (r *ZapLogger) Import() string {
|
||||
return `"go.uber.org/zap"`
|
||||
}
|
||||
|
||||
func (r *ZapLogger) ImportCore() string {
|
||||
return `"go.uber.org/zap/zapcore"`
|
||||
}
|
||||
|
||||
func (r *ZapLogger) Declaration() string {
|
||||
return `cfgLogger := zap.NewDevelopmentConfig()
|
||||
cfgLogger.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
|
||||
cfgLogger.EncoderConfig.ConsoleSeparator = " "
|
||||
|
||||
logger, err := cfgLogger.Build()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}`
|
||||
}
|
||||
|
||||
func (r *ZapLogger) Type() string {
|
||||
return "*zap.Logger"
|
||||
}
|
||||
|
||||
func (r *ZapLogger) Message(messageType, message string, fields ...any) string {
|
||||
var fieldsToString string
|
||||
|
||||
caser := cases.Title(language.English)
|
||||
messageType = caser.String(messageType)
|
||||
|
||||
if len(fields)%2 != 0 {
|
||||
return `//FIXME TEMPLATE ERROR: logger.Message need even number of fields. Example: logger.Message <messageType> <some msg> <fieldName> <fieldVal>...`
|
||||
}
|
||||
|
||||
for i := 0; i < len(fields); i += 2 {
|
||||
fieldsToString += fmt.Sprintf(`zap.Any("%v", %v)`, fields[i], fields[i+1])
|
||||
|
||||
if i+1 < len(fields)-1 {
|
||||
fieldsToString += ", "
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf(`logger.%v("%v", %v)`, messageType, message, fieldsToString)
|
||||
}
|
45
blueprint/modules/logger/zap_test.go
Normal file
45
blueprint/modules/logger/zap_test.go
Normal file
@ -0,0 +1,45 @@
|
||||
package logger
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestZapLogger_InfoMsg(t *testing.T) {
|
||||
type args struct {
|
||||
messageType string
|
||||
message string
|
||||
fields []any
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
r *ZapLogger
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "default",
|
||||
r: &ZapLogger{},
|
||||
args: args{
|
||||
messageType: "info",
|
||||
message: `some msg`,
|
||||
fields: []any{`key1`, 1, `key2`, 2},
|
||||
},
|
||||
want: `logger.Info("some msg", zap.Any("key1", 1), zap.Any("key2", 2))`,
|
||||
},
|
||||
{
|
||||
name: "error need even number of fields",
|
||||
r: &ZapLogger{},
|
||||
args: args{
|
||||
messageType: "info",
|
||||
message: `some msg`,
|
||||
fields: []any{`key1`},
|
||||
},
|
||||
want: `//FIXME TEMPLATE ERROR: logger.Message need even number of fields. Example: logger.Message <messageType> <some msg> <fieldName> <fieldVal>...`,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := tt.r.Message(tt.args.messageType, tt.args.message, tt.args.fields...); got != tt.want {
|
||||
t.Errorf("ZapLogger.Message() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -11,7 +11,6 @@ import (
|
||||
"text/template"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -22,27 +21,24 @@ var (
|
||||
TemplateFormats = []string{"tmpl", "gotmpl"}
|
||||
)
|
||||
|
||||
type TemplateSettings struct {
|
||||
ExecPath string `yaml:"-"`
|
||||
TemplatePath string `yaml:"path"`
|
||||
Vars map[string]any `yaml:"vars,omitempty"`
|
||||
Functions map[string]any `yaml:"-"`
|
||||
type Settings struct {
|
||||
ExecPath string `yaml:"-"`
|
||||
TemplatePath string `yaml:"path"`
|
||||
Vars map[string]any `yaml:"vars,omitempty"`
|
||||
Modules map[string]BlueprintModule `yaml:"-"`
|
||||
// TODO: добавить модули сюда и попробовать вызвать функцию
|
||||
}
|
||||
|
||||
type Template struct {
|
||||
settings *TemplateSettings
|
||||
settings *Settings
|
||||
queue []string
|
||||
}
|
||||
|
||||
func NewTemplate(settings TemplateSettings) *Template {
|
||||
func NewTemplate(settings Settings) *Template {
|
||||
if settings.Vars == nil {
|
||||
settings.Vars = make(map[string]any)
|
||||
}
|
||||
|
||||
if settings.Functions == nil {
|
||||
settings.Functions = make(map[string]any)
|
||||
}
|
||||
|
||||
return &Template{
|
||||
settings: &settings,
|
||||
queue: make([]string, 0),
|
||||
@ -59,23 +55,6 @@ func (r *Template) SetVar(name string, value any) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (r *Template) SetSettings(settings any) error {
|
||||
out, err := yaml.Marshal(settings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = yaml.Unmarshal(out, &r.settings); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if r.settings.TemplatePath == "" {
|
||||
r.settings.TemplatePath = DefaultPath
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Template) Execute() error {
|
||||
log.Info().Msg("template.Execute: started")
|
||||
err := filepath.WalkDir(r.settings.TemplatePath, r.walkFunc)
|
||||
@ -134,7 +113,7 @@ func (r *Template) templateFile(templatePath string) error {
|
||||
return fmt.Errorf("templateFile: parse template (%v). %v", templatePath, err)
|
||||
}
|
||||
|
||||
if err = t.Execute(file, r.settings.Vars); err != nil {
|
||||
if err = t.Execute(file, r.settings); err != nil {
|
||||
return fmt.Errorf("templateFile: execute template (%v). %v", templatePath, err)
|
||||
}
|
||||
|
||||
@ -149,7 +128,7 @@ func (r *Template) execCommands() error {
|
||||
log.Info().Msg("execCommands: go mod init")
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
return fmt.Errorf("execCommands: go mod init. %v", err)
|
||||
}
|
||||
|
||||
cmd = exec.Command("go", "mod", "tidy")
|
||||
|
@ -3,3 +3,12 @@ Description: Some descr
|
||||
|
||||
Template:
|
||||
path: "./"
|
||||
|
||||
Modules:
|
||||
logger:
|
||||
name: zap
|
||||
env:
|
||||
vars:
|
||||
- name: APP_NAME
|
||||
type: string
|
||||
default: "{{.ProjectName}}"
|
||||
|
@ -1,11 +1,30 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"{{.ProjectName}}/internal/app"
|
||||
"context"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"{{.Vars.ProjectName}}/internal/app"
|
||||
|
||||
{{.Modules.logger.Import}}
|
||||
{{.Modules.logger.ImportCore}}
|
||||
|
||||
{{.Modules.env.Import}}
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := app.New()
|
||||
app.Run()
|
||||
{{.Modules.logger.Init}}
|
||||
|
||||
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||
defer cancel()
|
||||
|
||||
var config app.Config
|
||||
|
||||
{{.Modules.env.Declaration "config"}}
|
||||
|
||||
if err := app.Run(ctx, config, logger); err != nil {
|
||||
{{.Modules.logger.Message "Fatal" "Failed to run app" "Error" "err"}}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,17 +1,19 @@
|
||||
package app
|
||||
|
||||
type App struct {
|
||||
config *Config
|
||||
}
|
||||
import (
|
||||
"context"
|
||||
|
||||
type Config struct {
|
||||
Name string `env:"NAME"`
|
||||
}
|
||||
{{.Modules.logger.Import}}
|
||||
)
|
||||
|
||||
func New() *App {
|
||||
return &App{}
|
||||
}
|
||||
{{.Modules.env.Struct}}
|
||||
|
||||
func (r *App) Run() {
|
||||
|
||||
func Run(ctx context.Context, config Config, logger {{.Modules.logger.Type}}) error {
|
||||
{{.Modules.logger.Message "info" "App started" "config" "config"}}
|
||||
|
||||
<-ctx.Done()
|
||||
|
||||
logger.Info("App shutting down gracefully")
|
||||
|
||||
return nil
|
||||
}
|
1
go.mod
1
go.mod
@ -12,4 +12,5 @@ require (
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
golang.org/x/sys v0.15.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
)
|
||||
|
2
go.sum
2
go.sum
@ -15,6 +15,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
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.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
|
Loading…
Reference in New Issue
Block a user