diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..849ddff --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +dist/ diff --git a/cmd/char/ls.go b/cmd/char/ls.go index 6da075f..7d6e691 100644 --- a/cmd/char/ls.go +++ b/cmd/char/ls.go @@ -15,20 +15,23 @@ func NewLsCommand() *cobra.Command { r, err := register. NewPluginRegisterBuilder(). - Add("gocli", ""). + Add("gocli", "plugins/gocli/main.go"). + Add("rust", "plugins/rust/main.go"). Build(ctx) if err != nil { return err } + defer r.Close() about, err := r.About(ctx) if err != nil { return err } - for plugin, aboutText := range about { - fmt.Printf("plugin: %s\n", plugin) - fmt.Printf("\tabout: %s\n", aboutText) + for _, a := range about { + fmt.Printf("plugin: %s\n", a.Name) + fmt.Printf("\tversion: %s\n", a.Version) + fmt.Printf("\tabout: %s\n", a.About) fmt.Println() } diff --git a/examples/basic/.char/plugins/rust b/examples/basic/.char/plugins/rust new file mode 120000 index 0000000..11a54ed --- /dev/null +++ b/examples/basic/.char/plugins/rust @@ -0,0 +1 @@ +../../../../ \ No newline at end of file diff --git a/examples/basic/char b/examples/basic/char new file mode 100755 index 0000000..11c065e Binary files /dev/null and b/examples/basic/char differ diff --git a/examples/basic/char.yml b/examples/basic/char.yml new file mode 100644 index 0000000..c749f47 --- /dev/null +++ b/examples/basic/char.yml @@ -0,0 +1,4 @@ +registry: git.front.kjuulh.io +plugins: + "kjuulh/char#plugins/gocli": {} + "kjuulh/char#plugins/rust": {} diff --git a/examples/basic/test.sh b/examples/basic/test.sh index d133f8e..66aab38 100755 --- a/examples/basic/test.sh +++ b/examples/basic/test.sh @@ -1,3 +1,5 @@ #!/bin/bash -go run ../../main.go ls +go build -o char ../../main.go + +CHAR_DEV_MODE=true ./char ls diff --git a/pkg/plugins/script/script.go b/pkg/plugins/script/script.go deleted file mode 100644 index 4759160..0000000 --- a/pkg/plugins/script/script.go +++ /dev/null @@ -1 +0,0 @@ -package script diff --git a/pkg/register/plugin.go b/pkg/register/plugin.go index 139dd36..5a28a5a 100644 --- a/pkg/register/plugin.go +++ b/pkg/register/plugin.go @@ -1,5 +1,13 @@ package register -type Plugin interface { - About() string +import "context" + +type About struct { + Name string `json:"name"` + Version string `json:"version"` + About string `json:"about"` +} + +type Plugin interface { + About(ctx context.Context) (*About, error) } diff --git a/pkg/register/plugin_api.go b/pkg/register/plugin_api.go index b19ada3..f5885a9 100644 --- a/pkg/register/plugin_api.go +++ b/pkg/register/plugin_api.go @@ -7,6 +7,7 @@ import ( ) type PluginAPI struct { + path string Impl Plugin } diff --git a/pkg/register/plugin_builder.go b/pkg/register/plugin_builder.go index 18735d2..eef9d70 100644 --- a/pkg/register/plugin_builder.go +++ b/pkg/register/plugin_builder.go @@ -14,7 +14,7 @@ type PluginBuilder struct { func NewPluginBuilder(name string, p Plugin) *PluginBuilder { logger := hclog.New(&hclog.LoggerOptions{ - Level: hclog.Trace, + Level: hclog.Error, Output: os.Stderr, JSONFormat: false, }) diff --git a/pkg/register/plugin_client.go b/pkg/register/plugin_client.go index 0477b08..954acc6 100644 --- a/pkg/register/plugin_client.go +++ b/pkg/register/plugin_client.go @@ -1,7 +1,8 @@ package register import ( - "log" + "context" + "encoding/json" "net/rpc" ) @@ -11,12 +12,18 @@ type PluginClient struct { var _ Plugin = &PluginClient{} -func (pc *PluginClient) About() string { +func (pc *PluginClient) About(ctx context.Context) (*About, error) { var resp string err := pc.client.Call("Plugin.About", new(any), &resp) if err != nil { - log.Fatal(err) + return nil, err } - return resp + var about About + err = json.Unmarshal([]byte(resp), &about) + if err != nil { + return nil, err + } + + return &about, nil } diff --git a/pkg/register/plugin_register.go b/pkg/register/plugin_register.go index f615f8a..9d7a698 100644 --- a/pkg/register/plugin_register.go +++ b/pkg/register/plugin_register.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "log" "os" "os/exec" @@ -23,7 +24,9 @@ func NewPluginRegisterBuilder() *PluginRegisterBuilder { } func (pr *PluginRegisterBuilder) Add(name, path string) *PluginRegisterBuilder { - pr.plugins[name] = PluginAPI{} + pr.plugins[name] = PluginAPI{ + path: path, + } return pr } @@ -32,10 +35,38 @@ func (pr *PluginRegisterBuilder) Build(ctx context.Context) (*PluginRegister, er clients := make(map[string]*pluginClientWrapper, 0) errgroup, _ := errgroup.WithContext(ctx) + if err := os.MkdirAll(".char/plugins/", 0755); err != nil { + return nil, err + } + for name, p := range pr.plugins { name, p := name, p errgroup.Go(func() error { + pluginPath := fmt.Sprintf(".char/plugins/%s/dist/%s", name, name) + + _, err := os.Stat(pluginPath) + if err != nil || os.Getenv("CHAR_DEV_MODE") == "true" { + log.Printf("building: %s", name) + cmd := exec.Command( + "sh", + "-c", + fmt.Sprintf( + "(cd .char/plugins/%s; go build -o dist/%s %s)", + name, + name, + p.path, + ), + ) + output, err := cmd.CombinedOutput() + if len(output) > 0 { + log.Println(string(output)) + } + if err != nil { + return fmt.Errorf("could not build plugin: %w", err) + } + } + client := plugin.NewClient(&plugin.ClientConfig{ HandshakeConfig: plugin.HandshakeConfig{ ProtocolVersion: 1, @@ -45,13 +76,15 @@ func (pr *PluginRegisterBuilder) Build(ctx context.Context) (*PluginRegister, er Logger: hclog.New(&hclog.LoggerOptions{ Name: "char", Output: os.Stdout, - Level: hclog.Debug, + Level: hclog.Error, }), - Cmd: exec.Command("sh", "-c", fmt.Sprintf( - "(cd ./.char/plugins/%s; go run plugins/%s/main.go)", - name, - name, - )), + Cmd: exec.Command( + fmt.Sprintf( + ".char/plugins/%s/dist/%s", + name, + name, + ), + ), Plugins: map[string]plugin.Plugin{ name: &p, }, @@ -126,17 +159,30 @@ func (pr *PluginRegister) Close() error { return nil } -func (pr *PluginRegister) About(ctx context.Context) (map[string]string, error) { - list := make(map[string]string, len(pr.clients)) +type AboutItem struct { + Name string + Version string + About string +} + +func (pr *PluginRegister) About(ctx context.Context) ([]AboutItem, error) { + list := make([]AboutItem, 0) errgroup, ctx := errgroup.WithContext(ctx) for n, c := range pr.clients { n, c := n, c errgroup.Go(func() error { - about := c.plugin.About() + about, err := c.plugin.About(ctx) + if err != nil { + return err + } - list[n] = about + list = append(list, AboutItem{ + Name: n, + Version: about.Version, + About: about.About, + }) return nil }) } diff --git a/pkg/register/plugin_server.go b/pkg/register/plugin_server.go index bdb333f..0de527c 100644 --- a/pkg/register/plugin_server.go +++ b/pkg/register/plugin_server.go @@ -1,10 +1,25 @@ package register +import ( + "context" + "encoding/json" +) + type PluginServer struct { Impl Plugin } func (ps *PluginServer) About(args any, resp *string) error { - *resp = ps.Impl.About() + r, err := ps.Impl.About(context.Background()) + if err != nil { + return err + } + + respB, err := json.Marshal(r) + if err != nil { + return err + } + + *resp = string(respB) return nil } diff --git a/plugins/gocli/main.go b/plugins/gocli/main.go index e48e79f..cd34804 100644 --- a/plugins/gocli/main.go +++ b/plugins/gocli/main.go @@ -10,9 +10,12 @@ import ( type GoCliPlugin struct { } -// About implements register.Plugin -func (*GoCliPlugin) About() string { - return "gocli" +func (*GoCliPlugin) About(ctx context.Context) (*register.About, error) { + return ®ister.About{ + Name: "gocli", + Version: "v0.0.1", + About: "golang cli provides a set of actions and presets supporting golang development", + }, nil } var _ register.Plugin = &GoCliPlugin{} diff --git a/plugins/rust/main.go b/plugins/rust/main.go new file mode 100644 index 0000000..edc957b --- /dev/null +++ b/plugins/rust/main.go @@ -0,0 +1,32 @@ +package main + +import ( + "context" + "log" + + "git.front.kjuulh.io/kjuulh/char/pkg/register" +) + +type GoCliPlugin struct { +} + +func (*GoCliPlugin) About(ctx context.Context) (*register.About, error) { + return ®ister.About{ + Name: "rust", + Version: "v0.0.1", + About: "golang cli provides a set of actions and presets supporting golang development", + }, nil +} + +var _ register.Plugin = &GoCliPlugin{} + +func main() { + if err := register. + NewPluginBuilder( + "rust", + &GoCliPlugin{}, + ). + Serve(context.Background()); err != nil { + log.Fatal(err) + } +}