Skip to content

Commit cd9e502

Browse files
authored
Merge pull request #328 from daniel-hutao/feat-2
feat: `dtm develop create-plugin` implement
2 parents 76ac5ba + 4af18e6 commit cd9e502

File tree

16 files changed

+522
-0
lines changed

16 files changed

+522
-0
lines changed

cmd/devstream/develop.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/spf13/cobra"
7+
8+
"github.com/merico-dev/stream/internal/pkg/develop"
9+
"github.com/merico-dev/stream/pkg/util/log"
10+
)
11+
12+
var name string
13+
14+
var developCMD = &cobra.Command{
15+
Use: "develop",
16+
Short: "Develop is used for develop a new plugin",
17+
Long: `Develop is used for develop a new plugin.
18+
eg.
19+
- dtm develop create-plugin --name=YOUR-PLUGIN-NAME`,
20+
Run: developCMDFunc,
21+
}
22+
23+
func developCMDFunc(cmd *cobra.Command, args []string) {
24+
if err := validateArgs(args); err != nil {
25+
log.Fatal(err)
26+
}
27+
28+
developAction := develop.Action(args[0])
29+
log.Debugf("The develop action is: %s.", developAction)
30+
if err := develop.ExecuteAction(developAction); err != nil {
31+
log.Fatal(err)
32+
}
33+
}
34+
35+
func validateArgs(args []string) error {
36+
// "create-plugin"/ maybe it will be "delete-plugin"/"rename-plugin" in future.
37+
if len(args) != 1 {
38+
return fmt.Errorf("got illegal args count (expect 1, got %d). "+
39+
"See `help` command for more info", len(args))
40+
}
41+
developAction := develop.Action(args[0])
42+
if !develop.IsValideAction(developAction) {
43+
return fmt.Errorf("invalide Develop Action")
44+
}
45+
return nil
46+
}
47+
48+
func init() {
49+
developCMD.PersistentFlags().StringVarP(&name, "name", "n", "", "specify name with the new plugin")
50+
}

cmd/devstream/main.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ func init() {
5050
rootCMD.AddCommand(deleteCMD)
5151
rootCMD.AddCommand(destroyCMD)
5252
rootCMD.AddCommand(verifyCMD)
53+
rootCMD.AddCommand(developCMD)
5354
}
5455

