grafana/kinds/gen.go
Selene 473898e47c
Core: Remove thema and kindsys dependencies (#84499)
* Move some thema code inside grafana

* Use new codegen instead of thema for core kinds

* Replace TS generator

* Use new generator for go types

* Remove thema from oapi generator

* Remove thema from generators

* Don't use kindsys/thema for core kinds

* Remove kindsys/thema from plugins

* Remove last thema related

* Remove most of cuectx and move utils_ts into codegen. It also deletes wire dependency

* Merge plugins generators

* Delete thema dependency 🎉

* Fix CODEOWNERS

* Fix package name

* Fix TS output names

* More path fixes

* Fix mod codeowners

* Use original plugin's name

* Remove kindsys dependency 🎉

* Modify oapi schema and create an apply function to fix elasticsearch errors

* cue.mod was deleted by mistake

* Fix TS panels

* sort imports

* Fixing elasticsearch output

* Downgrade oapi-codegen library

* Update output ts files

* More fixes

* Restore old elasticsearch generated file and skip its generation. Remove core imports into plugins

* More lint fixes

* Add codeowners

* restore embed.go file

* Fix embed.go
2024-03-21 11:11:29 +01:00

213 lines
5.5 KiB
Go

//go:build ignore
// +build ignore
//go:generate go run gen.go
package main
import (
"context"
"fmt"
"io/fs"
"os"
"path/filepath"
"regexp"
"sort"
"strings"
"cuelang.org/go/cue"
"cuelang.org/go/cue/cuecontext"
"cuelang.org/go/cue/load"
"github.com/grafana/codejen"
"github.com/grafana/cuetsy"
"github.com/grafana/grafana/pkg/codegen"
)
// CoreDefParentPath is the path, relative to the repository root, where
// each child directory is expected to contain .cue files defining one
// Core kind.
var CoreDefParentPath = "kinds"
// TSCoreKindParentPath is the path, relative to the repository root, to the directory that
// contains one directory per kind, full of generated TS kind output: types and default consts.
var TSCoreKindParentPath = filepath.Join("packages", "grafana-schema", "src", "raw")
func main() {
if len(os.Args) > 1 {
fmt.Fprintf(os.Stderr, "code generator does not currently accept any arguments\n, got %q", os.Args)
os.Exit(1)
}
// Core kinds composite code generator. Produces all generated code in
// grafana/grafana that derives from core kinds.
coreKindsGen := codejen.JennyListWithNamer(func(def codegen.SchemaForGen) string {
return def.Name
})
// All the jennies that comprise the core kinds generator pipeline
coreKindsGen.Append(
&codegen.GoSpecJenny{},
&codegen.K8ResourcesJenny{},
&codegen.CoreRegistryJenny{},
codegen.LatestMajorsOrXJenny(TSCoreKindParentPath),
codegen.TSVeneerIndexJenny(filepath.Join("packages", "grafana-schema", "src")),
)
header := codegen.SlashHeaderMapper("kinds/gen.go")
coreKindsGen.AddPostprocessors(header)
ctx := cuecontext.New()
cwd, err := os.Getwd()
if err != nil {
fmt.Fprintf(os.Stderr, "could not get working directory: %s", err)
os.Exit(1)
}
groot := filepath.Dir(cwd)
f := os.DirFS(filepath.Join(groot, CoreDefParentPath))
kinddirs := elsedie(fs.ReadDir(f, "."))("error reading core kind fs root directory")
all, err := loadCueFiles(ctx, kinddirs)
if err != nil {
die(err)
}
sort.Slice(all, func(i, j int) bool {
return all[i].Name < all[j].Name
})
jfs, err := coreKindsGen.GenerateFS(all...)
if err != nil {
die(fmt.Errorf("core kinddirs codegen failed: %w", err))
}
commfsys := elsedie(genCommon(ctx, groot))("common schemas failed")
commfsys = elsedie(commfsys.Map(header))("failed gen header on common fsys")
if err = jfs.Merge(commfsys); err != nil {
die(err)
}
if _, set := os.LookupEnv("CODEGEN_VERIFY"); set {
if err = jfs.Verify(context.Background(), groot); err != nil {
die(fmt.Errorf("generated code is out of sync with inputs:\n%s\nrun `make gen-cue` to regenerate", err))
}
} else if err = jfs.Write(context.Background(), groot); err != nil {
die(fmt.Errorf("error while writing generated code to disk:\n%s", err))
}
}
type dummyCommonJenny struct{}
func genCommon(ctx *cue.Context, groot string) (*codejen.FS, error) {
fsys := codejen.NewFS()
path := filepath.Join("packages", "grafana-schema", "src", "common")
fsys = elsedie(fsys.Map(packageMapper))("failed remapping fs")
commonFiles := make([]string, 0)
filepath.WalkDir(filepath.Join(groot, path), func(path string, d fs.DirEntry, err error) error {
if d.IsDir() || filepath.Ext(d.Name()) != ".cue" {
return nil
}
commonFiles = append(commonFiles, path)
return nil
})
instance := load.Instances(commonFiles, &load.Config{})[0]
if instance.Err != nil {
return nil, instance.Err
}
v := ctx.BuildInstance(instance)
b := elsedie(cuetsy.Generate(v, cuetsy.Config{
Export: true,
}))("failed to generate common schema TS")
_ = fsys.Add(*codejen.NewFile(filepath.Join(path, "common.gen.ts"), b, dummyCommonJenny{}))
return fsys, nil
}
func (j dummyCommonJenny) JennyName() string {
return "CommonSchemaJenny"
}
func (j dummyCommonJenny) Generate(dummy any) ([]codejen.File, error) {
return nil, nil
}
var pkgReplace = regexp.MustCompile("^package kindsys")
func packageMapper(f codejen.File) (codejen.File, error) {
f.Data = pkgReplace.ReplaceAllLiteral(f.Data, []byte("package common"))
return f, nil
}
func elsedie[T any](t T, err error) func(msg string) T {
if err != nil {
return func(msg string) T {
fmt.Fprintf(os.Stderr, "%s: %s\n", msg, err)
os.Exit(1)
return t
}
}
return func(msg string) T {
return t
}
}
func die(err error) {
fmt.Fprint(os.Stderr, err, "\n")
os.Exit(1)
}
func loadCueFiles(ctx *cue.Context, dirs []os.DirEntry) ([]codegen.SchemaForGen, error) {
values := make([]codegen.SchemaForGen, 0)
for _, dir := range dirs {
if !dir.IsDir() {
continue
}
entries, err := os.ReadDir(dir.Name())
if err != nil {
fmt.Fprintf(os.Stderr, "error opening %s directory: %s", dir, err)
os.Exit(1)
}
// It's assuming that we only have one file in each folder
entry := filepath.Join(dir.Name(), entries[0].Name())
cueFile, err := os.ReadFile(entry)
if err != nil {
fmt.Fprintf(os.Stderr, "unable to open %s/%s file: %s", dir, entries[0].Name(), err)
os.Exit(1)
}
v := ctx.CompileBytes(cueFile)
name, err := getSchemaName(v)
if err != nil {
return nil, err
}
sch := codegen.SchemaForGen{
Name: name,
FilePath: "./" + filepath.Join(CoreDefParentPath, entry),
CueFile: v,
IsGroup: false,
OutputName: strings.ToLower(name),
}
values = append(values, sch)
}
return values, nil
}
func getSchemaName(v cue.Value) (string, error) {
namePath := v.LookupPath(cue.ParsePath("name"))
name, err := namePath.String()
if err != nil {
return "", fmt.Errorf("file doesn't have name field set: %s", err)
}
name = strings.Replace(name, "-", "_", -1)
return name, nil
}