package main import ( "context" "fmt" "os" "strings" "git.golem15.com/golem15/summercms/internal/build" "git.golem15.com/golem15/summercms/internal/dev" "git.golem15.com/golem15/summercms/modules/bonfire" "git.golem15.com/golem15/summercms/modules/lagoon" ) func main() { root, err := bonfire.NewRoot("summer", toolCommands(), os.Stdout) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } root.SetArgs(os.Args[1:]) if err := root.Execute(); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } func toolCommands() []bonfire.Command { return append([]bonfire.Command{ buildCommand(), makePluginCommand(), makeModelCommand(), makeMigrationCommand(), makeCommandCommand(), makeJobCommand(), makeAdminControllerCommand(), addPluginCommand(), newCommand(), devCommand(), parityProxyCommand(), parityRecordCommand(), parityReplayCommand(), parityBroadcastsCommand(), parityUpstreamCommand(), docsBuildCommand(), docsSyncCommand(), docsServeCommand(), }, delegateCommands()...) } func buildCommand() bonfire.Command { return bonfire.Command{ Name: "build", Description: "Generate plugin imports and build the app binary", Run: func(ctx context.Context, in bonfire.Input, out bonfire.Output) error { dir, err := os.Getwd() if err != nil { return err } return build.App(ctx, dir, out) }, } } func makePluginCommand() bonfire.Command { return bonfire.Command{ Name: "make:plugin", Description: "Scaffold a compiling plugin module from a vendor.plugin id", Args: []bonfire.Arg{{ Name: "id", Description: "Plugin ID in vendor.plugin form", Required: true, }}, Run: func(ctx context.Context, in bonfire.Input, out bonfire.Output) error { id, ok := in.Argument("id") if !ok || id == "" { return fmt.Errorf("make:plugin requires a vendor.plugin id") } dir, err := os.Getwd() if err != nil { return err } created, err := build.MakePlugin(ctx, dir, id) if err != nil { return err } out.Printf("created %s\n", created) return nil }, } } func makeModelCommand() bonfire.Command { return bonfire.Command{ Name: "make:model", Description: "Generate a GORM model and create-table migration", Args: makeArtifactArgs(), Flags: []bonfire.Flag{{ Name: "no-migration", Description: "Omit the create-table migration", Bare: true, }}, Run: func(ctx context.Context, in bonfire.Input, out bonfire.Output) error { dir, err := os.Getwd() if err != nil { return err } id, name, err := resolveMakeArgs(in, "make:model") if err != nil { return err } result, err := build.MakeModel(ctx, dir, id, name, flagTrue(in, "no-migration")) if err != nil { return err } return printArtifact(out, result) }, } } func makeMigrationCommand() bonfire.Command { return bonfire.Command{ Name: "make:migration", Description: "Generate an ordered gormigrate stub", Args: makeArtifactArgs(), Run: func(ctx context.Context, in bonfire.Input, out bonfire.Output) error { dir, err := os.Getwd() if err != nil { return err } id, name, err := resolveMakeArgs(in, "make:migration") if err != nil { return err } result, err := build.MakeMigration(ctx, dir, id, name) if err != nil { return err } return printArtifact(out, result) }, } } func makeCommandCommand() bonfire.Command { return bonfire.Command{ Name: "make:command", Description: "Generate a plugin console command", Args: makeArtifactArgs(), Run: func(ctx context.Context, in bonfire.Input, out bonfire.Output) error { dir, err := os.Getwd() if err != nil { return err } id, name, err := resolveMakeArgs(in, "make:command") if err != nil { return err } result, err := build.MakeCommand(ctx, dir, id, name) if err != nil { return err } return printArtifact(out, result) }, } } func makeJobCommand() bonfire.Command { return bonfire.Command{ Name: "make:job", Description: "Generate a plugin job without importing River", Args: makeArtifactArgs(), Run: func(ctx context.Context, in bonfire.Input, out bonfire.Output) error { dir, err := os.Getwd() if err != nil { return err } id, name, err := resolveMakeArgs(in, "make:job") if err != nil { return err } result, err := build.MakeJob(ctx, dir, id, name) if err != nil { return err } return printArtifact(out, result) }, } } func makeAdminControllerCommand() bonfire.Command { return bonfire.Command{ Name: "make:admin-controller", Description: "Generate an admin controller with fields.yaml and columns.yaml", Args: makeArtifactArgs(), Run: func(ctx context.Context, in bonfire.Input, out bonfire.Output) error { dir, err := os.Getwd() if err != nil { return err } id, name, err := resolveMakeArgs(in, "make:admin-controller") if err != nil { return err } result, err := build.MakeAdminController(ctx, dir, id, name) if err != nil { return err } return printArtifact(out, result) }, } } func makeArtifactArgs() []bonfire.Arg { return []bonfire.Arg{ {Name: "plugin", Description: "Plugin ID in vendor.plugin form, or the artifact name when already inside the plugin"}, {Name: "name", Description: "Exported Go identifier"}, } } func resolveMakeArgs(in bonfire.Input, cmd string) (id, name string, err error) { args := in.Args() switch len(args) { case 2: return args[0], args[1], nil case 1: return "", args[0], nil default: return "", "", fmt.Errorf("%s requires , or inside a plugin directory", cmd) } } func flagTrue(in bonfire.Input, name string) bool { v, ok := in.Flag(name) if !ok { return false } switch strings.ToLower(strings.TrimSpace(v)) { case "", "true", "1", "yes": return true default: return false } } func printArtifact(out bonfire.Output, result build.ArtifactResult) error { for _, path := range result.Files { out.Printf("created %s\n", path) } if result.Hint != "" { out.Printf("%s\n", result.Hint) } return nil } func devCommand() bonfire.Command { return bonfire.Command{ Name: "dev", Description: "Watch sources and rebuild the app binary", Run: func(ctx context.Context, in bonfire.Input, out bonfire.Output) error { dir, err := os.Getwd() if err != nil { return err } return dev.Watch(ctx, dir, out) }, } } func addPluginCommand() bonfire.Command { return bonfire.Command{ Name: "plugin:add", Description: "Register a local plugin module in the app manifest and workspace", Args: []bonfire.Arg{{ Name: "dir", Description: "Local plugin directory", Required: true, }}, Run: func(ctx context.Context, in bonfire.Input, out bonfire.Output) error { pluginDir, ok := in.Argument("dir") if !ok || pluginDir == "" { return fmt.Errorf("plugin:add requires a local plugin directory") } dir, err := os.Getwd() if err != nil { return err } if err := build.AddPlugin(ctx, dir, pluginDir); err != nil { return err } out.Printf("added %s\n", pluginDir) return nil }, } } func newCommand() bonfire.Command { return bonfire.Command{ Name: "new", Description: "Create a SummerCMS application in sm--app", Args: []bonfire.Arg{{ Name: "name", Description: "Application name: lower-case letters and digits, starting with a letter; not a Go keyword, main or internal", Required: true, }}, Flags: []bonfire.Flag{ {Name: "framework", Description: "Path to the framework checkout (default: a sibling summercms or summercms.go checkout)"}, {Name: "no-plugin", Description: "Do not create the starter plugin", Bare: true}, {Name: "no-git", Description: "Do not initialise a git repository", Bare: true}, }, Run: func(ctx context.Context, in bonfire.Input, out bonfire.Output) error { name, ok := in.Argument("name") if !ok || name == "" { return fmt.Errorf("new requires an application name") } dir, err := os.Getwd() if err != nil { return err } framework, _ := in.Flag("framework") key, err := lagoon.GenerateAppKey() if err != nil { return fmt.Errorf("new: generate application key: %w", err) } _, err = build.NewApp(ctx, build.NewOptions{ Name: name, Parent: dir, Framework: framework, NoPlugin: flagTrue(in, "no-plugin"), AppKey: key, NoGit: flagTrue(in, "no-git"), }, out) return err }, } }