CLI Platform
Build command-line applications with the Goose CLI platform.
Overview
The CLI platform handles command-line argument parsing and executing commands.
Quick Start
package main
import (
"myapp/app"
"github.com/awesome-goose/goose"
"github.com/awesome-goose/goose/platforms/cli"
)
func main() {
platform := cli.NewPlatform(
cli.WithName("myapp"),
cli.WithVersion("0.0.0"),
)
module := &app.AppModule{}
stop, err := goose.Start(goose.CLI(platform, module, nil))
if err != nil {
panic(err)
}
defer stop()
}
Configuration
platform := cli.NewPlatform(
cli.WithName("myapp"),
cli.WithVersion("0.0.0"),
cli.WithDescription("My CLI application"),
)
Defining Commands
Basic Command
type GreetController struct{}
func (c *GreetController) Routes() types.Routes {
return types.Routes{
{Path: "greet", Handler: c.Greet},
}
}
func (c *GreetController) Greet(ctx types.Context) any {
name := ctx.Arg(0)
if name == "" {
name = "World"
}
return fmt.Sprintf("Hello, %s!", name)
}
Usage:
myapp greet John
# Output: Hello, John!
Command with Flags
func (c *Controller) Routes() types.Routes {
return types.Routes{
{
Path: "create",
Handler: c.Create,
Meta: map[string]interface{}{
"flags": []cli.Flag{
{Name: "name", Short: "n", Required: true},
{Name: "force", Short: "f", Type: "bool"},
},
},
},
}
}
func (c *Controller) Create(ctx types.Context) any {
name := ctx.Flag("name")
force := ctx.FlagBool("force")
if force {
return fmt.Sprintf("Force creating: %s", name)
}
return fmt.Sprintf("Creating: %s", name)
}
Usage:
myapp create --name=myproject
myapp create -n myproject -f
Subcommands
type UserController struct {
service *UserService `inject:""`
}
func (c *UserController) Routes() types.Routes {
return types.Routes{
{Path: "user/list", Handler: c.List},
{Path: "user/create", Handler: c.Create},
{Path: "user/delete", Handler: c.Delete},
}
}
func (c *UserController) List(ctx types.Context) any {
users := c.service.GetAll()
for _, user := range users {
fmt.Printf("%s - %s\n", user.ID, user.Email)
}
return nil
}
func (c *UserController) Create(ctx types.Context) any {
email := ctx.Flag("email")
name := ctx.Flag("name")
user, err := c.service.Create(CreateUserDTO{
Email: email,
Name: name,
})
if err != nil {
return fmt.Sprintf("Error: %v", err)
}
return fmt.Sprintf("Created user: %s", user.ID)
}
Usage:
myapp user list
myapp user create --email=user@example.com --name=John
myapp user delete 123
Context Methods
Arguments
func (c *Controller) Run(ctx types.Context) any {
// Get argument by position
first := ctx.Arg(0)
second := ctx.Arg(1)
// Get all arguments
args := ctx.Args()
return nil
}
Flags
func (c *Controller) Run(ctx types.Context) any {
// String flag
name := ctx.Flag("name")
// Bool flag
verbose := ctx.FlagBool("verbose")
// Int flag
count := ctx.FlagInt("count")
// With defaults
port := ctx.FlagInt("port")
if port == 0 {
port = 8080
}
return nil
}
Output
Console Output
func (c *Controller) Run(ctx types.Context) any {
// Return string to print
return "Hello, World!"
}
func (c *Controller) RunComplex(ctx types.Context) any {
// Print directly
fmt.Println("Processing...")
// Return formatted output
return fmt.Sprintf("Completed %d items", count)
}
Formatted Tables
func (c *UserController) List(ctx types.Context) any {
users := c.service.GetAll()
// Print header
fmt.Printf("%-36s %-30s %-20s\n", "ID", "EMAIL", "NAME")
fmt.Println(strings.Repeat("-", 86))
// Print rows
for _, user := range users {
fmt.Printf("%-36s %-30s %-20s\n", user.ID, user.Email, user.Name)
}
return nil
}
Interactive Input
import "bufio"
func (c *Controller) CreateInteractive(ctx types.Context) any {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter email: ")
email, _ := reader.ReadString('\n')
email = strings.TrimSpace(email)
fmt.Print("Enter name: ")
name, _ := reader.ReadString('\n')
name = strings.TrimSpace(name)
// Create user...
return fmt.Sprintf("Created user with email: %s", email)
}
Progress Indicators
func (c *Controller) Process(ctx types.Context) any {
items := []string{"item1", "item2", "item3"}
total := len(items)
for i, item := range items {
// Process item...
time.Sleep(time.Second)
// Show progress
percent := float64(i+1) / float64(total) * 100
fmt.Printf("\rProgress: %.0f%% (%d/%d)", percent, i+1, total)
}
fmt.Println("\nComplete!")
return nil
}
Error Handling
func (c *Controller) Run(ctx types.Context) any {
id := ctx.Arg(0)
if id == "" {
return errors.New("user ID is required")
}
user, err := c.service.GetByID(id)
if err != nil {
return fmt.Errorf("failed to get user: %w", err)
}
return fmt.Sprintf("User: %s", user.Email)
}
Exit Codes
func (c *Controller) Run(ctx types.Context) any {
err := c.doSomething()
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
return "Success"
}
Common Patterns
CRUD Commands
func (c *UserController) Routes() types.Routes {
return types.Routes{
{Path: "users", Handler: c.List},
{Path: "users/show", Handler: c.Show},
{Path: "users/create", Handler: c.Create},
{Path: "users/update", Handler: c.Update},
{Path: "users/delete", Handler: c.Delete},
}
}
Database Commands
type DatabaseController struct {
db *gorm.DB `inject:""`
}
func (c *DatabaseController) Routes() types.Routes {
return types.Routes{
{Path: "db/migrate", Handler: c.Migrate},
{Path: "db/seed", Handler: c.Seed},
{Path: "db/reset", Handler: c.Reset},
}
}
func (c *DatabaseController) Migrate(ctx types.Context) any {
c.db.AutoMigrate(&User{}, &Post{})
return "Migrations complete"
}
Help Text
Commands automatically get help:
myapp --help
myapp user --help
myapp user create --help
Best Practices
- Provide clear help text for all commands
- Use consistent flag naming across commands
- Handle errors gracefully with clear messages
- Show progress for long operations
- Use exit codes appropriately
- Support both flags and arguments where sensible
Next Steps
- CLI Tutorial - Build a CLI tool
- Services - Business logic
- Configuration - App configuration