From fc72750ebbe343e4a13aeefe1289a0eb271e482e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Tue, 12 Sep 2023 00:33:20 +0200 Subject: [PATCH] Replace "sandbox" main files by a unified CLI --- {sandbox => cmd}/builder/main.go | 0 cmd/cli/generate/command.go | 117 ++++++++++++++++++ .../main.go => cmd/cli/generate/cue.go | 97 ++++----------- cmd/cli/generate/jsonschema.go | 32 +++++ cmd/cli/generate/kindsyscore.go | 61 +++++++++ cmd/cli/generate/kindsyscustom.go | 61 +++++++++ cmd/cli/main.go | 22 ++++ go.mod | 3 + go.sum | 9 ++ internal/jennies/all.go | 10 +- sandbox/codegen-jsonschema/main.go | 72 ----------- sandbox/codegen-kindsys-custom/main.go | 117 ------------------ sandbox/codegen-kindsys/main.go | 117 ------------------ schemas/kindsys/core/dashboard/dashboard.cue | 18 +-- 14 files changed, 345 insertions(+), 391 deletions(-) rename {sandbox => cmd}/builder/main.go (100%) create mode 100644 cmd/cli/generate/command.go rename sandbox/codegen-cue/main.go => cmd/cli/generate/cue.go (57%) create mode 100644 cmd/cli/generate/jsonschema.go create mode 100644 cmd/cli/generate/kindsyscore.go create mode 100644 cmd/cli/generate/kindsyscustom.go create mode 100644 cmd/cli/main.go delete mode 100644 sandbox/codegen-jsonschema/main.go delete mode 100644 sandbox/codegen-kindsys-custom/main.go delete mode 100644 sandbox/codegen-kindsys/main.go diff --git a/sandbox/builder/main.go b/cmd/builder/main.go similarity index 100% rename from sandbox/builder/main.go rename to cmd/builder/main.go diff --git a/cmd/cli/generate/command.go b/cmd/cli/generate/command.go new file mode 100644 index 000000000..9d1f6c721 --- /dev/null +++ b/cmd/cli/generate/command.go @@ -0,0 +1,117 @@ +package generate + +import ( + "context" + "fmt" + "strings" + + "github.com/grafana/codejen" + "github.com/grafana/cog/internal/ast" + "github.com/grafana/cog/internal/jennies" + "github.com/spf13/cobra" +) + +type options struct { + outputDir string + entrypoints []string + schemasType string + + // Cue-specific options + cueImports []string +} + +func (opts options) cueIncludeImports() ([]cueIncludeImport, error) { + if len(opts.cueImports) == 0 { + return nil, nil + } + + imports := make([]cueIncludeImport, len(opts.cueImports)) + for i, importDefinition := range opts.cueImports { + parts := strings.Split(importDefinition, ":") + if len(parts) != 2 { + return nil, fmt.Errorf("'%s' is not a valid import definition", importDefinition) + } + + imports[i].fsPath = parts[0] + imports[i].importPath = parts[1] + } + + return imports, nil +} + +func Command() *cobra.Command { + opts := options{} + + cmd := &cobra.Command{ + Use: "generate", + Short: "Generates code from schemas.", // TODO: better descriptions + Long: `Generates code from schemas.`, + RunE: func(cmd *cobra.Command, args []string) error { + return doGenerate(opts) + }, + } + + cmd.Flags().StringVarP(&opts.schemasType, "loader", "l", "cue", "Schemas type.") // TODO: better usage text + cmd.Flags().StringVarP(&opts.outputDir, "output", "o", "generated", "Output directory.") // TODO: better usage text + cmd.Flags().StringArrayVarP(&opts.entrypoints, "input", "i", nil, "Schema.") // TODO: better usage text + cmd.Flags().StringArrayVarP(&opts.cueImports, "include-cue-import", "I", nil, "Specify an additional library import directory. Format: [path]:[import]. Example: '../grafana/common-library:github.com/grafana/grafana/packages/grafana-schema/src/common") + + _ = cmd.MarkFlagRequired("input") + _ = cmd.MarkFlagDirname("input") + _ = cmd.MarkFlagDirname("output") + + return cmd +} + +func doGenerate(opts options) error { + loaders := map[string]func(opts options) ([]*ast.File, error){ + "cue": cueLoader, + "kindsys-core": kindsysCoreLoader, + "kindsys-custom": kindsysCustomLoader, + "jsonschema": jsonschemaLoader, + } + loader, ok := loaders[opts.schemasType] + if !ok { + return fmt.Errorf("no loader found for '%s'", opts.schemasType) + } + + schemas, err := loader(opts) + if err != nil { + return err + } + + // Here begins the code generation setup + targetsByLanguage := jennies.All() + rootCodeJenFS := codejen.NewFS() + + for language, target := range targetsByLanguage { + fmt.Printf("Running '%s' jennies...\n", language) + + var err error + processedAsts := schemas + + for _, compilerPass := range target.CompilerPasses { + processedAsts, err = compilerPass.Process(processedAsts) + if err != nil { + return err + } + } + + fs, err := target.Jennies.GenerateFS(processedAsts) + if err != nil { + return err + } + + err = rootCodeJenFS.Merge(fs) + if err != nil { + return err + } + } + + err = rootCodeJenFS.Write(context.Background(), opts.outputDir) + if err != nil { + return err + } + + return nil +} diff --git a/sandbox/codegen-cue/main.go b/cmd/cli/generate/cue.go similarity index 57% rename from sandbox/codegen-cue/main.go rename to cmd/cli/generate/cue.go index 43d627a4a..251e47fd3 100644 --- a/sandbox/codegen-cue/main.go +++ b/cmd/cli/generate/cue.go @@ -1,7 +1,6 @@ -package main +package generate import ( - "context" "fmt" "io" "io/fs" @@ -11,30 +10,24 @@ import ( "cuelang.org/go/cue/cuecontext" "cuelang.org/go/cue/load" - "github.com/grafana/codejen" "github.com/grafana/cog/internal/ast" - "github.com/grafana/cog/internal/jennies" "github.com/grafana/cog/internal/simplecue" "github.com/yalue/merged_fs" ) -func main() { - entrypoints := []string{ - "./schemas/cue/core/dashboard/", - // "./schemas/cue/core/playlist/", - - "./schemas/cue/composable/timeseries/", - - "github.com/grafana/grafana/packages/grafana-schema/src/common", - } +type cueIncludeImport struct { + fsPath string // path of the library on the filesystem + importPath string // path used in CUE files to import that library +} - cueFsOverlay, err := buildCueOverlay() +func cueLoader(opts options) ([]*ast.File, error) { + cueFsOverlay, err := buildCueOverlay(opts) if err != nil { - panic(err) + return nil, err } - allSchemas := make([]*ast.File, 0, len(entrypoints)) - for _, entrypoint := range entrypoints { + allSchemas := make([]*ast.File, 0, len(opts.entrypoints)) + for _, entrypoint := range opts.entrypoints { pkg := filepath.Base(entrypoint) // Load Cue files into Cue build.Instances slice @@ -47,90 +40,52 @@ func main() { values, err := cuecontext.New().BuildInstances(bis) if err != nil { - panic(err) + return nil, err } schemaAst, err := simplecue.GenerateAST(values[0], simplecue.Config{ Package: pkg, // TODO: extract from input schema/? }) if err != nil { - panic(err) + return nil, err } allSchemas = append(allSchemas, schemaAst) } - // Here begins the code generation setup - targetsByLanguage := jennies.All() - rootCodeJenFS := codejen.NewFS() - - for language, target := range targetsByLanguage { - fmt.Printf("Running '%s' jennies...\n", language) - - var err error - processedAsts := allSchemas - - for _, compilerPass := range target.CompilerPasses { - processedAsts, err = compilerPass.Process(processedAsts) - if err != nil { - panic(err) - } - } - - fs, err := target.Jennies.GenerateFS(processedAsts) - if err != nil { - panic(err) - } - - err = rootCodeJenFS.Merge(fs) - if err != nil { - panic(err) - } - } - - err = rootCodeJenFS.Write(context.Background(), "generated") - if err != nil { - panic(err) - } + return allSchemas, nil } -func buildCueOverlay() (map[string]load.Source, error) { - libFs, err := buildBaseFSWithLibraries() +func buildCueOverlay(opts options) (map[string]load.Source, error) { + libFs, err := buildBaseFSWithLibraries(opts) if err != nil { return nil, err } overlay := make(map[string]load.Source) - if err := ToCueOverlay("/", libFs, overlay); err != nil { + if err := toCueOverlay("/", libFs, overlay); err != nil { return nil, err } return overlay, nil } -func buildBaseFSWithLibraries() (fs.FS, error) { - // TODO: these should be received as inputs/arguments/parameters - importDefinitions := [][2]string{ - { - "github.com/grafana/grafana/packages/grafana-schema/src/common", - "../kind-registry/grafana/next/common", - }, - { - "github.com/grafana/cog", - ".", - }, +func buildBaseFSWithLibraries(opts options) (fs.FS, error) { + importDefinitions, err := opts.cueIncludeImports() + if err != nil { + return nil, err } var librariesFS []fs.FS for _, importDefinition := range importDefinitions { - absPath, err := filepath.Abs(importDefinition[1]) + absPath, err := filepath.Abs(importDefinition.fsPath) if err != nil { return nil, err } - fmt.Printf("Loading '%s' module from '%s'\n", importDefinition[0], absPath) + fmt.Printf("Loading '%s' module from '%s'\n", importDefinition.importPath, absPath) - libraryFS, err := dirToPrefixedFS(absPath, "cue.mod/pkg/"+importDefinition[0]) + libraryFS, err := dirToPrefixedFS(absPath, "cue.mod/pkg/"+importDefinition.importPath) if err != nil { return nil, err } @@ -164,8 +119,8 @@ func dirToPrefixedFS(directory string, prefix string) (fs.FS, error) { return commonFS, nil } -// ToOverlay converts an fs.FS into a CUE loader overlay. -func ToCueOverlay(prefix string, vfs fs.FS, overlay map[string]load.Source) error { +// ToOverlay converts a fs.FS into a CUE loader overlay. +func toCueOverlay(prefix string, vfs fs.FS, overlay map[string]load.Source) error { // TODO why not just stick the prefix on automatically...? if !filepath.IsAbs(prefix) { return fmt.Errorf("must provide absolute path prefix when generating cue overlay, got %q", prefix) @@ -183,7 +138,7 @@ func ToCueOverlay(prefix string, vfs fs.FS, overlay map[string]load.Source) erro if err != nil { return err } - defer f.Close() // nolint: errcheck + defer func() { _ = f.Close() }() b, err := io.ReadAll(f) if err != nil { diff --git a/cmd/cli/generate/jsonschema.go b/cmd/cli/generate/jsonschema.go new file mode 100644 index 000000000..20e894def --- /dev/null +++ b/cmd/cli/generate/jsonschema.go @@ -0,0 +1,32 @@ +package generate + +import ( + "os" + "path/filepath" + + "github.com/grafana/cog/internal/ast" + "github.com/grafana/cog/internal/jsonschema" +) + +func jsonschemaLoader(opts options) ([]*ast.File, error) { + allSchemas := make([]*ast.File, 0, len(opts.entrypoints)) + for _, entrypoint := range opts.entrypoints { + pkg := filepath.Base(filepath.Dir(entrypoint)) + + reader, err := os.Open(entrypoint) + if err != nil { + return nil, err + } + + schemaAst, err := jsonschema.GenerateAST(reader, jsonschema.Config{ + Package: pkg, // TODO: extract from input schema/folder? + }) + if err != nil { + return nil, err + } + + allSchemas = append(allSchemas, schemaAst) + } + + return allSchemas, nil +} diff --git a/cmd/cli/generate/kindsyscore.go b/cmd/cli/generate/kindsyscore.go new file mode 100644 index 000000000..eb2cf2a99 --- /dev/null +++ b/cmd/cli/generate/kindsyscore.go @@ -0,0 +1,61 @@ +package generate + +import ( + "fmt" + "path/filepath" + + "cuelang.org/go/cue" + "cuelang.org/go/cue/cuecontext" + "github.com/grafana/cog/internal/ast" + "github.com/grafana/cog/internal/simplecue" + "github.com/grafana/kindsys" + "github.com/grafana/thema" +) + +func kindsysCoreLoader(opts options) ([]*ast.File, error) { + themaRuntime := thema.NewRuntime(cuecontext.New()) + + allSchemas := make([]*ast.File, 0, len(opts.entrypoints)) + for _, entrypoint := range opts.entrypoints { + pkg := filepath.Base(entrypoint) + + overlayFS, err := dirToPrefixedFS(entrypoint, "") + if err != nil { + return nil, err + } + + cueInstance, err := kindsys.BuildInstance(themaRuntime.Context(), ".", "kind", overlayFS) + if err != nil { + return nil, fmt.Errorf("could not load kindsys instance: %w", err) + } + + props, err := kindsys.ToKindProps[kindsys.CoreProperties](cueInstance) + if err != nil { + return nil, fmt.Errorf("could not convert cue value to kindsys props: %w", err) + } + + kindDefinition := kindsys.Def[kindsys.CoreProperties]{ + V: cueInstance, + Properties: props, + } + + boundKind, err := kindsys.BindCore(themaRuntime, kindDefinition) + if err != nil { + return nil, fmt.Errorf("could not bind kind definition to kind: %w", err) + } + + rawLatestSchemaAsCue := boundKind.Lineage().Latest().Underlying() + latestSchemaAsCue := rawLatestSchemaAsCue.LookupPath(cue.MakePath(cue.Hid("_#schema", "github.com/grafana/thema"))) + + schemaAst, err := simplecue.GenerateAST(latestSchemaAsCue, simplecue.Config{ + Package: pkg, // TODO: extract from input schema/folder? + }) + if err != nil { + return nil, err + } + + allSchemas = append(allSchemas, schemaAst) + } + + return allSchemas, nil +} diff --git a/cmd/cli/generate/kindsyscustom.go b/cmd/cli/generate/kindsyscustom.go new file mode 100644 index 000000000..987dcac10 --- /dev/null +++ b/cmd/cli/generate/kindsyscustom.go @@ -0,0 +1,61 @@ +package generate + +import ( + "fmt" + "path/filepath" + + "cuelang.org/go/cue" + "cuelang.org/go/cue/cuecontext" + "github.com/grafana/cog/internal/ast" + "github.com/grafana/cog/internal/simplecue" + "github.com/grafana/kindsys" + "github.com/grafana/thema" +) + +func kindsysCustomLoader(opts options) ([]*ast.File, error) { + themaRuntime := thema.NewRuntime(cuecontext.New()) + + allSchemas := make([]*ast.File, 0, len(opts.entrypoints)) + for _, entrypoint := range opts.entrypoints { + pkg := filepath.Base(entrypoint) + + overlayFS, err := dirToPrefixedFS(entrypoint, "") + if err != nil { + return nil, err + } + + cueInstance, err := kindsys.BuildInstance(themaRuntime.Context(), ".", pkg, overlayFS) + if err != nil { + return nil, fmt.Errorf("could not load kindsys instance: %w", err) + } + + props, err := kindsys.ToKindProps[kindsys.CustomProperties](cueInstance) + if err != nil { + return nil, fmt.Errorf("could not convert cue value to kindsys props: %w", err) + } + + kindDefinition := kindsys.Def[kindsys.CustomProperties]{ + V: cueInstance, + Properties: props, + } + + boundKind, err := kindsys.BindCustom(themaRuntime, kindDefinition) + if err != nil { + return nil, fmt.Errorf("could not bind kind definition to kind: %w", err) + } + + rawLatestSchemaAsCue := boundKind.Lineage().Latest().Underlying() + latestSchemaAsCue := rawLatestSchemaAsCue.LookupPath(cue.MakePath(cue.Hid("_#schema", "github.com/grafana/thema"))) + + schemaAst, err := simplecue.GenerateAST(latestSchemaAsCue, simplecue.Config{ + Package: pkg, // TODO: extract from input schema/folder? + }) + if err != nil { + return nil, err + } + + allSchemas = append(allSchemas, schemaAst) + } + + return allSchemas, nil +} diff --git a/cmd/cli/main.go b/cmd/cli/main.go new file mode 100644 index 000000000..3c64cfe05 --- /dev/null +++ b/cmd/cli/main.go @@ -0,0 +1,22 @@ +package main + +import ( + "os" + + "github.com/grafana/cog/cmd/cli/generate" + "github.com/spf13/cobra" +) + +func main() { + rootCmd := &cobra.Command{ + Use: "cog ", + Short: "A tool for working with Grafana objects from code", + SilenceUsage: true, + } + + rootCmd.AddCommand(generate.Command()) + + if err := rootCmd.Execute(); err != nil { + os.Exit(1) + } +} diff --git a/go.mod b/go.mod index eb888ed13..8a0bfe129 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/grafana/kindsys v0.0.0-20230615185749-1424263c17c7 github.com/grafana/thema v0.0.0-20230628103417-8f63313207a5 github.com/santhosh-tekuri/jsonschema v1.2.4 + github.com/spf13/cobra v1.4.0 github.com/stretchr/testify v1.8.2 github.com/yalue/merged_fs v1.2.2 golang.org/x/text v0.10.0 @@ -27,6 +28,7 @@ require ( github.com/google/uuid v1.3.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/lib/pq v1.10.6 // indirect @@ -36,6 +38,7 @@ require ( github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/protocolbuffers/txtpbfmt v0.0.0-20230412060525-fa9f017c0ded // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect golang.org/x/mod v0.11.0 // indirect golang.org/x/net v0.11.0 // indirect golang.org/x/sync v0.3.0 // indirect diff --git a/go.sum b/go.sum index ecddf591d..3ddecc562 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,7 @@ github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZe github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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= @@ -33,6 +34,8 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -61,10 +64,15 @@ github.com/protocolbuffers/txtpbfmt v0.0.0-20230412060525-fa9f017c0ded/go.mod h1 github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/santhosh-tekuri/jsonschema v1.2.4 h1:hNhW8e7t+H1vgY+1QeEQpveR6D4+OwKPXCfD2aieJis= github.com/santhosh-tekuri/jsonschema v1.2.4/go.mod h1:TEAUOeZSmIxTTuHatJzrvARHiuO9LYd+cIxzgEHCQI4= github.com/sdboyer/cue v0.5.0-beta.2.0.20221218111347-341999f48bdb h1:X6XJsprVDQnlG4vT5TVb+cRlGMU78L/IKej8Q6SDFGY= github.com/sdboyer/cue v0.5.0-beta.2.0.20221218111347-341999f48bdb/go.mod h1:okjJBHFQFer+a41sAe2SaGm1glWS8oEb6CmJvn5Zdws= +github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q= +github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= +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/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -116,6 +124,7 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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/jennies/all.go b/internal/jennies/all.go index b9606e246..24d03805a 100644 --- a/internal/jennies/all.go +++ b/internal/jennies/all.go @@ -3,14 +3,14 @@ package jennies import ( "github.com/grafana/codejen" "github.com/grafana/cog/internal/ast" - compiler2 "github.com/grafana/cog/internal/ast/compiler" + "github.com/grafana/cog/internal/ast/compiler" "github.com/grafana/cog/internal/jennies/golang" "github.com/grafana/cog/internal/jennies/typescript" ) type LanguageTarget struct { Jennies *codejen.JennyList[[]*ast.File] - CompilerPasses []compiler2.Pass + CompilerPasses []compiler.Pass } func All() map[string]LanguageTarget { @@ -18,9 +18,9 @@ func All() map[string]LanguageTarget { // Compiler passes should not have side effects, but they do. "go": { Jennies: golang.Jennies(), - CompilerPasses: []compiler2.Pass{ - &compiler2.AnonymousEnumToExplicitType{}, - &compiler2.DisjunctionToType{}, + CompilerPasses: []compiler.Pass{ + &compiler.AnonymousEnumToExplicitType{}, + &compiler.DisjunctionToType{}, }, }, "typescript": { diff --git a/sandbox/codegen-jsonschema/main.go b/sandbox/codegen-jsonschema/main.go deleted file mode 100644 index 5684b75f4..000000000 --- a/sandbox/codegen-jsonschema/main.go +++ /dev/null @@ -1,72 +0,0 @@ -package main - -import ( - "context" - "fmt" - "os" - "path/filepath" - - "github.com/grafana/codejen" - "github.com/grafana/cog/internal/ast" - "github.com/grafana/cog/internal/jennies" - "github.com/grafana/cog/internal/jsonschema" -) - -func main() { - entrypoints := []string{ - "./schemas/jsonschema/core/playlist/playlist.json", - "./schemas/jsonschema/core/dockerd/dockerd.json", - } - - allSchemas := make([]*ast.File, 0, len(entrypoints)) - for _, entrypoint := range entrypoints { - pkg := filepath.Base(filepath.Dir(entrypoint)) - - reader, err := os.Open(entrypoint) - if err != nil { - panic(err) - } - - schemaAst, err := jsonschema.GenerateAST(reader, jsonschema.Config{ - Package: pkg, // TODO: extract from input schema/folder? - }) - if err != nil { - panic(err) - } - - allSchemas = append(allSchemas, schemaAst) - } - - // Here begins the code generation setup - targetsByLanguage := jennies.All() - rootCodeJenFS := codejen.NewFS() - - for language, target := range targetsByLanguage { - fmt.Printf("Running '%s' jennies...\n", language) - - var err error - processedAsts := allSchemas - - for _, compilerPass := range target.CompilerPasses { - processedAsts, err = compilerPass.Process(processedAsts) - if err != nil { - panic(err) - } - } - - fs, err := target.Jennies.GenerateFS(processedAsts) - if err != nil { - panic(err) - } - - err = rootCodeJenFS.Merge(fs) - if err != nil { - panic(err) - } - } - - err := rootCodeJenFS.Write(context.Background(), "generated") - if err != nil { - panic(err) - } -} diff --git a/sandbox/codegen-kindsys-custom/main.go b/sandbox/codegen-kindsys-custom/main.go deleted file mode 100644 index ec2541201..000000000 --- a/sandbox/codegen-kindsys-custom/main.go +++ /dev/null @@ -1,117 +0,0 @@ -package main - -import ( - "context" - "fmt" - "io/fs" - "os" - "path/filepath" - "testing/fstest" - - "cuelang.org/go/cue" - "cuelang.org/go/cue/cuecontext" - "github.com/grafana/codejen" - "github.com/grafana/cog/internal/ast" - "github.com/grafana/cog/internal/jennies" - "github.com/grafana/cog/internal/simplecue" - "github.com/grafana/kindsys" - "github.com/grafana/thema" -) - -func main() { - themaRuntime := thema.NewRuntime(cuecontext.New()) - - entrypoints := []string{"./schemas/kindsys/custom/slo"} - pkg := "slo" - - overlayFS, err := dirToPrefixedFS(entrypoints[0], "") - if err != nil { - panic(err) - } - - cueInstance, err := kindsys.BuildInstance(themaRuntime.Context(), ".", pkg, overlayFS) - if err != nil { - panic(fmt.Errorf("could not load kindsys instance: %w", err)) - } - - props, err := kindsys.ToKindProps[kindsys.CustomProperties](cueInstance) - if err != nil { - panic(fmt.Errorf("could not convert cue value to kindsys props: %w", err)) - } - - kindDefinition := kindsys.Def[kindsys.CustomProperties]{ - V: cueInstance, - Properties: props, - } - - boundKind, err := kindsys.BindCustom(themaRuntime, kindDefinition) - if err != nil { - panic(fmt.Errorf("could not bind kind definition to kind: %w", err)) - } - - rawLatestSchemaAsCue := boundKind.Lineage().Latest().Underlying() - latestSchemaAsCue := rawLatestSchemaAsCue.LookupPath(cue.MakePath(cue.Hid("_#schema", "github.com/grafana/thema"))) - - schemaAst, err := simplecue.GenerateAST(latestSchemaAsCue, simplecue.Config{ - Package: pkg, // TODO: extract from input schema/folder? - }) - if err != nil { - panic(err) - } - - // Here begins the code generation setup - targetsByLanguage := jennies.All() - rootCodeJenFS := codejen.NewFS() - - for language, target := range targetsByLanguage { - fmt.Printf("Running '%s' jennies...\n", language) - - var err error - processedAst := []*ast.File{schemaAst} - - for _, compilerPass := range target.CompilerPasses { - processedAst, err = compilerPass.Process(processedAst) - if err != nil { - panic(err) - } - } - - targetFs, err := target.Jennies.GenerateFS(processedAst) - if err != nil { - panic(err) - } - - err = rootCodeJenFS.Merge(targetFs) - if err != nil { - panic(err) - } - } - - err = rootCodeJenFS.Write(context.Background(), "generated") - if err != nil { - panic(err) - } -} - -func dirToPrefixedFS(directory string, prefix string) (fs.FS, error) { - dirHandle, err := os.ReadDir(directory) - if err != nil { - return nil, err - } - - commonFS := fstest.MapFS{} - for _, file := range dirHandle { - if file.IsDir() { - continue - } - - content, err := os.ReadFile(filepath.Join(directory, file.Name())) - if err != nil { - return nil, err - } - - commonFS[filepath.Join(prefix, file.Name())] = &fstest.MapFile{Data: content} - } - - return commonFS, nil -} diff --git a/sandbox/codegen-kindsys/main.go b/sandbox/codegen-kindsys/main.go deleted file mode 100644 index b1e45d5fc..000000000 --- a/sandbox/codegen-kindsys/main.go +++ /dev/null @@ -1,117 +0,0 @@ -package main - -import ( - "context" - "fmt" - "io/fs" - "os" - "path/filepath" - "testing/fstest" - - "cuelang.org/go/cue" - "cuelang.org/go/cue/cuecontext" - "github.com/grafana/codejen" - "github.com/grafana/cog/internal/ast" - "github.com/grafana/cog/internal/jennies" - "github.com/grafana/cog/internal/simplecue" - "github.com/grafana/kindsys" - "github.com/grafana/thema" -) - -func main() { - themaRuntime := thema.NewRuntime(cuecontext.New()) - - entrypoints := []string{"./schemas/kindsys/core/dashboard"} - pkg := "dashboard" - - overlayFS, err := dirToPrefixedFS(entrypoints[0], "") - if err != nil { - panic(err) - } - - cueInstance, err := kindsys.BuildInstance(themaRuntime.Context(), ".", "kind", overlayFS) - if err != nil { - panic(fmt.Errorf("could not load kindsys instance: %w", err)) - } - - props, err := kindsys.ToKindProps[kindsys.CoreProperties](cueInstance) - if err != nil { - panic(fmt.Errorf("could not convert cue value to kindsys props: %w", err)) - } - - kindDefinition := kindsys.Def[kindsys.CoreProperties]{ - V: cueInstance, - Properties: props, - } - - boundKind, err := kindsys.BindCore(themaRuntime, kindDefinition) - if err != nil { - panic(fmt.Errorf("could not bind kind definition to kind: %w", err)) - } - - rawLatestSchemaAsCue := boundKind.Lineage().Latest().Underlying() - latestSchemaAsCue := rawLatestSchemaAsCue.LookupPath(cue.MakePath(cue.Hid("_#schema", "github.com/grafana/thema"))) - - schemaAst, err := simplecue.GenerateAST(latestSchemaAsCue, simplecue.Config{ - Package: pkg, // TODO: extract from input schema/folder? - }) - if err != nil { - panic(err) - } - - // Here begins the code generation setup - targetsByLanguage := jennies.All() - rootCodeJenFS := codejen.NewFS() - - for language, target := range targetsByLanguage { - fmt.Printf("Running '%s' jennies...\n", language) - - var err error - processedAst := []*ast.File{schemaAst} - - for _, compilerPass := range target.CompilerPasses { - processedAst, err = compilerPass.Process(processedAst) - if err != nil { - panic(err) - } - } - - targetFs, err := target.Jennies.GenerateFS(processedAst) - if err != nil { - panic(err) - } - - err = rootCodeJenFS.Merge(targetFs) - if err != nil { - panic(err) - } - } - - err = rootCodeJenFS.Write(context.Background(), "generated") - if err != nil { - panic(err) - } -} - -func dirToPrefixedFS(directory string, prefix string) (fs.FS, error) { - dirHandle, err := os.ReadDir(directory) - if err != nil { - return nil, err - } - - commonFS := fstest.MapFS{} - for _, file := range dirHandle { - if file.IsDir() { - continue - } - - content, err := os.ReadFile(filepath.Join(directory, file.Name())) - if err != nil { - return nil, err - } - - commonFS[filepath.Join(prefix, file.Name())] = &fstest.MapFile{Data: content} - } - - return commonFS, nil -} diff --git a/schemas/kindsys/core/dashboard/dashboard.cue b/schemas/kindsys/core/dashboard/dashboard.cue index 055811074..f9235fde0 100644 --- a/schemas/kindsys/core/dashboard/dashboard.cue +++ b/schemas/kindsys/core/dashboard/dashboard.cue @@ -231,11 +231,11 @@ lineage: { // `0`: Never refresh the variable // `1`: Queries the data source every time the dashboard loads. // `2`: Queries the data source when the dashboard time range changes. - #VariableRefresh: 0 | 1 | 2 @grabana(kind="enum",memberNames="never|onDashboardLoad|onTimeRangeChanged") + #VariableRefresh: 0 | 1 | 2 @cog(kind="enum",memberNames="never|onDashboardLoad|onTimeRangeChanged") // Determine if the variable shows on dashboard // Accepted values are 0 (show label and value), 1 (show value only), 2 (show nothing). - #VariableHide: 0 | 1 | 2 @grabana(kind="enum",memberNames="dontHide|hideLabel|hideVariable") + #VariableHide: 0 | 1 | 2 @cog(kind="enum",memberNames="dontHide|hideLabel|hideVariable") // Sort variable options // Accepted values are: @@ -246,11 +246,11 @@ lineage: { // `4`: Numerical DESC // `5`: Alphabetical Case Insensitive ASC // `6`: Alphabetical Case Insensitive DESC - #VariableSort: 0 | 1 | 2 | 3 | 4 | 5 | 6 @grabana(kind="enum",memberNames="disabled|alphabeticalAsc|alphabeticalDesc|numericalAsc|numericalDesc|alphabeticalCaseInsensitiveAsc|alphabeticalCaseInsensitiveDesc") + #VariableSort: 0 | 1 | 2 | 3 | 4 | 5 | 6 @cog(kind="enum",memberNames="disabled|alphabeticalAsc|alphabeticalDesc|numericalAsc|numericalDesc|alphabeticalCaseInsensitiveAsc|alphabeticalCaseInsensitiveDesc") // Loading status // Accepted values are `NotStarted` (the request is not started), `Loading` (waiting for response), `Streaming` (pulling continuous data), `Done` (response received successfully) or `Error` (failed request). - #LoadingState: "NotStarted" | "Loading" | "Streaming" | "Done" | "Error" @grabana(kind="enum") + #LoadingState: "NotStarted" | "Loading" | "Streaming" | "Done" | "Error" @cog(kind="enum") // Ref to a DataSource instance #DataSourceRef: { @@ -317,7 +317,7 @@ lineage: { // `continuous-purples`: Continuous Purple palette mode // `shades`: Shades of a single color. Specify a single color, useful in an override rule. // `fixed`: Fixed color mode. Specify a single color, useful in an override rule. - #FieldColorModeId: "thresholds" | "palette-classic" | "palette-classic-by-name" | "continuous-GrYlRd" | "continuous-RdYlGr" | "continuous-BlYlRd" | "continuous-YlRd" | "continuous-BlPu" | "continuous-YlBl" | "continuous-blues" | "continuous-reds" | "continuous-greens" | "continuous-purples" | "fixed" | "shades" @grabana(kind="enum",memberNames="Thresholds|PaletteClassic|PaletteClassicByName|ContinuousGrYlRd|ContinuousRdYlGr|ContinuousBlYlRd|ContinuousYlRd|ContinuousBlPu|ContinuousYlBl|ContinuousBlues|ContinuousReds|ContinuousGreens|ContinuousPurples|Fixed|Shades") @grafanamaturity(NeedsExpertReview) + #FieldColorModeId: "thresholds" | "palette-classic" | "palette-classic-by-name" | "continuous-GrYlRd" | "continuous-RdYlGr" | "continuous-BlYlRd" | "continuous-YlRd" | "continuous-BlPu" | "continuous-YlBl" | "continuous-blues" | "continuous-reds" | "continuous-greens" | "continuous-purples" | "fixed" | "shades" @cog(kind="enum",memberNames="Thresholds|PaletteClassic|PaletteClassicByName|ContinuousGrYlRd|ContinuousRdYlGr|ContinuousBlYlRd|ContinuousYlRd|ContinuousBlPu|ContinuousYlBl|ContinuousBlues|ContinuousReds|ContinuousGreens|ContinuousPurples|Fixed|Shades") @grafanamaturity(NeedsExpertReview) // Defines how to assign a series color from "by value" color schemes. For example for an aggregated data points like a timeseries, the color can be assigned by the min, max or last value. #FieldColorSeriesByMode: "min" | "max" | "last" @@ -357,7 +357,7 @@ lineage: { } @grafanamaturity(NeedsExpertReview) // Thresholds can either be `absolute` (specific number) or `percentage` (relative to min or max, it will be values between 0 and 1). - #ThresholdsMode: "absolute" | "percentage" @grabana(kind="enum",memberNames="Absolute|Percentage") + #ThresholdsMode: "absolute" | "percentage" @cog(kind="enum",memberNames="Absolute|Percentage") // Thresholds configuration for the panel #ThresholdsConfig: { @@ -376,7 +376,7 @@ lineage: { // `range`: Maps numerical ranges to a display text and color. For example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number. // `regex`: Maps regular expressions to replacement text and a color. For example, if a value is www.example.com, you can configure a regex value mapping so that Grafana displays www and truncates the domain. // `special`: Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color. See SpecialValueMatch to see the list of special values. For example, you can configure a special value mapping so that null values appear as N/A. - #MappingType: "value" | "range" | "regex" | "special" @grabana(kind="enum",memberNames="ValueToText|RangeToText|RegexToText|SpecialValue") @grafanamaturity(NeedsExpertReview) + #MappingType: "value" | "range" | "regex" | "special" @cog(kind="enum",memberNames="ValueToText|RangeToText|RegexToText|SpecialValue") @grafanamaturity(NeedsExpertReview) // Maps text values to a color or different display text and color. // For example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number. @@ -438,7 +438,7 @@ lineage: { } @grafanamaturity(NeedsExpertReview) // Special value types supported by the `SpecialValueMap` - #SpecialValueMatch: "true" | "false" | "null" | "nan" | "null+nan" | "empty" @grabana(kind="enum",memberNames="True|False|Null|NaN|NullAndNan|Empty") + #SpecialValueMatch: "true" | "false" | "null" | "nan" | "null+nan" | "empty" @cog(kind="enum",memberNames="True|False|Null|NaN|NullAndNan|Empty") // Result used as replacement with text and color when the value matches #ValueMappingResult: { @@ -470,7 +470,7 @@ lineage: { // 0 for no shared crosshair or tooltip (default). // 1 for shared crosshair. // 2 for shared crosshair AND shared tooltip. - #DashboardCursorSync: *0 | 1 | 2 @grabana(kind="enum",memberNames="Off|Crosshair|Tooltip") + #DashboardCursorSync: *0 | 1 | 2 @cog(kind="enum",memberNames="Off|Crosshair|Tooltip") // Schema for panel targets is specified by datasource // plugins. We use a placeholder definition, which the Go