Skip to content

Commit

Permalink
feat: Add generate command
Browse files Browse the repository at this point in the history
  • Loading branch information
yufeiminds committed Oct 10, 2023
1 parent 9e6c91f commit 71d5603
Show file tree
Hide file tree
Showing 40 changed files with 1,270 additions and 879 deletions.
15 changes: 2 additions & 13 deletions cmd/iacker/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,12 @@ package main
import (
"fmt"

"github.com/spf13/cobra"
"github.com/GuanceCloud/iacker/internal/cmd"
)

func main() {
rootCmd := NewRootCmd()
rootCmd := cmd.NewRootCmd()
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
}
}

func NewRootCmd() *cobra.Command {
return &cobra.Command{
Use: "iacker",
Short: "The IaC Provider Development Framework",
Long: `Help you to develop IaC provider easily.`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Hello, world!")
},
}
}
4 changes: 3 additions & 1 deletion cmd/iacker/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ package main
import (
"bytes"
"testing"

"github.com/GuanceCloud/iacker/internal/cmd"
)

func TestMain(t *testing.T) {
var buf bytes.Buffer
rootCmd := NewRootCmd()
rootCmd := cmd.NewRootCmd()
rootCmd.SetOutput(&buf)
rootCmd.SetArgs([]string{"--help"})
if err := rootCmd.Execute(); err != nil {
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
61 changes: 61 additions & 0 deletions examples/simple/manifest.cue
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package allinone

import (
template "github.com/GuanceCloud/iacker/pkg/template/v1"
)

// Pet is the pet resource definition
resources: "Pet": {
plural: "pets"
title: {
zh: "宠物"
en: "Pet"
}
description: {
zh: "宠物很可爱,包括猫猫狗狗等"
en: "Pets are cute, including cats, dogs, etc."
}
model: "Pet"
}

// Pet model
resources: "Pet": models: "Pet": {
title: {
zh: "宠物"
en: "Pet"
}
properties: [
{
name: "id"
title: {
zh: "ID"
en: "ID"
}
schema: {
type: "integer"
required: true
}
},
]
}

// Template definition
templates: "foo": {
name: "basic"

inputs: template.#Inputs

diagnostics: [...template.#Diagnostic]

outputs: files: "README.md": {
content: "Hello, World!"
}
}

// Template options
options: templates: [
{
template: "foo"
outdir: ".build"
},
]
20 changes: 19 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,33 @@ module github.com/GuanceCloud/iacker
go 1.19

require (
cuelang.org/go v0.6.0
github.com/fatih/color v1.15.0
github.com/hashicorp/go-multierror v1.1.1
github.com/magefile/mage v1.15.0
github.com/sergi/go-diff v1.3.1
github.com/spf13/cobra v1.7.0
github.com/stretchr/testify v1.8.4
google.golang.org/protobuf v1.31.0
)

require (
github.com/google/go-cmp v0.5.9 // indirect
github.com/cockroachdb/apd/v3 v3.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emicklei/proto v1.10.0 // indirect
github.com/google/uuid v1.2.0 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/protocolbuffers/txtpbfmt v0.0.0-20230328191034-3462fbc510c0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/mod v0.9.0 // indirect
golang.org/x/net v0.8.0 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/text v0.8.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
57 changes: 56 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
@@ -1,24 +1,79 @@
cuelang.org/go v0.6.0 h1:dJhgKCog+FEZt7OwAYV1R+o/RZPmE8aqFoptmxSWyr8=
cuelang.org/go v0.6.0/go.mod h1:9CxOX8aawrr3BgSdqPj7V0RYoXo7XIb+yDFC6uESrOQ=
github.com/cockroachdb/apd/v3 v3.2.0 h1:79kHCn4tO0VGu3W0WujYrMjBDk8a2H4KEUYcXf7whcg=
github.com/cockroachdb/apd/v3 v3.2.0/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
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=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emicklei/proto v1.10.0 h1:pDGyFRVV5RvV+nkBK9iy3q67FBy9Xa7vwrOTE+g5aGw=
github.com/emicklei/proto v1.10.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/go-quicktest/qt v1.100.0 h1:I7iSLgIwNp0E0UnSvKJzs7ig0jg/Iq83zsZjtQNW7jY=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.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.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg=
github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de h1:D5x39vF5KCwKQaw+OC9ZPiLVHXz3UFw2+psEX+gYcto=
github.com/mpvl/unique v0.0.0-20150818121801-cbe035fff7de/go.mod h1:kJun4WP5gFuHZgRjZUWWuH1DTxCtxbHDOIJsudS8jzY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/protocolbuffers/txtpbfmt v0.0.0-20230328191034-3462fbc510c0 h1:sadMIsgmHpEOGbUs6VtHBXRR1OHevnj7hLx9ZcdNGW4=
github.com/protocolbuffers/txtpbfmt v0.0.0-20230328191034-3462fbc510c0/go.mod h1:jgxiZysxFPM+iWKwQwPR+y+Jvo54ARd4EisXxKYpB5c=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
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/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
37 changes: 37 additions & 0 deletions internal/cmd/generate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package cmd

import (
"fmt"

"github.com/GuanceCloud/iacker/internal/cue"
"github.com/hashicorp/go-multierror"
"github.com/spf13/cobra"
)

// NewGenerateCmd returns a new generate command.
func NewGenerateCmd() *cobra.Command {
generateCmd := &cobra.Command{
Use: "generate",
Aliases: []string{"gen"},
Short: "Generate code from CUE",
RunE: func(cmd *cobra.Command, args []string) error {
var mErr error
if len(args) == 0 {
args = []string{"."}
}

// Generate all packages.
for _, root := range args {
rms, err := cue.ParsePackage(root)
if err != nil {
return err
}
if err := cue.Generate(rms); err != nil {
mErr = multierror.Append(mErr, fmt.Errorf("generate %s error: %w", root, err))
}
}
return mErr
},
}
return generateCmd
}
19 changes: 19 additions & 0 deletions internal/cmd/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package cmd

import (
"fmt"

"github.com/spf13/cobra"
)

// NewInitCmd returns a new init command.
func NewInitCmd() *cobra.Command {
initCmd := &cobra.Command{
Use: "init",
Short: "Initialize a new IaC provider project",
RunE: func(cmd *cobra.Command, args []string) error {
return fmt.Errorf("not implemented")
},
}
return initCmd
}
19 changes: 19 additions & 0 deletions internal/cmd/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package cmd

import (
"github.com/spf13/cobra"
)

// NewRootCmd returns a new root command.
func NewRootCmd() *cobra.Command {
root := &cobra.Command{
Use: "iacker",
Short: "The IaC Provider Development Framework",
Long: `Help you to develop IaC provider easily.`,
}
root.AddCommand(
NewInitCmd(),
NewGenerateCmd(),
)
return root
}
97 changes: 97 additions & 0 deletions internal/cue/generator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package cue

import (
"errors"
"fmt"
"path/filepath"

"cuelang.org/go/cue"
"github.com/fatih/color"
"github.com/hashicorp/go-multierror"

"github.com/GuanceCloud/iacker/internal/helpers/filediff"
templateV1 "github.com/GuanceCloud/iacker/pkg/template/v1"
)

type generator struct{}

// GenerateOption is a function that configures a generator.
type GenerateOption func(*generator) error

// Generate will generate code files into the output folder
func Generate(p *Package, options ...GenerateOption) error {
plan, err := GeneratePlan(p, options...)
if err != nil {
return fmt.Errorf("failed to generate plan: %w", err)
}
if err := plan.Pretty(); err != nil {
return fmt.Errorf("failed to pretty print plan: %w", err)
}
return plan.Save()
}

// GeneratePlan will generate a plan for generating code files into the output folder
func GeneratePlan(p *Package, options ...GenerateOption) (*Plan, error) {
var mErr error
sourceFiles := make(map[string]string)
targetFiles := make(map[string]string)
rms := p.Manifest()

// Create generator
g := &generator{}
for _, option := range options {
if err := option(g); err != nil {
mErr = multierror.Append(mErr, fmt.Errorf("generate option failed: %w", err))
}
}

// Generate files from templates
for i, templateOptions := range rms.Options.Templates {
templateName := templateOptions.Template

color.Magenta("Generate template %q to folder %q", templateName, templateOptions.Outdir)

// Get the template
templateValue := p.value.LookupPath(cue.MakePath(cue.Str("templates"), cue.Str(templateName)))
if err := templateValue.Err(); err != nil {
return nil, fmt.Errorf("lookup path failed: %w", err)
}

// Fill the template with the input RMS data
concretTemplateValue := templateValue.FillPath(cue.MakePath(cue.Str("inputs")), &templateV1.Inputs{
Resources: rms.Resources,
Errors: rms.Errors,
Vars: templateOptions.Vars,
})
if err := concretTemplateValue.Err(); err != nil {
return nil, fmt.Errorf("fill path failed: %w", err)
}

// Check the template diagnostics
var concretTemplate templateV1.Manifest
if err := concretTemplateValue.Decode(&concretTemplate); err != nil {
return nil, fmt.Errorf("decode template %s(%d): %w", templateName, i, err)
}
for _, diag := range concretTemplate.Diagnostics {
mErr = multierror.Append(mErr, errors.New(diag.Message))
}

// Build the generate plan
files, err := filediff.ReadFiles(templateOptions.Outdir)
if err != nil {
return nil, fmt.Errorf("read existed files from outdir failed: %w", err)
}
for fileName, file := range files {
sourceFiles[filepath.Join(templateOptions.Outdir, fileName)] = file
}

for fileName, file := range concretTemplate.Outputs.Files {
targetFiles[filepath.Join(templateOptions.Outdir, fileName)] = file.Content
}
}
if mErr != nil {
return nil, mErr
}

return &Plan{sources: sourceFiles, targets: targetFiles}, nil
}
Loading

0 comments on commit 71d5603

Please sign in to comment.