From 8f4ef2f62afdda8bec61de423ec3150b160c165f Mon Sep 17 00:00:00 2001 From: Qiaozp Date: Mon, 13 Mar 2023 14:27:50 +0800 Subject: [PATCH] seperate def and module modifier Signed-off-by: Qiaozp --- pkg/definition/gen_sdk/_scaffold/go/go.mod_ | 5 + pkg/definition/gen_sdk/gen_sdk.go | 89 ++++--- pkg/definition/gen_sdk/gen_sdk_test.go | 2 +- pkg/definition/gen_sdk/go.go | 271 ++++++++++++++------ references/cli/def.go | 10 +- 5 files changed, 262 insertions(+), 115 deletions(-) diff --git a/pkg/definition/gen_sdk/_scaffold/go/go.mod_ b/pkg/definition/gen_sdk/_scaffold/go/go.mod_ index 99468113b..f06562f9c 100644 --- a/pkg/definition/gen_sdk/_scaffold/go/go.mod_ +++ b/pkg/definition/gen_sdk/_scaffold/go/go.mod_ @@ -4,11 +4,16 @@ go 1.19 require ( github.com/oam-dev/kubevela-core-api v1.5.8 + + // for main module github.com/pkg/errors v0.9.1 k8s.io/apimachinery v0.23.6 k8s.io/client-go v0.23.6 sigs.k8s.io/controller-runtime v0.11.2 sigs.k8s.io/yaml v1.3.0 + + // for sub-module + github.com/kubevela/vela-go-sdk v0.0.0-20230310062146-e5d4070da6b4 ) require ( diff --git a/pkg/definition/gen_sdk/gen_sdk.go b/pkg/definition/gen_sdk/gen_sdk.go index 53efeffcf..95bd2cc20 100644 --- a/pkg/definition/gen_sdk/gen_sdk.go +++ b/pkg/definition/gen_sdk/gen_sdk.go @@ -34,6 +34,7 @@ import ( "github.com/kubevela/pkg/util/slices" "github.com/kubevela/workflow/pkg/cue/model/value" "github.com/pkg/errors" + "github.com/spf13/pflag" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/client-go/rest" "k8s.io/klog/v2" @@ -56,6 +57,8 @@ var ( // GenMeta stores the metadata for generator. type GenMeta struct { config *rest.Config + name string + kind string Output string APIDirectory string @@ -66,6 +69,7 @@ type GenMeta struct { File []string InitSDK bool Verbose bool + GoArgs GoArgs cuePaths []string templatePath string @@ -75,11 +79,12 @@ type GenMeta struct { // Generator is used to generate SDK code from CUE template for one language. type Generator struct { meta *GenMeta - name string - kind string def definition.Definition openapiSchema []byte - modifiers []Modifier + // defModifiers are the modifiers for each definition. + defModifiers []Modifier + // moduleModifiers are the modifiers for the whole module. It will be executed after generating all definitions. + moduleModifiers []Modifier } // Modifier is used to modify the generated code. @@ -90,7 +95,7 @@ type Modifier interface { // Init initializes the generator. // It will validate the param, analyze the CUE files, read them to memory, mkdir for output. -func (meta *GenMeta) Init(c common.Args) (err error) { +func (meta *GenMeta) Init(c common.Args, flags *pflag.FlagSet) (err error) { meta.config, err = c.GetConfig() if err != nil { klog.Info("No kubeconfig found, skipping") @@ -107,9 +112,19 @@ func (meta *GenMeta) Init(c common.Args) (err error) { if meta.APIDirectory == "" { meta.APIDirectory = defaultAPIDir[meta.Lang] } + if flags != nil { + switch meta.Lang { + case "go": + if goargs, err := flags.GetStringSlice("go-args"); err != nil { + return err + } else { + meta.GoArgs.parse(goargs) + } + } + } packageFuncs := map[string]byteHandler{ "go": func(b []byte) []byte { - return bytes.ReplaceAll(b, []byte("github.com/kubevela/vela-go-sdk"), []byte(meta.Package)) + return bytes.ReplaceAll(b, []byte(DefaultPackage), []byte(meta.Package)) }, } @@ -248,18 +263,20 @@ func (meta *GenMeta) PrepareGeneratorAndTemplate() error { // 1. Generate OpenAPI schema from cue files // 2. Generate code from OpenAPI schema func (meta *GenMeta) Run() error { + g := NewModifiableGenerator(meta) for _, cuePath := range meta.cuePaths { klog.Infof("Generating SDK for %s", cuePath) - g := NewModifiableGenerator(meta) // nolint:gosec cueBytes, err := os.ReadFile(cuePath) if err != nil { return errors.Wrapf(err, "failed to read %s", cuePath) } - template, err := g.GetDefinitionValue(cueBytes) + template, defName, defKind, err := g.GetDefinitionValue(cueBytes) if err != nil { return err } + g.meta.SetDefinition(defName, defKind) + err = g.GenOpenAPISchema(template) if err != nil { if strings.Contains(err.Error(), "unsupported node string (*ast.Ident)") { @@ -269,32 +286,43 @@ func (meta *GenMeta) Run() error { } return errors.Wrapf(err, "generate OpenAPI schema") } + err = g.GenerateCode() if err != nil { return err } } + for _, m := range g.moduleModifiers { + err := m.Modify() + if err != nil { + return err + } + } + return nil } -// GetDefinitionValue returns a value.Value from cue bytes -func (g *Generator) GetDefinitionValue(cueBytes []byte) (*value.Value, error) { +func (meta *GenMeta) SetDefinition(defName, defKind string) { + meta.name = defName + meta.kind = defKind +} + +// GetDefinitionValue returns a value.Value definition name, definition kind from cue bytes +func (g *Generator) GetDefinitionValue(cueBytes []byte) (*value.Value, string, string, error) { g.def = definition.Definition{Unstructured: unstructured.Unstructured{}} if err := g.def.FromCUEString(string(cueBytes), g.meta.config); err != nil { - return nil, errors.Wrapf(err, "failed to parse CUE") + return nil, "", "", errors.Wrapf(err, "failed to parse CUE") } - g.name = g.def.GetName() - g.kind = g.def.GetKind() templateString, _, err := unstructured.NestedString(g.def.Object, definition.DefinitionTemplateKeys...) if err != nil { - return nil, err + return nil, "", "", err } template, err := value.NewValue(templateString+velacue.BaseTemplate, nil, "") if err != nil { - return nil, err + return nil, "", "", err } - return template, nil + return template, g.def.GetName(), g.def.GetKind(), nil } // GenOpenAPISchema generates OpenAPI json schema from cue.Instance @@ -349,13 +377,13 @@ func (g *Generator) completeOpenAPISchema(doc *openapi3.T) { for key, schema := range doc.Components.Schemas { switch key { case "parameter": - spec := g.name + "-spec" + spec := g.meta.name + "-spec" schema.Value.Title = spec completeFreeFormSchema(schema) completeSchema(key, schema) doc.Components.Schemas[spec] = schema delete(doc.Components.Schemas, key) - case g.name + "-spec": + case g.meta.name + "-spec": continue default: completeSchema(key, schema) @@ -365,7 +393,7 @@ func (g *Generator) completeOpenAPISchema(doc *openapi3.T) { // GenerateCode will call openapi-generator to generate code and modify it func (g *Generator) GenerateCode() (err error) { - tmpFile, err := os.CreateTemp("", g.name+"-*.json") + tmpFile, err := os.CreateTemp("", g.meta.name+"-*.json") _, err = tmpFile.Write(g.openapiSchema) if err != nil { return errors.Wrap(err, "write openapi schema to temporary file") @@ -380,7 +408,7 @@ func (g *Generator) GenerateCode() (err error) { if err != nil { return errors.Wrapf(err, "get absolute path of %s", apiDir) } - err = os.MkdirAll(path.Join(apiDir, definition.DefinitionKindToType[g.kind]), 0750) + err = os.MkdirAll(path.Join(apiDir, definition.DefinitionKindToType[g.meta.kind]), 0750) if err != nil { return errors.Wrapf(err, "create directory %s", apiDir) } @@ -396,13 +424,13 @@ func (g *Generator) GenerateCode() (err error) { "generate", "-i", "/local/input/"+filepath.Base(tmpFile.Name()), "-g", g.meta.Lang, - "-o", fmt.Sprintf("/local/output/%s/%s", definition.DefinitionKindToType[g.kind], g.name), + "-o", fmt.Sprintf("/local/output/%s/%s", definition.DefinitionKindToType[g.meta.kind], g.meta.name), "-t", "/local/template", "--skip-validate-spec", "--enable-post-process-file", "--generate-alias-as-model", "--inline-schema-name-defaults", "arrayItemSuffix=,mapItemSuffix=", - "--additional-properties", fmt.Sprintf("packageName=%s", strings.ReplaceAll(g.name, "-", "_")), + "--additional-properties", fmt.Sprintf("packageName=%s", strings.ReplaceAll(g.meta.name, "-", "_")), "--global-property", "modelDocs=false,models,supportingFiles=utils.go", ) if g.meta.Verbose { @@ -417,7 +445,7 @@ func (g *Generator) GenerateCode() (err error) { } // Adjust the generated files and code - for _, m := range g.modifiers { + for _, m := range g.defModifiers { err := m.Modify() if err != nil { return errors.Wrapf(err, "modify fail by %s", m.Name()) @@ -543,20 +571,19 @@ func completeSchemas(schemas openapi3.Schemas) { // NewModifiableGenerator returns a new Generator with modifiers func NewModifiableGenerator(meta *GenMeta) *Generator { g := &Generator{ - meta: meta, - modifiers: []Modifier{}, + meta: meta, + defModifiers: []Modifier{}, + moduleModifiers: []Modifier{}, } - mo := newModifierOnLanguage(meta.Lang, g) - g.modifiers = append(g.modifiers, mo) + appendModifiersByLanguage(g, meta) return g } -func newModifierOnLanguage(lang string, generator *Generator) Modifier { - switch lang { +func appendModifiersByLanguage(g *Generator, meta *GenMeta) { + switch meta.Lang { case "go": - return &GoModifier{g: generator} - default: - panic("unsupported language: " + lang) + g.defModifiers = append(g.defModifiers, &GoDefModifier{GenMeta: meta}) + g.moduleModifiers = append(g.moduleModifiers, &GoModuleModifier{GenMeta: meta}) } } diff --git a/pkg/definition/gen_sdk/gen_sdk_test.go b/pkg/definition/gen_sdk/gen_sdk_test.go index 047108125..b7883467d 100644 --- a/pkg/definition/gen_sdk/gen_sdk_test.go +++ b/pkg/definition/gen_sdk/gen_sdk_test.go @@ -44,7 +44,7 @@ var _ = Describe("Test Generating SDK", func() { Expect(err).Should(BeNil()) } genWithMeta := func(meta GenMeta) { - err = meta.Init(common.Args{}) + err = meta.Init(common.Args{}, nil) Expect(err).Should(BeNil()) err = meta.CreateScaffold() Expect(err).Should(BeNil()) diff --git a/pkg/definition/gen_sdk/go.go b/pkg/definition/gen_sdk/go.go index 81e13e0d1..1fa8aa459 100644 --- a/pkg/definition/gen_sdk/go.go +++ b/pkg/definition/gen_sdk/go.go @@ -23,6 +23,7 @@ import ( "os" "os/exec" "path" + "path/filepath" "regexp" "strings" @@ -36,6 +37,20 @@ import ( pkgdef "github.com/oam-dev/kubevela/pkg/definition" ) +type GoArgs struct { + // MainModuleVersion is version of main module, it will be used in go get command. For example, tag, commit id, branch name + // if set, vela will run a go get command in docker + MainModuleVersion string + // GoProxy is the proxy for go get/go mod tidy command + GoProxy string +} + +const ( + DefaultMainModuleHash = "cd431bb25a9a" + DefaultGoProxy = "https://goproxy.cn,direct" + DefaultPackage = "github.com/kubevela/vela-go-sdk" +) + var ( // DefinitionKindToPascal is the map of definition kind to pascal case DefinitionKindToPascal = map[string]string{ @@ -60,14 +75,20 @@ var ( } ) -// GoModifier is the Modifier for golang -type GoModifier struct { - g *Generator +// GoDefModifier is the Modifier for golang, modify code for each definition +type GoDefModifier struct { + *GenMeta + *goArgs - defName string - defKind string - verbose bool + defStructPointer *j.Statement +} +type GoModuleModifier struct { + *GenMeta + *goArgs +} + +type goArgs struct { apiDir string defDir string utilsDir string @@ -78,25 +99,29 @@ type GoModifier struct { typeVarName string defStructName string defFuncReceiver string - defStructPointer *j.Statement } -// Name the name of modifier -func (m *GoModifier) Name() string { - return "GoModifier" +func (a *goArgs) init(m *GenMeta) error { + var err error + a.apiDir, err = filepath.Abs(path.Join(m.Output, m.APIDirectory)) + if err != nil { + return err + } + a.defDir = path.Join(a.apiDir, pkgdef.DefinitionKindToType[m.kind], m.name) + a.utilsDir = path.Join(m.Output, "pkg", "apis", "utils") + a.nameInSnakeCase = strcase.ToSnake(m.name) + a.nameInPascalCase = strcase.ToPascal(m.name) + a.typeVarName = a.nameInPascalCase + "Type" + a.specNameInPascalCase = a.nameInPascalCase + "Spec" + a.defStructName = strcase.ToGoPascal(m.name + "-" + pkgdef.DefinitionKindToType[m.kind]) + a.defFuncReceiver = m.name[:1] + return nil } -// Modify the modification of generated code -func (m *GoModifier) Modify() error { +func (m *GoModuleModifier) Modify() error { for _, fn := range []func() error{ m.init, - m.clean, m.addSubGoMod, - m.moveUtils, - m.modifyDefs, - m.addDefAPI, - m.addValidateTraits, - m.exportMethods, m.format, } { if err := fn(); err != nil { @@ -106,26 +131,56 @@ func (m *GoModifier) Modify() error { return nil } -func (m *GoModifier) init() error { - m.defName = m.g.name - m.defKind = m.g.kind - m.verbose = m.g.meta.Verbose +func (m *GoModuleModifier) init() error { + m.goArgs = &goArgs{} + err := m.goArgs.init(m.GenMeta) + if err != nil { + return err + } + return nil +} + +func (m *GoModuleModifier) Name() string { + return "goModuleModifier" +} + +// Name the name of modifier +func (m *GoDefModifier) Name() string { + return "GoDefModifier" +} + +// Modify the modification of generated code +func (m *GoDefModifier) Modify() error { + for _, fn := range []func() error{ + m.init, + m.clean, + m.moveUtils, + m.modifyDefs, + m.addDefAPI, + m.addValidateTraits, + m.exportMethods, + } { + if err := fn(); err != nil { + return err + } + } + return nil +} + +func (m *GoDefModifier) init() error { + m.goArgs = &goArgs{} + err := m.goArgs.init(m.GenMeta) + if err != nil { + return err + } - m.apiDir = path.Join(m.g.meta.Output, m.g.meta.APIDirectory) - m.defDir = path.Join(m.apiDir, pkgdef.DefinitionKindToType[m.defKind], m.defName) - m.utilsDir = path.Join(m.g.meta.Output, "pkg", "apis", "utils") - m.nameInSnakeCase = strcase.ToSnake(m.defName) - m.nameInPascalCase = strcase.ToPascal(m.defName) - m.typeVarName = m.nameInPascalCase + "Type" - m.specNameInPascalCase = m.nameInPascalCase + "Spec" - m.defStructName = strcase.ToGoPascal(m.defName + "-" + pkgdef.DefinitionKindToType[m.defKind]) m.defStructPointer = j.Op("*").Id(m.defStructName) - m.defFuncReceiver = m.defName[:1] - err := os.MkdirAll(m.utilsDir, 0750) + + err = os.MkdirAll(m.utilsDir, 0750) return err } -func (m *GoModifier) clean() error { +func (m *GoDefModifier) clean() error { err := os.RemoveAll(path.Join(m.defDir, ".openapi-generator")) if err != nil { return err @@ -150,9 +205,9 @@ func (m *GoModifier) clean() error { } -// addSubGoMod will add a go.mod and go.sum in the api directory if user mark that the api is a sub module -func (m *GoModifier) addSubGoMod() error { - if !m.g.meta.IsSubModule { +// addSubGoMod will add a go.mod and go.sum in the api directory if user mark that the api is a submodule +func (m *GoModuleModifier) addSubGoMod() error { + if !m.IsSubModule { return nil } copyFiles := map[string]string{ @@ -164,25 +219,59 @@ func (m *GoModifier) addSubGoMod() error { if err != nil { return errors.Wrap(err, "read "+src) } + subModuleName := strings.TrimSuffix(fmt.Sprintf("%s/%s", m.Package, m.APIDirectory), "/") + srcContent = bytes.ReplaceAll(srcContent, []byte(DefaultPackage), []byte(subModuleName)) + err = os.WriteFile(path.Join(m.apiDir, dst), srcContent, 0644) if err != nil { return errors.Wrap(err, "write "+dst) } } + + cmds := make([]*exec.Cmd, 0) + if m.GoArgs.MainModuleVersion != DefaultMainModuleHash { + cmds = append(cmds, exec.Command("docker", "run", + "--rm", + "-v", m.apiDir+":/api", + "-w", "/api", + "golang:1.19-alpine", + "go", "get", fmt.Sprintf("%s@%s", m.Package, m.GoArgs.MainModuleVersion), + )) + } + cmds = append(cmds, exec.Command("docker", "run", + "--rm", + "-v", m.apiDir+":/api", + "-w", "/api", + "golang:1.19-alpine", + "go", "mod", "tidy", + )) + for _, cmd := range cmds { + if m.Verbose { + fmt.Println(cmd.String()) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + } + + cmd.Env = append(cmd.Env, "GOPROXY="+m.GoArgs.GoProxy) + err := cmd.Run() + if err != nil { + return errors.Wrapf(err, "fail to run command %s", cmd.String()) + } + } return nil } // read all files in definition directory, // 1. replace the Nullable* Struct // 2. replace the package name -func (m *GoModifier) modifyDefs() error { +func (m *GoDefModifier) modifyDefs() error { changeNullableType := func(b []byte) []byte { return regexp.MustCompile("Nullable(String|(Float|Int)(32|64)|Bool)").ReplaceAll(b, []byte("utils.Nullable$1")) } files, err := os.ReadDir(m.defDir) defHandleFunc := []byteHandler{ - m.g.meta.packageFunc, + m.packageFunc, changeNullableType, } if err != nil { @@ -204,7 +293,7 @@ func (m *GoModifier) modifyDefs() error { return nil } -func (m *GoModifier) moveUtils() error { +func (m *GoDefModifier) moveUtils() error { // Adjust the generated files and code err := os.Rename(path.Join(m.defDir, "utils.go"), path.Join(m.utilsDir, "utils.go")) if err != nil { @@ -217,7 +306,7 @@ func (m *GoModifier) moveUtils() error { if err != nil { return err } - utilsBytes = bytes.Replace(utilsBytes, []byte(fmt.Sprintf("package %s", strcase.ToSnake(m.defName))), []byte("package utils"), 1) + utilsBytes = bytes.Replace(utilsBytes, []byte(fmt.Sprintf("package %s", strcase.ToSnake(m.name))), []byte("package utils"), 1) utilsBytes = bytes.ReplaceAll(utilsBytes, []byte("isNil"), []byte("IsNil")) err = os.WriteFile(utilsFile, utilsBytes, 0600) if err != nil { @@ -227,7 +316,7 @@ func (m *GoModifier) moveUtils() error { } // addDefAPI will add component/trait/workflowstep/policy Object to the api -func (m *GoModifier) addDefAPI() error { +func (m *GoDefModifier) addDefAPI() error { file, err := os.OpenFile(path.Join(m.defDir, m.nameInSnakeCase+".go"), os.O_APPEND|os.O_WRONLY, 0600) if err != nil { return err @@ -260,11 +349,11 @@ func (m *GoModifier) addDefAPI() error { return nil } -func (m *GoModifier) genCommonFunc() []*j.Statement { - kind := m.defKind +func (m *GoDefModifier) genCommonFunc() []*j.Statement { + kind := m.kind typeName := j.Id(m.nameInPascalCase + "Type") - typeConst := j.Const().Add(typeName).Op("=").Lit(m.defName) - j.Op("=").Lit(m.defName) + typeConst := j.Const().Add(typeName).Op("=").Lit(m.name) + j.Op("=").Lit(m.name) defStruct := j.Type().Id(m.defStructName).Struct( j.Id("Base").Id("apis").Dot(DefinitionKindToBaseType[kind]), j.Id("Properties").Id(m.specNameInPascalCase), @@ -352,8 +441,8 @@ func (m *GoModifier) genCommonFunc() []*j.Statement { return []*j.Statement{typeConst, initFunc, defStruct, defStructConstructor, buildFunc} } -func (m *GoModifier) genFromFunc() []*j.Statement { - kind := m.g.kind +func (m *GoDefModifier) genFromFunc() []*j.Statement { + kind := m.kind kindBaseProperties := map[string][]string{ v1beta1.ComponentDefinitionKind: {"Name", "DependsOn", "Inputs", "Outputs"}, v1beta1.WorkflowStepDefinitionKind: {"Name", "DependsOn", "Inputs", "Outputs", "If", "Timeout", "Meta"}, @@ -364,7 +453,7 @@ func (m *GoModifier) genFromFunc() []*j.Statement { // fromFuncRsv means build from a part of K8s Object (e.g. v1beta1.Application.spec.component[*] to internal presentation (e.g. Component) // fromFuncRsv will have a function receiver getSubSteps := func(sub bool) func(g *j.Group) { - if m.defKind != v1beta1.WorkflowStepDefinitionKind || sub { + if m.kind != v1beta1.WorkflowStepDefinitionKind || sub { return func(g *j.Group) {} } return func(g *j.Group) { @@ -382,7 +471,7 @@ func (m *GoModifier) genFromFunc() []*j.Statement { } } assignSubSteps := func(sub bool) func(g *j.Group) { - if m.defKind != v1beta1.WorkflowStepDefinitionKind || sub { + if m.kind != v1beta1.WorkflowStepDefinitionKind || sub { return func(g *j.Group) {} } return func(g *j.Group) { @@ -449,15 +538,15 @@ func (m *GoModifier) genFromFunc() []*j.Statement { ) res := []*j.Statement{fromFuncRsv(false), fromFunc} - if m.defKind == v1beta1.WorkflowStepDefinitionKind { + if m.kind == v1beta1.WorkflowStepDefinitionKind { res = append(res, fromFuncRsv(true), fromSubFunc) } return res } // genDedicatedFunc generate functions for definition kinds -func (m *GoModifier) genDedicatedFunc() []*j.Statement { - switch m.defKind { +func (m *GoDefModifier) genDedicatedFunc() []*j.Statement { + switch m.kind { case v1beta1.ComponentDefinitionKind: setTraitFunc := j.Func(). Params(j.Id(m.defFuncReceiver).Add(m.defStructPointer)). @@ -508,14 +597,14 @@ func (m *GoModifier) genDedicatedFunc() []*j.Statement { return nil } -func (m *GoModifier) genNameTypeFunc() []*j.Statement { - nameFunc := j.Func().Params(j.Id(m.defFuncReceiver).Add(m.defStructPointer)).Id(DefinitionKindToPascal[m.defKind] + "Name").Params().String().Block( +func (m *GoDefModifier) genNameTypeFunc() []*j.Statement { + nameFunc := j.Func().Params(j.Id(m.defFuncReceiver).Add(m.defStructPointer)).Id(DefinitionKindToPascal[m.kind] + "Name").Params().String().Block( j.Return(j.Id(m.defFuncReceiver).Dot("Base").Dot("Name")), ) typeFunc := j.Func().Params(j.Id(m.defFuncReceiver).Add(m.defStructPointer)).Id("DefType").Params().String().Block( j.Return(j.Id(m.typeVarName)), ) - switch m.defKind { + switch m.kind { case v1beta1.ComponentDefinitionKind, v1beta1.WorkflowStepDefinitionKind, v1beta1.PolicyDefinitionKind: return []*j.Statement{nameFunc, typeFunc} case v1beta1.TraitDefinitionKind: @@ -524,11 +613,11 @@ func (m *GoModifier) genNameTypeFunc() []*j.Statement { return nil } -func (m *GoModifier) genUnmarshalFunc() []*j.Statement { +func (m *GoDefModifier) genUnmarshalFunc() []*j.Statement { return []*j.Statement{j.Null()} } -func (m *GoModifier) genBaseSetterFunc() []*j.Statement { +func (m *GoDefModifier) genBaseSetterFunc() []*j.Statement { baseFuncArgs := map[string][]struct { funcName string argName string @@ -557,7 +646,7 @@ func (m *GoModifier) genBaseSetterFunc() []*j.Statement { }, } baseFuncs := make([]*j.Statement, 0) - for _, fn := range baseFuncArgs[m.defKind] { + for _, fn := range baseFuncArgs[m.kind] { if fn.dst == nil { fn.dst = j.Dot(fn.funcName) } @@ -580,8 +669,8 @@ func (m *GoModifier) genBaseSetterFunc() []*j.Statement { return baseFuncs } -func (m *GoModifier) genAddSubStepFunc() *j.Statement { - if m.defName != "step-group" || m.defKind != v1beta1.WorkflowStepDefinitionKind { +func (m *GoDefModifier) genAddSubStepFunc() *j.Statement { + if m.name != "step-group" || m.kind != v1beta1.WorkflowStepDefinitionKind { return j.Null() } subList := j.Id(m.defFuncReceiver).Dot("Base").Dot("SubSteps") @@ -597,7 +686,7 @@ func (m *GoModifier) genAddSubStepFunc() *j.Statement { } // exportMethods will export methods from definition spec struct to definition struct -func (m *GoModifier) exportMethods() error { +func (m *GoDefModifier) exportMethods() error { fileLoc := path.Join(m.defDir, m.nameInSnakeCase+".go") // nolint:gosec file, err := os.ReadFile(fileLoc) @@ -628,8 +717,8 @@ func (m *GoModifier) exportMethods() error { return os.WriteFile(fileLoc, []byte(fileStr), 0600) } -func (m *GoModifier) addValidateTraits() error { - if m.defKind != v1beta1.ComponentDefinitionKind { +func (m *GoDefModifier) addValidateTraits() error { + if m.kind != v1beta1.ComponentDefinitionKind { return nil } fileLoc := path.Join(m.defDir, m.nameInSnakeCase+".go") @@ -656,7 +745,7 @@ func (m *GoModifier) addValidateTraits() error { return os.WriteFile(fileLoc, []byte(fileStr), 0600) } -func (m *GoModifier) format() error { +func (m *GoModuleModifier) format() error { // check if gofmt is installed // todo (chivalryq): support go mod tidy for sub-module @@ -673,11 +762,11 @@ func (m *GoModifier) format() error { } if allFormattersInstalled { for _, fmter := range formatterPaths { - if m.verbose { + if m.Verbose { fmt.Printf("Use %s to format code\n", fmter) } // nolint:gosec - cmd := exec.Command(fmter, "-w", m.defDir) + cmd := exec.Command(fmter, "-w", m.apiDir) output, err := cmd.CombinedOutput() if err != nil { return errors.Wrap(err, string(output)) @@ -686,31 +775,51 @@ func (m *GoModifier) format() error { return nil } // fallback to use go lib - if m.verbose { + if m.Verbose { fmt.Println("At least one of linters is not installed, use go/format lib to format code") } - files, err := os.ReadDir(m.defDir) - if err != nil { - return errors.Wrap(err, "read dir") - } - for _, f := range files { - if !strings.HasSuffix(f.Name(), ".go") { - continue + + // format all .go files + return filepath.Walk(m.apiDir, func(path string, info os.FileInfo, err error) error { + if !strings.HasSuffix(path, ".go") { + return nil } - filePath := path.Join(m.defDir, f.Name()) // nolint:gosec - content, err := os.ReadFile(filePath) + content, err := os.ReadFile(path) if err != nil { - return errors.Wrapf(err, "read file %s", filePath) + return errors.Wrapf(err, "read file %s", path) } formatted, err := format.Source(content) if err != nil { - return errors.Wrapf(err, "format file %s", filePath) + return errors.Wrapf(err, "format file %s", path) } - err = os.WriteFile(filePath, formatted, 0600) + err = os.WriteFile(path, formatted, 0600) if err != nil { - return errors.Wrapf(err, "write file %s", filePath) + return errors.Wrapf(err, "write file %s", path) + } + return nil + }) +} + +// parse parses the args and set the value to GoArgs +// todo(chivalryq): generalize the args parsing +func (args *GoArgs) parse(stringArgs []string) { + for _, arg := range stringArgs { + parts := strings.Split(arg, "=") + if len(parts) != 2 { + continue + } + switch parts[0] { + case "MainModuleVersion": + args.MainModuleVersion = parts[1] + case "GoProxy": + args.GoProxy = parts[1] } } - return nil + if args.GoProxy == "" { + args.GoProxy = DefaultGoProxy + } + if args.MainModuleVersion == "" { + args.MainModuleVersion = DefaultMainModuleHash + } } diff --git a/references/cli/def.go b/references/cli/def.go index 361bebe74..f9debe0ba 100644 --- a/references/cli/def.go +++ b/references/cli/def.go @@ -1077,7 +1077,7 @@ func NewDefinitionGenAPICommand(c common.Args) *cobra.Command { "# Generate incremental definition files to existing sdk directory\n" + "> vela def gen-api --lang go -f /path/to/def -o /path/to/sdk", RunE: func(cmd *cobra.Command, args []string) error { - err := meta.Init(c) + err := meta.Init(c, cmd.Flags()) if err != nil { return err } @@ -1100,12 +1100,18 @@ func NewDefinitionGenAPICommand(c common.Args) *cobra.Command { cmd.Flags().StringVarP(&meta.Output, "output", "o", "./apis", "Output directory path") cmd.Flags().StringVar(&meta.APIDirectory, "api-dir", "", "API directory path to put definition API files, relative to output directory. Default value: go: pkg/apis") cmd.Flags().BoolVar(&meta.IsSubModule, "submodule", false, "Whether the generated code is a submodule of the project. If set, the directory specified by `api-dir` will be treated as a submodule of the project") - cmd.Flags().StringVarP(&meta.Package, "package", "p", "github.com/kubevela/vela-go-sdk", "Package name of generated code") + cmd.Flags().StringVarP(&meta.Package, "package", "p", gen_sdk.DefaultPackage, "Package name of generated code") cmd.Flags().StringVarP(&meta.Lang, "lang", "g", "go", "Language to generate code. Valid languages: go") cmd.Flags().StringVarP(&meta.Template, "template", "t", "", "Template file path, if not specified, the default template will be used") cmd.Flags().StringSliceVarP(&meta.File, "file", "f", nil, "File name of definitions, can be specified multiple times, or use comma to separate multiple files. If directory specified, all files found recursively in the directory will be used") cmd.Flags().BoolVar(&meta.InitSDK, "init", false, "Init the whole SDK project, if not set, only the API file will be generated") cmd.Flags().BoolVarP(&meta.Verbose, "verbose", "v", false, "Print verbose logs") + cmd.Flags().StringSlice("go-args", []string{}, + fmt.Sprintf("Additional arguments to pass to the go generator, available options: "+ + "MainModuleVersion(specify the version of the main module in submodule, used when set --submodule, default: cd431bb25a9a), "+ + "GoProxy(specify the GOPROXY environment variable, default: https://goproxy.cn,direct)", + ), + ) return cmd }