mirror of
https://github.com/kubevela/kubevela.git
synced 2026-05-06 01:17:09 +00:00
Feat: add vela def gen-cue command (#5956)
* Feat: add vela def gen-cue command Signed-off-by: iyear <ljyngup@gmail.com> * Fix: golangci-lint G304 error Signed-off-by: iyear <ljyngup@gmail.com> * Chore: remove useless stat Signed-off-by: iyear <ljyngup@gmail.com> * Chore: remove useless log Signed-off-by: iyear <ljyngup@gmail.com> * Chore: type alias Signed-off-by: iyear <ljyngup@gmail.com> --------- Signed-off-by: iyear <ljyngup@gmail.com>
This commit is contained in:
2
go.mod
2
go.mod
@@ -85,6 +85,7 @@ require (
|
||||
github.com/wonderflow/cert-manager-api v1.0.4-0.20210304051430-e08aa76f6c5f
|
||||
github.com/xanzy/go-gitlab v0.83.0
|
||||
github.com/xlab/treeprint v1.2.0
|
||||
go.uber.org/multierr v1.7.0
|
||||
go.uber.org/zap v1.24.0 // indirect
|
||||
golang.org/x/crypto v0.6.0
|
||||
golang.org/x/oauth2 v0.6.0
|
||||
@@ -287,7 +288,6 @@ require (
|
||||
go.opentelemetry.io/proto/otlp v0.19.0 // indirect
|
||||
go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.7.0 // indirect
|
||||
golang.org/x/mod v0.9.0 // indirect
|
||||
golang.org/x/net v0.8.0 // indirect
|
||||
golang.org/x/sync v0.1.0 // indirect
|
||||
|
||||
@@ -38,6 +38,7 @@ import (
|
||||
crossplane "github.com/oam-dev/terraform-controller/api/types/crossplane-runtime"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"go.uber.org/multierr"
|
||||
"gopkg.in/yaml.v3"
|
||||
errors2 "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -57,6 +58,8 @@ import (
|
||||
"github.com/oam-dev/kubevela/pkg/utils/common"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/filters"
|
||||
"github.com/oam-dev/kubevela/pkg/utils/util"
|
||||
"github.com/oam-dev/kubevela/references/cuegen"
|
||||
providergen "github.com/oam-dev/kubevela/references/cuegen/generators/provider"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -89,6 +92,7 @@ func DefinitionCommandGroup(c common.Args, order string, ioStreams util.IOStream
|
||||
NewDefinitionGenDocCommand(c, ioStreams),
|
||||
NewCapabilityShowCommand(c, "", ioStreams),
|
||||
NewDefinitionGenAPICommand(c),
|
||||
NewDefinitionGenCUECommand(c),
|
||||
)
|
||||
return cmd
|
||||
}
|
||||
@@ -1138,3 +1142,72 @@ func NewDefinitionGenAPICommand(c common.Args) *cobra.Command {
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// NewDefinitionGenCUECommand create the `vela def gen-cue` command to help user generate CUE schema from the go code
|
||||
func NewDefinitionGenCUECommand(_ common.Args) *cobra.Command {
|
||||
const (
|
||||
typeProvider = "provider"
|
||||
)
|
||||
|
||||
var (
|
||||
typ string
|
||||
output string
|
||||
typeMap map[string]string
|
||||
nullable bool
|
||||
)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "gen-cue [flags] SOURCE.go",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Short: "Generate CUE schema from Go code.",
|
||||
Long: "Generate CUE schema from Go code.\n" +
|
||||
"* This command provide a way to generate CUE schema from Go code,\n" +
|
||||
"* Which can be used to keep consistency between Go code and CUE schema automatically.\n",
|
||||
Example: "# Generate CUE schema for provider type\n" +
|
||||
"> vela def gen-cue -t provider /path/to/myprovider.go\n" +
|
||||
"# Generate CUE schema for provider type with custom types\n" +
|
||||
"> vela def gen-cue -t provider --types *k8s.io/apimachinery/pkg/apis/meta/v1/unstructured.Unstructured=ellipsis /path/to/myprovider.go\n" +
|
||||
"# Generate CUE schema for provider type with custom output path\n" +
|
||||
"> vela def gen-cue -t provider -o /path/to/myprovider.cue /path/to/myprovider.go\n",
|
||||
RunE: func(cmd *cobra.Command, args []string) (rerr error) {
|
||||
// convert map[string]string to map[string]cuegen.Type
|
||||
newTypeMap := make(map[string]cuegen.Type, len(typeMap))
|
||||
for k, v := range typeMap {
|
||||
newTypeMap[k] = cuegen.Type(v)
|
||||
}
|
||||
|
||||
file := args[0]
|
||||
if !strings.HasSuffix(file, ".go") {
|
||||
return fmt.Errorf("invalid file %s, must be a go file", file)
|
||||
}
|
||||
|
||||
if output == "" {
|
||||
output = strings.TrimSuffix(file, filepath.Ext(file)) + ".cue"
|
||||
}
|
||||
f, err := os.Create(filepath.Clean(output))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer multierr.AppendInvoke(&rerr, multierr.Close(f))
|
||||
|
||||
switch typ {
|
||||
case typeProvider:
|
||||
return providergen.Generate(providergen.Options{
|
||||
File: file,
|
||||
Writer: f,
|
||||
Types: newTypeMap,
|
||||
Nullable: nullable,
|
||||
})
|
||||
default:
|
||||
return fmt.Errorf("invalid type %s", typ)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringVarP(&typ, "type", "t", "", "Type of the definition to generate. Valid types: [provider]")
|
||||
cmd.Flags().BoolVar(&nullable, "nullable", false, "Whether to generate null enum for pointer type")
|
||||
cmd.Flags().StringVarP(&output, "output", "o", "", "Output CUE file path, if not specified, the CUE file will be generated in the same directory")
|
||||
cmd.Flags().StringToStringVar(&typeMap, "types", map[string]string{}, "Special types to generate, format: <package+struct>=[any|ellipsis]. e.g. --types=*k8s.io/apimachinery/pkg/apis/meta/v1/unstructured.Unstructured=ellipsis")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -179,6 +179,22 @@ func removeDir(dirname string, t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func compareFile(t *testing.T, got, expected string) {
|
||||
gotBytes, err := os.ReadFile(got)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read file %s: %v", got, err)
|
||||
}
|
||||
|
||||
expectedBytes, err := os.ReadFile(expected)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read file %s: %v", expected, err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(gotBytes, expectedBytes) {
|
||||
t.Errorf("got %s, expected %s", gotBytes, expectedBytes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewDefinitionCommandGroup(t *testing.T) {
|
||||
cmd := DefinitionCommandGroup(common2.Args{}, "", util.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr})
|
||||
initCommand(cmd)
|
||||
@@ -655,3 +671,28 @@ func TestNewDefinitionGenAPICommand(t *testing.T) {
|
||||
t.Fatalf("unexpeced error when executing genapi command: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewDefinitionGenCUECommand(t *testing.T) {
|
||||
c := initArgs()
|
||||
cmd := NewDefinitionGenCUECommand(c)
|
||||
initCommand(cmd)
|
||||
|
||||
// re-use the provider testdata
|
||||
providerPath := "../cuegen/generators/provider/testdata"
|
||||
output := filepath.Join(t.TempDir(), "output.cue")
|
||||
expected := filepath.Join(providerPath, "valid.cue")
|
||||
|
||||
cmd.SetArgs([]string{
|
||||
"-t", "provider",
|
||||
"-o", output,
|
||||
"--types", "*k8s.io/apimachinery/pkg/apis/meta/v1/unstructured.Unstructured=ellipsis",
|
||||
"--types", "*k8s.io/apimachinery/pkg/apis/meta/v1/unstructured.UnstructuredList=ellipsis",
|
||||
filepath.Join(providerPath, "valid.go"),
|
||||
})
|
||||
|
||||
if err := cmd.Execute(); err != nil {
|
||||
t.Fatalf("unexpeced error when executing gencue command: %v", err)
|
||||
}
|
||||
|
||||
compareFile(t, output, expected)
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ func (g *Generator) convert(typ gotypes.Type) (cueast.Expr, error) {
|
||||
case TypeEllipsis:
|
||||
return &cueast.StructLit{Elts: []cueast.Decl{&cueast.Ellipsis{}}}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported special cue type %d", t)
|
||||
return nil, fmt.Errorf("unsupported special cue type: %v", t)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,13 +19,13 @@ package cuegen
|
||||
import goast "go/ast"
|
||||
|
||||
// Type is a special cue type
|
||||
type Type int
|
||||
type Type string
|
||||
|
||||
const (
|
||||
// TypeAny converts go type to _(top value) in cue
|
||||
TypeAny Type = iota
|
||||
TypeAny Type = "any"
|
||||
// TypeEllipsis converts go type to {...} in cue
|
||||
TypeEllipsis
|
||||
TypeEllipsis Type = "ellipsis"
|
||||
)
|
||||
|
||||
type options struct {
|
||||
|
||||
Reference in New Issue
Block a user