diff --git a/cmd/char/limitedroot.go b/cmd/char/limitedroot.go new file mode 100644 index 0000000..a204a62 --- /dev/null +++ b/cmd/char/limitedroot.go @@ -0,0 +1,13 @@ +package char + +import ( + "github.com/spf13/cobra" +) + +func NewLimitedCharCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "char", + } + + return cmd +} diff --git a/cmd/char/ls.go b/cmd/char/ls.go index 505ef87..4f50d44 100644 --- a/cmd/char/ls.go +++ b/cmd/char/ls.go @@ -3,48 +3,18 @@ package char import ( "fmt" - "git.front.kjuulh.io/kjuulh/char/pkg/plugins/provider" - "git.front.kjuulh.io/kjuulh/char/pkg/register" - "git.front.kjuulh.io/kjuulh/char/pkg/schema" + "git.front.kjuulh.io/kjuulh/char/pkg/charcontext" "github.com/spf13/cobra" ) -func NewLsCommand() *cobra.Command { - gpp := provider.NewGitPluginProvider() +func NewLsCommand(charctx *charcontext.CharContext) *cobra.Command { cmd := &cobra.Command{ Use: "ls", RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() - s, err := schema.ParseFile(ctx, ".char.yml") - if err != nil { - return err - } - - plugins, err := s.GetPlugins(ctx) - if err != nil { - return err - } - - err = gpp.FetchPlugins(ctx, s.Registry, plugins) - if err != nil { - return err - } - - builder := register.NewPluginRegisterBuilder() - - for name, plugin := range plugins { - builder = builder.Add(name.Hash(), plugin.Opts.Path) - } - - r, err := builder.Build(ctx) - if err != nil { - return err - } - defer r.Close() - - about, err := r.About(ctx) + about, err := charctx.About(ctx) if err != nil { return err } @@ -53,6 +23,36 @@ func NewLsCommand() *cobra.Command { fmt.Printf("plugin: %s\n", a.Name) fmt.Printf("\tversion: %s\n", a.Version) fmt.Printf("\tabout: %s\n", a.About) + if len(a.Vars) > 0 { + fmt.Println("\tVars:") + for _, av := range a.Vars { + fmt.Printf("\t\t%s\n", av) + } + } + if len(a.Commands) > 0 { + fmt.Println("\tCommands:") + for _, ac := range a.Commands { + fmt.Printf("\t\t%s", ac) + if len(ac.Args) == 0 { + continue + } + for _, aca := range ac.Args { + isrequired := false + for _, acr := range ac.Required { + if acr == aca { + isrequired = true + } + } + if isrequired { + fmt.Printf("\t\t\t%s: required\n", aca) + } else { + fmt.Printf("\t\t\t%s\n", aca) + } + } + } + } + + fmt.Println() } return nil diff --git a/cmd/char/root.go b/cmd/char/root.go index aff6551..f872cf2 100644 --- a/cmd/char/root.go +++ b/cmd/char/root.go @@ -1,14 +1,17 @@ package char -import "github.com/spf13/cobra" +import ( + "git.front.kjuulh.io/kjuulh/char/pkg/charcontext" + "github.com/spf13/cobra" +) -func NewCharCmd() *cobra.Command { +func NewCharCmd(charctx *charcontext.CharContext) *cobra.Command { cmd := &cobra.Command{ Use: "char", } cmd.AddCommand( - NewLsCommand(), + NewLsCommand(charctx), ) return cmd diff --git a/examples/basic/benchmark.sh b/examples/basic/benchmark.sh index 25580e4..bc7ae34 100755 --- a/examples/basic/benchmark.sh +++ b/examples/basic/benchmark.sh @@ -1,5 +1,7 @@ #!/bin/bash +set -e + go build -o char ../../main.go function devcharls() { diff --git a/examples/basic/char b/examples/basic/char index beed416..62d502a 100755 Binary files a/examples/basic/char and b/examples/basic/char differ diff --git a/examples/basic/test.sh b/examples/basic/test.sh index 66aab38..81973c9 100755 --- a/examples/basic/test.sh +++ b/examples/basic/test.sh @@ -1,5 +1,7 @@ #!/bin/bash +set -e + go build -o char ../../main.go CHAR_DEV_MODE=true ./char ls diff --git a/main.go b/main.go index 530284b..4eee703 100644 --- a/main.go +++ b/main.go @@ -1,13 +1,29 @@ package main import ( + "context" + "errors" "log" "git.front.kjuulh.io/kjuulh/char/cmd/char" + "git.front.kjuulh.io/kjuulh/char/pkg/charcontext" ) func main() { - if err := char.NewCharCmd().Execute(); err != nil { + charctx, err := charcontext.NewCharContext(context.Background()) + if err != nil { + if errors.Is(err, charcontext.ErrNoContextFound) { + log.Print("you are not in a char context, as such you will be presented with limited options") + if err := char.NewLimitedCharCmd().Execute(); err != nil { + log.Fatal(err) + } + } else { + log.Fatal(err) + } + } + defer charctx.Close() + + if err := char.NewCharCmd(charctx).Execute(); err != nil { log.Fatal(err) } } diff --git a/pkg/charcontext/char_context.go b/pkg/charcontext/char_context.go new file mode 100644 index 0000000..ddb30c5 --- /dev/null +++ b/pkg/charcontext/char_context.go @@ -0,0 +1,66 @@ +package charcontext + +import ( + "context" + "log" + + "git.front.kjuulh.io/kjuulh/char/pkg/plugins/provider" + "git.front.kjuulh.io/kjuulh/char/pkg/register" + "git.front.kjuulh.io/kjuulh/char/pkg/schema" +) + +type CharContext struct { + contextPath string + pluginRegister *register.PluginRegister + schema *schema.CharSchema +} + +func NewCharContext(ctx context.Context) (*CharContext, error) { + localPath, err := FindLocalRoot(ctx) + if err != nil { + return nil, err + } + gpp := provider.NewGitPluginProvider() + + s, err := schema.ParseFile(ctx, ".char.yml") + if err != nil { + return nil, err + } + + plugins, err := s.GetPlugins(ctx) + if err != nil { + return nil, err + } + + err = gpp.FetchPlugins(ctx, s.Registry, plugins) + if err != nil { + return nil, err + } + + builder := register.NewPluginRegisterBuilder() + + for name, plugin := range plugins { + builder = builder.Add(name.Hash(), plugin.Opts.Path) + } + + r, err := builder.Build(ctx) + if err != nil { + return nil, err + } + + return &CharContext{ + contextPath: localPath, + pluginRegister: r, + schema: s, + }, nil +} + +func (cc *CharContext) Close() { + if err := cc.pluginRegister.Close(); err != nil { + log.Fatal(err) + } +} + +func (cc *CharContext) About(ctx context.Context) ([]register.AboutItem, error) { + return cc.pluginRegister.About(ctx) +} diff --git a/pkg/charcontext/context_root.go b/pkg/charcontext/context_root.go new file mode 100644 index 0000000..e73ffda --- /dev/null +++ b/pkg/charcontext/context_root.go @@ -0,0 +1,57 @@ +package charcontext + +import ( + "context" + "errors" + "os" + "path" +) + +var ErrNoContextFound = errors.New("could not find project root") + +const CharFileName = ".char.yml" + +func FindLocalRoot(ctx context.Context) (string, error) { + curdir, err := os.Getwd() + if err != nil { + return "", err + } + + return recursiveFindLocalRoot(ctx, curdir) + + //output, err := exec.Command("git", "rev-parse", "--show-toplevel").CombinedOutput() + //if err != nil { + // return "", err + //} + //if len(output) == 0 { + // return "", errors.New("could not find absolute path") + //} + //if _, err := os.Stat(string(output)); errors.Is(err, os.ErrNotExist) { + // return "", fmt.Errorf("path does not exist %s", string(output)) + //} + + //return string(output), nil +} + +func recursiveFindLocalRoot(ctx context.Context, localpath string) (string, error) { + entries, err := os.ReadDir(localpath) + if err != nil { + return "", err + } + + for _, entry := range entries { + if entry.Name() == CharFileName { + return localpath, nil + } + } + + if localpath == "/" { + return "", ErrNoContextFound + } + + return recursiveFindLocalRoot(ctx, path.Dir(localpath)) +} + +func ChangeToPath(_ context.Context, path string) error { + return os.Chdir(path) +} diff --git a/pkg/register/plugin.go b/pkg/register/plugin.go index 85f5c16..56f6618 100644 --- a/pkg/register/plugin.go +++ b/pkg/register/plugin.go @@ -2,10 +2,18 @@ package register import "context" +type AboutCommand struct { + Name string `json:"name" yaml:"name"` + Args []string `json:"args" yaml:"args"` + Required []string `json:"required" yaml:"required"` +} + type About struct { - Name string `json:"name"` - Version string `json:"version"` - About string `json:"about"` + Name string `json:"name"` + Version string `json:"version"` + About string `json:"about"` + Vars []string `json:"vars"` + Commands []*AboutCommand `json:"commands"` } type Plugin interface { diff --git a/pkg/register/plugin_register.go b/pkg/register/plugin_register.go index 11dd724..dd32646 100644 --- a/pkg/register/plugin_register.go +++ b/pkg/register/plugin_register.go @@ -164,10 +164,32 @@ func (pr *PluginRegister) Close() error { return nil } +type CommandAboutItem struct { + Name string + Args []string + Required []string +} + +type CommandAboutItems []*CommandAboutItem + +func FromAboutCommands(commands []*AboutCommand) CommandAboutItems { + cai := make(CommandAboutItems, 0) + for _, command := range commands { + cai = append(cai, &CommandAboutItem{ + Name: command.Name, + Args: command.Args, + Required: command.Required, + }) + } + return cai +} + type AboutItem struct { - Name string - Version string - About string + Name string + Version string + About string + Vars []string + Commands CommandAboutItems } func (pr *PluginRegister) About(ctx context.Context) ([]AboutItem, error) { @@ -184,9 +206,11 @@ func (pr *PluginRegister) About(ctx context.Context) ([]AboutItem, error) { } list = append(list, AboutItem{ - Name: about.Name, - Version: about.Version, - About: about.About, + Name: about.Name, + Version: about.Version, + About: about.About, + Vars: about.Vars, + Commands: FromAboutCommands(about.Commands), }) return nil }) diff --git a/pkg/schema/schema_plugin.go b/pkg/schema/schema_plugin.go index cd359d3..08a55d0 100644 --- a/pkg/schema/schema_plugin.go +++ b/pkg/schema/schema_plugin.go @@ -95,7 +95,10 @@ func (cspn CharSchemaPluginName) Get() (*PluginOps, error) { } type CharSchemaPlugins map[CharSchemaPluginName]*CharSchemaPlugin +type CharSchemaPluginVarName string +type CharSchemaPluginVars map[CharSchemaPluginVarName]string type CharSchemaPlugin struct { Opts *PluginOps + Vars CharSchemaPluginVars `json:"vars"` } diff --git a/plugins/gocli/main.go b/plugins/gocli/main.go index 4303811..7da499e 100644 --- a/plugins/gocli/main.go +++ b/plugins/gocli/main.go @@ -14,6 +14,16 @@ func (*GoCliPlugin) About(ctx context.Context) (*register.About, error) { Name: "gocli", Version: "v0.0.1", About: "golang cli provides a set of actions and presets supporting golang development", + Vars: []string{ + "dev.mode", + }, + Commands: []*register.AboutCommand{ + { + Name: "local_up", + Args: []string{"fish"}, + Required: []string{"fish"}, + }, + }, }, nil }