Initial commit
This commit is contained in:
212
skills/cli-patterns/examples/EXAMPLES-INDEX.md
Normal file
212
skills/cli-patterns/examples/EXAMPLES-INDEX.md
Normal file
@@ -0,0 +1,212 @@
|
||||
# CLI Patterns Examples Index
|
||||
|
||||
Comprehensive examples demonstrating urfave/cli patterns in production-ready applications.
|
||||
|
||||
## Example Applications
|
||||
|
||||
### 1. Database CLI Tool (`db-cli/`)
|
||||
|
||||
**Purpose**: Complete database management CLI with categories, hooks, and connection handling.
|
||||
|
||||
**Features**:
|
||||
- Command categories (Schema, Data, Admin)
|
||||
- Before hook for connection validation
|
||||
- After hook for cleanup
|
||||
- Required and optional flags
|
||||
- Environment variable fallbacks
|
||||
|
||||
**Commands**:
|
||||
- `migrate` - Run migrations with direction and steps
|
||||
- `rollback` - Rollback last migration
|
||||
- `seed` - Seed database with test data
|
||||
- `backup` - Create database backup
|
||||
- `restore` - Restore from backup
|
||||
- `status` - Check database status
|
||||
- `vacuum` - Optimize database
|
||||
|
||||
**Key Patterns**:
|
||||
```go
|
||||
// Connection validation in Before hook
|
||||
Before: func(c *cli.Context) error {
|
||||
conn := c.String("connection")
|
||||
// Validate connection
|
||||
return nil
|
||||
}
|
||||
|
||||
// Cleanup in After hook
|
||||
After: func(c *cli.Context) error {
|
||||
// Close connections
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. Deployment CLI Tool (`deploy-cli/`)
|
||||
|
||||
**Purpose**: Deployment automation with context management and environment validation.
|
||||
|
||||
**Features**:
|
||||
- Context management with shared state
|
||||
- Environment validation
|
||||
- Confirmation prompts for destructive actions
|
||||
- AWS region configuration
|
||||
- Build, deploy, and monitor workflows
|
||||
|
||||
**Commands**:
|
||||
- `build` - Build application with tags
|
||||
- `test` - Run test suite
|
||||
- `deploy` - Deploy to environment (with confirmation)
|
||||
- `rollback` - Rollback to previous version
|
||||
- `logs` - View deployment logs
|
||||
- `status` - Check deployment status
|
||||
|
||||
**Key Patterns**:
|
||||
```go
|
||||
// Shared context across commands
|
||||
type DeployContext struct {
|
||||
Environment string
|
||||
AWSRegion string
|
||||
Verbose bool
|
||||
}
|
||||
|
||||
// Store context in Before hook
|
||||
ctx := &DeployContext{...}
|
||||
c.App.Metadata["ctx"] = ctx
|
||||
|
||||
// Retrieve in command
|
||||
ctx := c.App.Metadata["ctx"].(*DeployContext)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. API Client CLI Tool (`api-cli/`)
|
||||
|
||||
**Purpose**: REST API client with HTTP client sharing and authentication.
|
||||
|
||||
**Features**:
|
||||
- HTTP client sharing via context
|
||||
- Authentication in Before hook
|
||||
- Multiple HTTP methods (GET, POST, PUT, DELETE)
|
||||
- Request timeout configuration
|
||||
- Token masking for security
|
||||
|
||||
**Commands**:
|
||||
- `get` - GET request with headers
|
||||
- `post` - POST request with data
|
||||
- `put` - PUT request with data
|
||||
- `delete` - DELETE request
|
||||
- `auth-test` - Test authentication
|
||||
|
||||
**Key Patterns**:
|
||||
```go
|
||||
// HTTP client in context
|
||||
type APIContext struct {
|
||||
BaseURL string
|
||||
Token string
|
||||
HTTPClient *http.Client
|
||||
}
|
||||
|
||||
// Initialize in Before hook
|
||||
client := &http.Client{Timeout: timeout}
|
||||
ctx := &APIContext{
|
||||
HTTPClient: client,
|
||||
...
|
||||
}
|
||||
|
||||
// Use in commands
|
||||
ctx := c.App.Metadata["ctx"].(*APIContext)
|
||||
resp, err := ctx.HTTPClient.Get(url)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pattern Summary
|
||||
|
||||
### Context Management
|
||||
All three examples demonstrate different context patterns:
|
||||
- **db-cli**: Connection validation and cleanup
|
||||
- **deploy-cli**: Shared deployment configuration
|
||||
- **api-cli**: HTTP client and authentication sharing
|
||||
|
||||
### Before/After Hooks
|
||||
- **Before**: Setup, validation, authentication, connection establishment
|
||||
- **After**: Cleanup, resource release, connection closing
|
||||
|
||||
### Command Categories
|
||||
Organized command groups for better UX:
|
||||
- **db-cli**: Schema, Data, Admin
|
||||
- **deploy-cli**: Build, Deploy, Monitor
|
||||
- **api-cli**: No categories (simple HTTP verbs)
|
||||
|
||||
### Flag Patterns
|
||||
- Required flags: `--connection`, `--env`, `--token`
|
||||
- Environment variables: All support env var fallbacks
|
||||
- Aliases: Short forms (-v, -e, -t)
|
||||
- Multiple values: StringSlice for headers
|
||||
- Custom types: Duration for timeouts
|
||||
|
||||
### Error Handling
|
||||
All examples demonstrate:
|
||||
- Validation in Before hooks
|
||||
- Proper error returns
|
||||
- User-friendly error messages
|
||||
- Exit code handling
|
||||
|
||||
## Running the Examples
|
||||
|
||||
### Database CLI
|
||||
```bash
|
||||
export DATABASE_URL="postgres://user:pass@localhost/mydb"
|
||||
cd examples/db-cli
|
||||
go build -o dbctl .
|
||||
./dbctl migrate
|
||||
./dbctl backup --output backup.sql
|
||||
```
|
||||
|
||||
### Deployment CLI
|
||||
```bash
|
||||
export DEPLOY_ENV=staging
|
||||
export AWS_REGION=us-east-1
|
||||
cd examples/deploy-cli
|
||||
go build -o deploy .
|
||||
./deploy build --tag v1.0.0
|
||||
./deploy deploy
|
||||
```
|
||||
|
||||
### API Client CLI
|
||||
```bash
|
||||
export API_URL=https://api.example.com
|
||||
export API_TOKEN=your_token_here
|
||||
cd examples/api-cli
|
||||
go build -o api .
|
||||
./api get /users
|
||||
./api post /users '{"name":"John"}'
|
||||
```
|
||||
|
||||
## Learning Path
|
||||
|
||||
**Beginner**:
|
||||
1. Start with `db-cli` - demonstrates basic categories and hooks
|
||||
2. Study Before/After hook patterns
|
||||
3. Learn flag types and validation
|
||||
|
||||
**Intermediate**:
|
||||
4. Study `deploy-cli` - context management and shared state
|
||||
5. Learn environment validation
|
||||
6. Understand confirmation prompts
|
||||
|
||||
**Advanced**:
|
||||
7. Study `api-cli` - HTTP client sharing and authentication
|
||||
8. Learn complex context patterns
|
||||
9. Understand resource lifecycle management
|
||||
|
||||
## Cross-Language Comparison
|
||||
|
||||
Each example can be implemented in other languages:
|
||||
- **TypeScript**: Use commander.js (see templates/)
|
||||
- **Python**: Use click or typer (see templates/)
|
||||
- **Ruby**: Use thor
|
||||
- **Rust**: Use clap
|
||||
|
||||
The patterns translate directly across languages with similar CLI frameworks.
|
||||
69
skills/cli-patterns/examples/api-cli/README.md
Normal file
69
skills/cli-patterns/examples/api-cli/README.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# API Client CLI Tool Example
|
||||
|
||||
Complete REST API client CLI demonstrating:
|
||||
- HTTP client sharing via context
|
||||
- Authentication in Before hook
|
||||
- Multiple HTTP methods (GET, POST, PUT, DELETE)
|
||||
- Headers and request configuration
|
||||
- Arguments handling
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Set environment variables
|
||||
export API_URL=https://api.example.com
|
||||
export API_TOKEN=your_token_here
|
||||
|
||||
# GET request
|
||||
api get /users
|
||||
api get /users/123
|
||||
|
||||
# POST request
|
||||
api post /users '{"name": "John", "email": "john@example.com"}'
|
||||
api post /posts '{"title": "Hello", "body": "World"}' --content-type application/json
|
||||
|
||||
# PUT request
|
||||
api put /users/123 '{"name": "Jane"}'
|
||||
|
||||
# DELETE request
|
||||
api delete /users/123
|
||||
|
||||
# Test authentication
|
||||
api auth-test
|
||||
|
||||
# Custom timeout
|
||||
api --timeout 60s get /slow-endpoint
|
||||
|
||||
# Additional headers
|
||||
api get /users -H "Accept:application/json" -H "X-Custom:value"
|
||||
```
|
||||
|
||||
## Features Demonstrated
|
||||
|
||||
1. **Context Management**: Shared HTTPClient and auth across requests
|
||||
2. **Before Hook**: Authenticates and sets up HTTP client
|
||||
3. **Arguments**: Commands accept endpoint and data as arguments
|
||||
4. **Required Flags**: --url and --token are required
|
||||
5. **Environment Variables**: API_URL, API_TOKEN, API_TIMEOUT fallbacks
|
||||
6. **Duration Flags**: --timeout uses time.Duration type
|
||||
7. **Multiple Values**: --header can be specified multiple times
|
||||
8. **Helper Functions**: maskToken() for secure token display
|
||||
|
||||
## HTTP Client Pattern
|
||||
|
||||
```go
|
||||
type APIContext struct {
|
||||
BaseURL string
|
||||
Token string
|
||||
HTTPClient *http.Client
|
||||
}
|
||||
|
||||
// Initialize in Before hook
|
||||
client := &http.Client{Timeout: timeout}
|
||||
ctx := &APIContext{...}
|
||||
c.App.Metadata["ctx"] = ctx
|
||||
|
||||
// Use in commands
|
||||
ctx := c.App.Metadata["ctx"].(*APIContext)
|
||||
resp, err := ctx.HTTPClient.Get(url)
|
||||
```
|
||||
205
skills/cli-patterns/examples/api-cli/main.go
Normal file
205
skills/cli-patterns/examples/api-cli/main.go
Normal file
@@ -0,0 +1,205 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
type APIContext struct {
|
||||
BaseURL string
|
||||
Token string
|
||||
HTTPClient *http.Client
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := &cli.App{
|
||||
Name: "api",
|
||||
Usage: "REST API client CLI",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "url",
|
||||
Usage: "API base URL",
|
||||
EnvVars: []string{"API_URL"},
|
||||
Required: true,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "token",
|
||||
Aliases: []string{"t"},
|
||||
Usage: "Authentication token",
|
||||
EnvVars: []string{"API_TOKEN"},
|
||||
Required: true,
|
||||
},
|
||||
&cli.DurationFlag{
|
||||
Name: "timeout",
|
||||
Usage: "Request timeout",
|
||||
Value: 30 * time.Second,
|
||||
EnvVars: []string{"API_TIMEOUT"},
|
||||
},
|
||||
},
|
||||
|
||||
Before: func(c *cli.Context) error {
|
||||
baseURL := c.String("url")
|
||||
token := c.String("token")
|
||||
timeout := c.Duration("timeout")
|
||||
|
||||
fmt.Println("🔐 Authenticating with API...")
|
||||
|
||||
// Create HTTP client
|
||||
client := &http.Client{
|
||||
Timeout: timeout,
|
||||
}
|
||||
|
||||
// Store context
|
||||
ctx := &APIContext{
|
||||
BaseURL: baseURL,
|
||||
Token: token,
|
||||
HTTPClient: client,
|
||||
}
|
||||
c.App.Metadata["ctx"] = ctx
|
||||
|
||||
fmt.Println("✅ Authentication successful")
|
||||
|
||||
return nil
|
||||
},
|
||||
|
||||
Commands: []*cli.Command{
|
||||
{
|
||||
Name: "get",
|
||||
Usage: "GET request",
|
||||
ArgsUsage: "<endpoint>",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringSliceFlag{
|
||||
Name: "header",
|
||||
Aliases: []string{"H"},
|
||||
Usage: "Additional headers (key:value)",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
ctx := c.App.Metadata["ctx"].(*APIContext)
|
||||
|
||||
if c.NArg() < 1 {
|
||||
return fmt.Errorf("endpoint required")
|
||||
}
|
||||
|
||||
endpoint := c.Args().Get(0)
|
||||
url := fmt.Sprintf("%s%s", ctx.BaseURL, endpoint)
|
||||
|
||||
fmt.Printf("GET %s\n", url)
|
||||
fmt.Printf("Authorization: Bearer %s\n", maskToken(ctx.Token))
|
||||
|
||||
// In real app: make HTTP request
|
||||
fmt.Println("Response: 200 OK")
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Name: "post",
|
||||
Usage: "POST request",
|
||||
ArgsUsage: "<endpoint> <data>",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "content-type",
|
||||
Aliases: []string{"ct"},
|
||||
Usage: "Content-Type header",
|
||||
Value: "application/json",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
ctx := c.App.Metadata["ctx"].(*APIContext)
|
||||
|
||||
if c.NArg() < 2 {
|
||||
return fmt.Errorf("usage: post <endpoint> <data>")
|
||||
}
|
||||
|
||||
endpoint := c.Args().Get(0)
|
||||
data := c.Args().Get(1)
|
||||
url := fmt.Sprintf("%s%s", ctx.BaseURL, endpoint)
|
||||
contentType := c.String("content-type")
|
||||
|
||||
fmt.Printf("POST %s\n", url)
|
||||
fmt.Printf("Content-Type: %s\n", contentType)
|
||||
fmt.Printf("Data: %s\n", data)
|
||||
|
||||
// In real app: make HTTP POST request
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Name: "put",
|
||||
Usage: "PUT request",
|
||||
ArgsUsage: "<endpoint> <data>",
|
||||
Action: func(c *cli.Context) error {
|
||||
ctx := c.App.Metadata["ctx"].(*APIContext)
|
||||
|
||||
if c.NArg() < 2 {
|
||||
return fmt.Errorf("usage: put <endpoint> <data>")
|
||||
}
|
||||
|
||||
endpoint := c.Args().Get(0)
|
||||
data := c.Args().Get(1)
|
||||
url := fmt.Sprintf("%s%s", ctx.BaseURL, endpoint)
|
||||
|
||||
fmt.Printf("PUT %s\n", url)
|
||||
fmt.Printf("Data: %s\n", data)
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Name: "delete",
|
||||
Usage: "DELETE request",
|
||||
ArgsUsage: "<endpoint>",
|
||||
Action: func(c *cli.Context) error {
|
||||
ctx := c.App.Metadata["ctx"].(*APIContext)
|
||||
|
||||
if c.NArg() < 1 {
|
||||
return fmt.Errorf("endpoint required")
|
||||
}
|
||||
|
||||
endpoint := c.Args().Get(0)
|
||||
url := fmt.Sprintf("%s%s", ctx.BaseURL, endpoint)
|
||||
|
||||
fmt.Printf("DELETE %s\n", url)
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Name: "auth-test",
|
||||
Usage: "Test authentication",
|
||||
Action: func(c *cli.Context) error {
|
||||
ctx := c.App.Metadata["ctx"].(*APIContext)
|
||||
|
||||
fmt.Println("Testing authentication...")
|
||||
fmt.Printf("API URL: %s\n", ctx.BaseURL)
|
||||
fmt.Printf("Token: %s\n", maskToken(ctx.Token))
|
||||
fmt.Println("Status: Authenticated ✅")
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func maskToken(token string) string {
|
||||
if len(token) < 8 {
|
||||
return "****"
|
||||
}
|
||||
return token[:4] + "****" + token[len(token)-4:]
|
||||
}
|
||||
46
skills/cli-patterns/examples/db-cli/README.md
Normal file
46
skills/cli-patterns/examples/db-cli/README.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# Database CLI Tool Example
|
||||
|
||||
Complete database management CLI demonstrating:
|
||||
- Command categories (Schema, Data, Admin)
|
||||
- Before hook for connection validation
|
||||
- After hook for cleanup
|
||||
- Required and optional flags
|
||||
- Environment variable fallbacks
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Set connection string
|
||||
export DATABASE_URL="postgres://user:pass@localhost/mydb"
|
||||
|
||||
# Run migrations
|
||||
dbctl migrate
|
||||
dbctl migrate --direction down --steps 2
|
||||
|
||||
# Rollback
|
||||
dbctl rollback
|
||||
|
||||
# Seed database
|
||||
dbctl seed --file seeds/test-data.sql
|
||||
|
||||
# Backup and restore
|
||||
dbctl backup --output backups/db-$(date +%Y%m%d).sql
|
||||
dbctl restore --input backups/db-20240101.sql
|
||||
|
||||
# Admin tasks
|
||||
dbctl status
|
||||
dbctl vacuum
|
||||
|
||||
# Verbose output
|
||||
dbctl -v migrate
|
||||
```
|
||||
|
||||
## Features Demonstrated
|
||||
|
||||
1. **Command Categories**: Schema, Data, Admin
|
||||
2. **Global Flags**: --connection, --verbose
|
||||
3. **Before Hook**: Validates connection before any command
|
||||
4. **After Hook**: Closes connections after command completes
|
||||
5. **Required Flags**: backup/restore require file paths
|
||||
6. **Environment Variables**: DATABASE_URL fallback
|
||||
7. **Flag Aliases**: -v for --verbose, -d for --direction
|
||||
183
skills/cli-patterns/examples/db-cli/main.go
Normal file
183
skills/cli-patterns/examples/db-cli/main.go
Normal file
@@ -0,0 +1,183 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := &cli.App{
|
||||
Name: "dbctl",
|
||||
Usage: "Database management CLI tool",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "connection",
|
||||
Aliases: []string{"conn"},
|
||||
Usage: "Database connection string",
|
||||
EnvVars: []string{"DATABASE_URL"},
|
||||
Required: true,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "verbose",
|
||||
Aliases: []string{"v"},
|
||||
Usage: "Enable verbose output",
|
||||
},
|
||||
},
|
||||
|
||||
Before: func(c *cli.Context) error {
|
||||
conn := c.String("connection")
|
||||
verbose := c.Bool("verbose")
|
||||
|
||||
if verbose {
|
||||
fmt.Println("🔗 Validating database connection...")
|
||||
}
|
||||
|
||||
// Validate connection string
|
||||
if conn == "" {
|
||||
return fmt.Errorf("database connection string required")
|
||||
}
|
||||
|
||||
if verbose {
|
||||
fmt.Println("✅ Connection string validated")
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
|
||||
After: func(c *cli.Context) error {
|
||||
if c.Bool("verbose") {
|
||||
fmt.Println("🔚 Closing database connections...")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
|
||||
Commands: []*cli.Command{
|
||||
// Schema category
|
||||
{
|
||||
Name: "migrate",
|
||||
Category: "Schema",
|
||||
Usage: "Run database migrations",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "direction",
|
||||
Aliases: []string{"d"},
|
||||
Usage: "Migration direction (up/down)",
|
||||
Value: "up",
|
||||
},
|
||||
&cli.IntFlag{
|
||||
Name: "steps",
|
||||
Usage: "Number of steps to migrate",
|
||||
Value: 0,
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
direction := c.String("direction")
|
||||
steps := c.Int("steps")
|
||||
|
||||
fmt.Printf("Running migrations %s", direction)
|
||||
if steps > 0 {
|
||||
fmt.Printf(" (%d steps)", steps)
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "rollback",
|
||||
Category: "Schema",
|
||||
Usage: "Rollback last migration",
|
||||
Action: func(c *cli.Context) error {
|
||||
fmt.Println("Rolling back last migration...")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
|
||||
// Data category
|
||||
{
|
||||
Name: "seed",
|
||||
Category: "Data",
|
||||
Usage: "Seed database with test data",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "file",
|
||||
Aliases: []string{"f"},
|
||||
Usage: "Seed file path",
|
||||
Value: "seeds/default.sql",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
file := c.String("file")
|
||||
fmt.Printf("Seeding database from: %s\n", file)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "backup",
|
||||
Category: "Data",
|
||||
Usage: "Backup database",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "output",
|
||||
Aliases: []string{"o"},
|
||||
Usage: "Backup output path",
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
output := c.String("output")
|
||||
fmt.Printf("Backing up database to: %s\n", output)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "restore",
|
||||
Category: "Data",
|
||||
Usage: "Restore database from backup",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "input",
|
||||
Aliases: []string{"i"},
|
||||
Usage: "Backup file path",
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
input := c.String("input")
|
||||
fmt.Printf("Restoring database from: %s\n", input)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
|
||||
// Admin category
|
||||
{
|
||||
Name: "status",
|
||||
Category: "Admin",
|
||||
Usage: "Check database status",
|
||||
Action: func(c *cli.Context) error {
|
||||
fmt.Println("Database Status:")
|
||||
fmt.Println(" Connection: Active")
|
||||
fmt.Println(" Tables: 15")
|
||||
fmt.Println(" Size: 245 MB")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "vacuum",
|
||||
Category: "Admin",
|
||||
Usage: "Optimize database",
|
||||
Action: func(c *cli.Context) error {
|
||||
fmt.Println("Optimizing database...")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
60
skills/cli-patterns/examples/deploy-cli/README.md
Normal file
60
skills/cli-patterns/examples/deploy-cli/README.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# Deployment CLI Tool Example
|
||||
|
||||
Complete deployment automation CLI demonstrating:
|
||||
- Context management with shared state
|
||||
- Environment validation in Before hook
|
||||
- Command categories (Build, Deploy, Monitor)
|
||||
- Confirmation prompts for destructive actions
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Set environment variables
|
||||
export DEPLOY_ENV=staging
|
||||
export AWS_REGION=us-west-2
|
||||
|
||||
# Build application
|
||||
deploy --env staging build
|
||||
deploy -e production build --tag v1.2.3
|
||||
|
||||
# Run tests
|
||||
deploy --env staging test
|
||||
|
||||
# Deploy
|
||||
deploy --env staging deploy
|
||||
deploy -e production deploy --auto-approve
|
||||
|
||||
# Rollback
|
||||
deploy --env production rollback
|
||||
|
||||
# Monitor
|
||||
deploy --env production logs --follow
|
||||
deploy -e staging status
|
||||
```
|
||||
|
||||
## Features Demonstrated
|
||||
|
||||
1. **Context Management**: Shared DeployContext across commands
|
||||
2. **Environment Validation**: Before hook validates target environment
|
||||
3. **Required Flags**: --env is required for all operations
|
||||
4. **Confirmation Prompts**: Deploy asks for confirmation (unless --auto-approve)
|
||||
5. **Command Categories**: Build, Deploy, Monitor
|
||||
6. **Environment Variables**: DEPLOY_ENV, AWS_REGION fallbacks
|
||||
7. **Shared State**: Context passed to all commands via metadata
|
||||
|
||||
## Context Pattern
|
||||
|
||||
```go
|
||||
type DeployContext struct {
|
||||
Environment string
|
||||
AWSRegion string
|
||||
Verbose bool
|
||||
}
|
||||
|
||||
// Store in Before hook
|
||||
ctx := &DeployContext{...}
|
||||
c.App.Metadata["ctx"] = ctx
|
||||
|
||||
// Retrieve in command
|
||||
ctx := c.App.Metadata["ctx"].(*DeployContext)
|
||||
```
|
||||
192
skills/cli-patterns/examples/deploy-cli/main.go
Normal file
192
skills/cli-patterns/examples/deploy-cli/main.go
Normal file
@@ -0,0 +1,192 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
type DeployContext struct {
|
||||
Environment string
|
||||
AWSRegion string
|
||||
Verbose bool
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := &cli.App{
|
||||
Name: "deploy",
|
||||
Usage: "Deployment automation CLI",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "env",
|
||||
Aliases: []string{"e"},
|
||||
Usage: "Target environment",
|
||||
EnvVars: []string{"DEPLOY_ENV"},
|
||||
Required: true,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "region",
|
||||
Usage: "AWS region",
|
||||
EnvVars: []string{"AWS_REGION"},
|
||||
Value: "us-east-1",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "verbose",
|
||||
Aliases: []string{"v"},
|
||||
Usage: "Enable verbose output",
|
||||
},
|
||||
},
|
||||
|
||||
Before: func(c *cli.Context) error {
|
||||
env := c.String("env")
|
||||
region := c.String("region")
|
||||
verbose := c.Bool("verbose")
|
||||
|
||||
if verbose {
|
||||
fmt.Println("🔧 Setting up deployment context...")
|
||||
}
|
||||
|
||||
// Validate environment
|
||||
validEnvs := []string{"dev", "staging", "production"}
|
||||
valid := false
|
||||
for _, e := range validEnvs {
|
||||
if env == e {
|
||||
valid = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !valid {
|
||||
return fmt.Errorf("invalid environment: %s (must be dev, staging, or production)", env)
|
||||
}
|
||||
|
||||
// Store context
|
||||
ctx := &DeployContext{
|
||||
Environment: env,
|
||||
AWSRegion: region,
|
||||
Verbose: verbose,
|
||||
}
|
||||
c.App.Metadata["ctx"] = ctx
|
||||
|
||||
if verbose {
|
||||
fmt.Printf("Environment: %s\n", env)
|
||||
fmt.Printf("Region: %s\n", region)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
|
||||
Commands: []*cli.Command{
|
||||
// Build category
|
||||
{
|
||||
Name: "build",
|
||||
Category: "Build",
|
||||
Usage: "Build application",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "tag",
|
||||
Usage: "Docker image tag",
|
||||
Value: "latest",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
ctx := c.App.Metadata["ctx"].(*DeployContext)
|
||||
tag := c.String("tag")
|
||||
|
||||
fmt.Printf("Building for environment: %s\n", ctx.Environment)
|
||||
fmt.Printf("Image tag: %s\n", tag)
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "test",
|
||||
Category: "Build",
|
||||
Usage: "Run tests",
|
||||
Action: func(c *cli.Context) error {
|
||||
fmt.Println("Running test suite...")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
|
||||
// Deploy category
|
||||
{
|
||||
Name: "deploy",
|
||||
Category: "Deploy",
|
||||
Usage: "Deploy application",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "auto-approve",
|
||||
Usage: "Skip confirmation prompt",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
ctx := c.App.Metadata["ctx"].(*DeployContext)
|
||||
autoApprove := c.Bool("auto-approve")
|
||||
|
||||
fmt.Printf("Deploying to %s in %s...\n", ctx.Environment, ctx.AWSRegion)
|
||||
|
||||
if !autoApprove {
|
||||
fmt.Print("Continue? (y/n): ")
|
||||
// In real app: read user input
|
||||
fmt.Println("y")
|
||||
}
|
||||
|
||||
fmt.Println("Deployment started...")
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "rollback",
|
||||
Category: "Deploy",
|
||||
Usage: "Rollback to previous version",
|
||||
Action: func(c *cli.Context) error {
|
||||
ctx := c.App.Metadata["ctx"].(*DeployContext)
|
||||
fmt.Printf("Rolling back %s deployment...\n", ctx.Environment)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
|
||||
// Monitor category
|
||||
{
|
||||
Name: "logs",
|
||||
Category: "Monitor",
|
||||
Usage: "View deployment logs",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "follow",
|
||||
Aliases: []string{"f"},
|
||||
Usage: "Follow log output",
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
follow := c.Bool("follow")
|
||||
fmt.Println("Fetching logs...")
|
||||
if follow {
|
||||
fmt.Println("Following logs (Ctrl+C to stop)...")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "status",
|
||||
Category: "Monitor",
|
||||
Usage: "Check deployment status",
|
||||
Action: func(c *cli.Context) error {
|
||||
ctx := c.App.Metadata["ctx"].(*DeployContext)
|
||||
fmt.Printf("Deployment Status (%s):\n", ctx.Environment)
|
||||
fmt.Println(" Status: Running")
|
||||
fmt.Println(" Instances: 3/3")
|
||||
fmt.Println(" Health: Healthy")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user