5556
func initConfig() {
@@ -75,6 +76,9 @@ func initConfig() {
7576
if err := viper.BindPFlags(rootCMD.Flags()); err != nil {
7677
log.Fatal(err)
7778
}
79+
if err := viper.BindPFlags(developCMD.Flags()); err != nil {
80+
log.Fatal(err)
81+
}
7882
}
7983

8084
func initLog() {

internal/pkg/develop/develop.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package develop
2+
3+
import (
4+
"github.com/merico-dev/stream/internal/pkg/develop/plugin"
5+
"github.com/merico-dev/stream/pkg/util/log"
6+
)
7+
8+
type Action string
9+
10+
const (
11+
ActionCreatePlugin Action = "create-plugin"
12+
)
13+
14+
var ActionSet = map[Action]struct{}{
15+
ActionCreatePlugin: {},
16+
}
17+
18+
func IsValideAction(action Action) bool {
19+
_, ok := ActionSet[action]
20+
return ok
21+
}
22+
23+
func ExecuteAction(action Action) error {
24+
switch action {
25+
case ActionCreatePlugin:
26+
log.Debugf("Action: %s.", ActionCreatePlugin)
27+
return plugin.Create()
28+
default:
29+
panic("This should be never happen!")
30+
}
31+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package plugin
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/spf13/viper"
7+
8+
"github.com/merico-dev/stream/pkg/util/log"
9+
)
10+
11+
// Create creates a new plugin.
12+
// 1. Render template files
13+
// 2. Persist all files
14+
// 3. Print help information
15+
func Create() error {
16+
name := viper.GetString("name")
17+
if name == "" {
18+
return fmt.Errorf("the name must be not \"\", you can specify it by --name flag")
19+
}
20+
log.Debugf("Got the name: %s.", name)
21+
22+
p := NewPlugin(name)
23+
24+
// 1. Render template files
25+
files, err := p.RenderTplFiles()
26+
if err != nil {
27+
log.Debugf("Failed to render template files: %s.", err)
28+
return err
29+
}
30+
log.Info("Render template files finished.")
31+
32+
// 2. Persist all files
33+
if err := p.PersistFiles(files); err != nil {
34+
log.Debugf("Failed to persist files: %s.", err)
35+
}
36+
log.Info("Persist all files finished.")
37+
38+
// 3. Print help information
39+
p.PrintHelpInfo()
40+
41+
return nil
42+
}
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package plugin
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"os"
7+
"path"
8+
"text/template"
9+
10+
pluginTpl "github.com/merico-dev/stream/internal/pkg/develop/plugin/template"
11+
"github.com/merico-dev/stream/pkg/util/log"
12+
)
13+
14+
type Plugin struct {
15+
Name string
16+
}
17+
18+
func NewPlugin(name string) *Plugin {
19+
return &Plugin{
20+
Name: name,
21+
}
22+
}
23+
24+
// RenderTplFiles takes specified data that the templates needed,
25+
// then render TplFiles to "Files" and return it as []File.
26+
func (p *Plugin) RenderTplFiles() ([]pluginTpl.File, error) {
27+
retFiles := make([]pluginTpl.File, 0)
28+
count := len(pluginTpl.TplFiles)
29+
log.Debugf("Template files count: %d.", count)
30+
31+
for i, tplFile := range pluginTpl.TplFiles {
32+
log.Debugf("Render process: %d/%d.", i+1, count)
33+
file, err := p.renderTplFile(&tplFile)
34+
if err != nil {
35+
return nil, err
36+
}
37+
log.Debugf("File: %v.", *file)
38+
retFiles = append(retFiles, *file)
39+
}
40+
41+
return retFiles, nil
42+
}
43+
44+
// RenderTplFile takes specified data that the template needed,
45+
// then render one TplFile to File and return it.
46+
func (p *Plugin) renderTplFile(tplFile *pluginTpl.TplFile) (*pluginTpl.File, error) {
47+
name, err := p.renderTplString(tplFile.NameTpl)
48+
if err != nil {
49+
return nil, err
50+
}
51+
dir, err := p.renderTplString(tplFile.DirTpl)
52+
if err != nil {
53+
return nil, err
54+
}
55+
content, err := p.renderTplString(tplFile.ContentTpl)
56+
if err != nil {
57+
return nil, err
58+
}
59+
60+
return &pluginTpl.File{
61+
Name: name,
62+
Dir: dir,
63+
Content: content,
64+
}, nil
65+
}
66+
67+
// renderTplString get the template string and the data object,
68+
// then render it and return the rendered string.
69+
func (p *Plugin) renderTplString(tplStr string) (string, error) {
70+
if tplStr == "" {
71+
return "", nil
72+
}
73+
74+
t, err := template.New("default").Parse(tplStr)
75+
if err != nil {
76+
log.Debugf("Template parse failed: %s.", err)
77+
log.Debugf("Template content: %s.", tplStr)
78+
return "", err
79+
}
80+
81+
var buf bytes.Buffer
82+
err = t.Execute(&buf, *p)
83+
if err != nil {
84+
log.Debugf("Template execute failed: %s.", err)
85+
log.Debugf("Template content: %s.", tplStr)
86+
log.Debugf("Data object: %v.", *p)
87+
return "", err
88+
}
89+
90+
return buf.String(), nil
91+
}
92+
93+
// PersistFiles gets the []pluginTpl.File, for each File:
94+
// call the persistFile() method to deal with.
95+
func (p *Plugin) PersistFiles(files []pluginTpl.File) error {
96+
fileCount := len(files)
97+
log.Debugf("There are %d files wait to persist.", fileCount)
98+
for i, file := range files {
99+
log.Debugf("Persist process: %d/%d.", i+1, fileCount)
100+
if err := p.persistFile(&file); err != nil {
101+
log.Errorf("Failed to persist: %s/%s.", file.Dir, file.Name)
102+
return err
103+
}
104+
}
105+
106+
return nil
107+
}
108+
109+
// persistFile gets the *pluginTpl.File, then do the following:
110+
// 1. mkdir the File.Dir
111+
// 2. create the File.Name file
112+
// 3. write the File.Content into the File.Name file
113+
func (p *Plugin) persistFile(file *pluginTpl.File) error {
114+
// mkdir the File.Dir
115+
if err := os.MkdirAll(file.Dir, 0755); err != nil {
116+
log.Debugf("Failed to create directory: %s.", file.Dir)
117+
}
118+
log.Debugf("Directory created: %s.", file.Dir)
119+
120+
// create the File.Name file and write the File.Content into the File.Name file
121+
filePath := path.Join(file.Dir, file.Name)
122+
if err := os.WriteFile(filePath, []byte(file.Content), 0644); err != nil {
123+
log.Debugf("Failed to write content to the file: %s.", err)
124+
}
125+
log.Debugf("File %s has been created.", filePath)
126+
127+
return nil
128+
}
129+
130+
func (p *Plugin) PrintHelpInfo() {
131+
help := `
132+
The DevStream PMC (project management committee) sincerely thank you for your devotion and enthusiasm in creating new plugins!
133+
134+
To make the process easy as a breeze, DevStream(dtm) has generated some templated source code files for you to flatten the learning curve and reduce manual copy-paste.
135+
In the generated templates, dtm has left some special marks in the format of "TODO(dtm)".
136+
Please look for these TODOs by global search. Once you find them, you will know what to do with them. Also, please remember to check our documentation on creating a new plugin:
137+
138+
**README_when_create_plugin.md**
139+
140+
Source code files created.
141+
142+
Happy hacking, buddy!
143+
Please give us feedback through GitHub issues if you encounter any difficulties. We guarantee that you will receive unrivaled help from our passionate community!
144+
`
145+
fmt.Println(help)
146+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package template
2+
3+
var NAME_go_nameTpl = "{{ .Name }}.go"
4+
var NAME_go_dirTpl = "internal/pkg/plugin/{{ .Name }}/"
5+
var NAME_go_contentTpl = `package {{ .Name }}
6+
7+
// TODO(dtm): Add your logic here.
8+
`
9+
10+
func init() {
11+
TplFiles = append(TplFiles, TplFile{
12+
NameTpl: NAME_go_nameTpl,
13+
DirTpl: NAME_go_dirTpl,
14+
ContentTpl: NAME_go_contentTpl,
15+
})
16+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package template
2+
3+
var NAME_plugin_md_nameTpl = "{{ .Name }}_plugin.md"
4+
var NAME_plugin_md_dirTpl = "docs/plugins/plugin/"
5+
6+
// TODO(daniel-hutao): * -> `
7+
var NAME_plugin_md_contentTpl = `## 1 *{{ .Name }}* Plugin
8+
9+
// TODO(dtm): Add your document here.
10+
`
11+
12+
func init() {
13+
TplFiles = append(TplFiles, TplFile{
14+
NameTpl: NAME_plugin_md_nameTpl,
15+
DirTpl: NAME_plugin_md_dirTpl,
16+
ContentTpl: NAME_plugin_md_contentTpl,
17+
})
18+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package template
2+
3+
var README_when_create_plugin_md_nameTpl = "README_when_create_plugin.md"
4+
var README_when_create_plugin_md_dirTpl = "./"
5+
var README_when_create_plugin_md_contentTpl = `# Note with **dtm develop create-plugin**
6+
7+
## Done-Done Check
8+
9+
- [ ] I've finished all the TODO items and deleted all the *TODO(dtm)* comments.
10+
11+
The files below have been updated with my plugin:
12+
13+
- [ ] Makefile
14+
- [ ] build/package/build_linux_amd64.sh
15+
`
16+
17+
func init() {
18+
TplFiles = append(TplFiles, TplFile{
19+
NameTpl: README_when_create_plugin_md_nameTpl,
20+
DirTpl: README_when_create_plugin_md_dirTpl,
21+
ContentTpl: README_when_create_plugin_md_contentTpl,
22+
})
23+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package template
2+
3+
var create_go_nameTpl = "create.go"
4+
var create_go_dirTpl = "internal/pkg/plugin/{{ .Name }}/"
5+
var create_go_contentTpl = `package {{ .Name }}
6+
7+
func Create(options map[string]interface{}) (map[string]interface{}, error) {
8+
// TODO(dtm): Add your logic here.
9+
10+
return nil, nil
11+
}
12+
`
13+
14+
func init() {
15+
TplFiles = append(TplFiles, TplFile{
16+
NameTpl: create_go_nameTpl,
17+
DirTpl: create_go_dirTpl,
18+
ContentTpl: create_go_contentTpl,
19+
})
20+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package template
2+
3+
var delete_go_nameTpl = "delete.go"
4+
var delete_go_dirTpl = "internal/pkg/plugin/{{ .Name }}/"
5+
var delete_go_contentTpl = `package {{ .Name }}
6+
7+
func Delete(options map[string]interface{}) (bool, error) {
8+
// TODO(dtm): Add your logic here.
9+
10+
return false, nil
11+
}
12+
`
13+
14+
func init() {
15+
TplFiles = append(TplFiles, TplFile{
16+
NameTpl: delete_go_nameTpl,
17+
DirTpl: delete_go_dirTpl,
18+
ContentTpl: delete_go_contentTpl,
19+
})
20+
}

0 commit comments

Comments
 (0)