Initial commit
This commit is contained in:
113
skills/cobra-patterns/scripts/add-command.sh
Executable file
113
skills/cobra-patterns/scripts/add-command.sh
Executable file
@@ -0,0 +1,113 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Add a new command to existing Cobra CLI
|
||||
# Usage: ./add-command.sh <command-name> [--parent parent-command]
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
COMMAND_NAME="${1:-}"
|
||||
PARENT_CMD=""
|
||||
|
||||
# Parse arguments
|
||||
shift || true
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--parent)
|
||||
PARENT_CMD="$2"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "$COMMAND_NAME" ]; then
|
||||
echo "Error: Command name required"
|
||||
echo "Usage: $0 <command-name> [--parent parent-command]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "cmd" ]; then
|
||||
echo "Error: cmd/ directory not found. Run from CLI root directory."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Determine file location
|
||||
if [ -n "$PARENT_CMD" ]; then
|
||||
CMD_DIR="cmd/$PARENT_CMD"
|
||||
mkdir -p "$CMD_DIR"
|
||||
CMD_FILE="$CMD_DIR/$COMMAND_NAME.go"
|
||||
PACKAGE_NAME="$PARENT_CMD"
|
||||
else
|
||||
CMD_FILE="cmd/$COMMAND_NAME.go"
|
||||
PACKAGE_NAME="cmd"
|
||||
fi
|
||||
|
||||
if [ -f "$CMD_FILE" ]; then
|
||||
echo "Error: Command file already exists: $CMD_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create command file
|
||||
cat > "$CMD_FILE" << EOF
|
||||
package $PACKAGE_NAME
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
// Add command-specific flags here
|
||||
${COMMAND_NAME}Example string
|
||||
)
|
||||
|
||||
var ${COMMAND_NAME}Cmd = &cobra.Command{
|
||||
Use: "$COMMAND_NAME",
|
||||
Short: "Short description of $COMMAND_NAME",
|
||||
Long: \`Detailed description of the $COMMAND_NAME command.
|
||||
|
||||
This command does something useful. Add more details here.
|
||||
|
||||
Examples:
|
||||
mycli $COMMAND_NAME --example value\`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
fmt.Printf("Executing $COMMAND_NAME command\n")
|
||||
|
||||
// Add command logic here
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Define flags
|
||||
${COMMAND_NAME}Cmd.Flags().StringVar(&${COMMAND_NAME}Example, "example", "", "example flag")
|
||||
|
||||
// Register command
|
||||
EOF
|
||||
|
||||
if [ -n "$PARENT_CMD" ]; then
|
||||
cat >> "$CMD_FILE" << EOF
|
||||
${PARENT_CMD}Cmd.AddCommand(${COMMAND_NAME}Cmd)
|
||||
EOF
|
||||
else
|
||||
cat >> "$CMD_FILE" << EOF
|
||||
rootCmd.AddCommand(${COMMAND_NAME}Cmd)
|
||||
EOF
|
||||
fi
|
||||
|
||||
cat >> "$CMD_FILE" << EOF
|
||||
}
|
||||
EOF
|
||||
|
||||
echo "✓ Created command file: $CMD_FILE"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo "1. Update the command logic in $CMD_FILE"
|
||||
echo "2. Add any required flags"
|
||||
echo "3. Build and test: go build"
|
||||
echo ""
|
||||
82
skills/cobra-patterns/scripts/generate-completions.sh
Executable file
82
skills/cobra-patterns/scripts/generate-completions.sh
Executable file
@@ -0,0 +1,82 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Generate shell completion scripts for Cobra CLI
|
||||
# Usage: ./generate-completions.sh <cli-binary> [output-dir]
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
CLI_BINARY="${1:-}"
|
||||
OUTPUT_DIR="${2:-./completions}"
|
||||
|
||||
if [ -z "$CLI_BINARY" ]; then
|
||||
echo "Error: CLI binary path required"
|
||||
echo "Usage: $0 <cli-binary> [output-dir]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$CLI_BINARY" ]; then
|
||||
echo "Error: Binary not found: $CLI_BINARY"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -x "$CLI_BINARY" ]; then
|
||||
echo "Error: Binary is not executable: $CLI_BINARY"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
CLI_NAME=$(basename "$CLI_BINARY")
|
||||
|
||||
echo "Generating shell completions for $CLI_NAME..."
|
||||
echo ""
|
||||
|
||||
# Generate Bash completion
|
||||
if "$CLI_BINARY" completion bash > "$OUTPUT_DIR/$CLI_NAME.bash" 2>/dev/null; then
|
||||
echo "✓ Bash completion: $OUTPUT_DIR/$CLI_NAME.bash"
|
||||
echo " Install: source $OUTPUT_DIR/$CLI_NAME.bash"
|
||||
else
|
||||
echo "⚠ Bash completion not available"
|
||||
fi
|
||||
|
||||
# Generate Zsh completion
|
||||
if "$CLI_BINARY" completion zsh > "$OUTPUT_DIR/_$CLI_NAME" 2>/dev/null; then
|
||||
echo "✓ Zsh completion: $OUTPUT_DIR/_$CLI_NAME"
|
||||
echo " Install: Place in \$fpath directory"
|
||||
else
|
||||
echo "⚠ Zsh completion not available"
|
||||
fi
|
||||
|
||||
# Generate Fish completion
|
||||
if "$CLI_BINARY" completion fish > "$OUTPUT_DIR/$CLI_NAME.fish" 2>/dev/null; then
|
||||
echo "✓ Fish completion: $OUTPUT_DIR/$CLI_NAME.fish"
|
||||
echo " Install: source $OUTPUT_DIR/$CLI_NAME.fish"
|
||||
else
|
||||
echo "⚠ Fish completion not available"
|
||||
fi
|
||||
|
||||
# Generate PowerShell completion
|
||||
if "$CLI_BINARY" completion powershell > "$OUTPUT_DIR/$CLI_NAME.ps1" 2>/dev/null; then
|
||||
echo "✓ PowerShell completion: $OUTPUT_DIR/$CLI_NAME.ps1"
|
||||
echo " Install: & $OUTPUT_DIR/$CLI_NAME.ps1"
|
||||
else
|
||||
echo "⚠ PowerShell completion not available"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Completions generated in: $OUTPUT_DIR"
|
||||
echo ""
|
||||
echo "Installation instructions:"
|
||||
echo ""
|
||||
echo "Bash:"
|
||||
echo " echo 'source $OUTPUT_DIR/$CLI_NAME.bash' >> ~/.bashrc"
|
||||
echo ""
|
||||
echo "Zsh:"
|
||||
echo " mkdir -p ~/.zsh/completions"
|
||||
echo " cp $OUTPUT_DIR/_$CLI_NAME ~/.zsh/completions/"
|
||||
echo " Add to ~/.zshrc: fpath=(~/.zsh/completions \$fpath)"
|
||||
echo ""
|
||||
echo "Fish:"
|
||||
echo " mkdir -p ~/.config/fish/completions"
|
||||
echo " cp $OUTPUT_DIR/$CLI_NAME.fish ~/.config/fish/completions/"
|
||||
echo ""
|
||||
566
skills/cobra-patterns/scripts/setup-cobra-cli.sh
Executable file
566
skills/cobra-patterns/scripts/setup-cobra-cli.sh
Executable file
@@ -0,0 +1,566 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Setup Cobra CLI with chosen structure pattern
|
||||
# Usage: ./setup-cobra-cli.sh <cli-name> <structure-type>
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
CLI_NAME="${1:-}"
|
||||
STRUCTURE_TYPE="${2:-flat}"
|
||||
|
||||
if [ -z "$CLI_NAME" ]; then
|
||||
echo "Error: CLI name required"
|
||||
echo "Usage: $0 <cli-name> <structure-type>"
|
||||
echo "Structure types: simple, flat, nested, plugin, hybrid"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate structure type
|
||||
case "$STRUCTURE_TYPE" in
|
||||
simple|flat|nested|plugin|hybrid)
|
||||
;;
|
||||
*)
|
||||
echo "Error: Invalid structure type: $STRUCTURE_TYPE"
|
||||
echo "Valid types: simple, flat, nested, plugin, hybrid"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "Creating Cobra CLI: $CLI_NAME with $STRUCTURE_TYPE structure..."
|
||||
|
||||
# Create directory structure
|
||||
mkdir -p "$CLI_NAME"
|
||||
cd "$CLI_NAME"
|
||||
|
||||
# Initialize Go module
|
||||
go mod init "$CLI_NAME" 2>/dev/null || echo "Go module already initialized"
|
||||
|
||||
# Create base directories
|
||||
mkdir -p cmd
|
||||
mkdir -p internal
|
||||
|
||||
# Install Cobra
|
||||
echo "Installing Cobra dependency..."
|
||||
go get -u github.com/spf13/cobra@latest
|
||||
|
||||
case "$STRUCTURE_TYPE" in
|
||||
simple)
|
||||
# Single command CLI
|
||||
cat > main.go << 'EOF'
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
verbose bool
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "CLI_NAME",
|
||||
Short: "A simple CLI tool",
|
||||
Long: `A simple command-line tool built with Cobra.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if verbose {
|
||||
fmt.Println("Running in verbose mode")
|
||||
}
|
||||
fmt.Println("Hello from CLI_NAME!")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "verbose output")
|
||||
}
|
||||
|
||||
func main() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
EOF
|
||||
sed -i "s/CLI_NAME/$CLI_NAME/g" main.go
|
||||
;;
|
||||
|
||||
flat)
|
||||
# Root with subcommands at one level
|
||||
cat > cmd/root.go << 'EOF'
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
cfgFile string
|
||||
verbose bool
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "CLI_NAME",
|
||||
Short: "A CLI tool with flat command structure",
|
||||
Long: `A command-line tool with subcommands at a single level.`,
|
||||
}
|
||||
|
||||
func Execute() error {
|
||||
return rootCmd.Execute()
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file")
|
||||
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose output")
|
||||
}
|
||||
EOF
|
||||
|
||||
cat > cmd/get.go << 'EOF'
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var getCmd = &cobra.Command{
|
||||
Use: "get [resource]",
|
||||
Short: "Get resources",
|
||||
Long: `Retrieve and display resources`,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
fmt.Printf("Getting resource: %s\n", args[0])
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(getCmd)
|
||||
}
|
||||
EOF
|
||||
|
||||
cat > cmd/create.go << 'EOF'
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
createName string
|
||||
)
|
||||
|
||||
var createCmd = &cobra.Command{
|
||||
Use: "create",
|
||||
Short: "Create resources",
|
||||
Long: `Create new resources`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if createName == "" {
|
||||
return fmt.Errorf("name is required")
|
||||
}
|
||||
fmt.Printf("Creating resource: %s\n", createName)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
createCmd.Flags().StringVarP(&createName, "name", "n", "", "resource name (required)")
|
||||
createCmd.MarkFlagRequired("name")
|
||||
rootCmd.AddCommand(createCmd)
|
||||
}
|
||||
EOF
|
||||
|
||||
cat > main.go << 'EOF'
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"CLI_NAME/cmd"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := cmd.Execute(); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
EOF
|
||||
sed -i "s/CLI_NAME/$CLI_NAME/g" main.go cmd/root.go
|
||||
;;
|
||||
|
||||
nested)
|
||||
# kubectl-style nested commands
|
||||
mkdir -p cmd/get cmd/create cmd/delete
|
||||
|
||||
cat > cmd/root.go << 'EOF'
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
cfgFile string
|
||||
verbose bool
|
||||
output string
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "CLI_NAME",
|
||||
Short: "A production-grade CLI tool",
|
||||
Long: `A complete CLI application with nested command structure.
|
||||
|
||||
This CLI demonstrates kubectl-style command organization with
|
||||
hierarchical commands and consistent flag handling.`,
|
||||
}
|
||||
|
||||
func Execute() error {
|
||||
return rootCmd.Execute()
|
||||
}
|
||||
|
||||
func init() {
|
||||
cobra.OnInitialize(initConfig)
|
||||
|
||||
// Global flags
|
||||
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file")
|
||||
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose output")
|
||||
rootCmd.PersistentFlags().StringVarP(&output, "output", "o", "text", "output format (text|json|yaml)")
|
||||
|
||||
// Command groups
|
||||
rootCmd.AddGroup(&cobra.Group{
|
||||
ID: "basic",
|
||||
Title: "Basic Commands:",
|
||||
})
|
||||
rootCmd.AddGroup(&cobra.Group{
|
||||
ID: "management",
|
||||
Title: "Management Commands:",
|
||||
})
|
||||
}
|
||||
|
||||
func initConfig() {
|
||||
if verbose {
|
||||
fmt.Fprintln(os.Stderr, "Verbose mode enabled")
|
||||
}
|
||||
if cfgFile != "" {
|
||||
fmt.Fprintf(os.Stderr, "Using config file: %s\n", cfgFile)
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
cat > cmd/get/get.go << 'EOF'
|
||||
package get
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var GetCmd = &cobra.Command{
|
||||
Use: "get",
|
||||
Short: "Display resources",
|
||||
Long: `Display one or many resources`,
|
||||
GroupID: "basic",
|
||||
}
|
||||
|
||||
func init() {
|
||||
GetCmd.AddCommand(podsCmd)
|
||||
GetCmd.AddCommand(servicesCmd)
|
||||
}
|
||||
EOF
|
||||
|
||||
cat > cmd/get/pods.go << 'EOF'
|
||||
package get
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
namespace string
|
||||
allNamespaces bool
|
||||
)
|
||||
|
||||
var podsCmd = &cobra.Command{
|
||||
Use: "pods [NAME]",
|
||||
Short: "Display pods",
|
||||
Long: `Display one or many pods`,
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if allNamespaces {
|
||||
fmt.Println("Listing pods in all namespaces")
|
||||
} else {
|
||||
fmt.Printf("Listing pods in namespace: %s\n", namespace)
|
||||
}
|
||||
if len(args) > 0 {
|
||||
fmt.Printf("Showing pod: %s\n", args[0])
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
podsCmd.Flags().StringVarP(&namespace, "namespace", "n", "default", "namespace")
|
||||
podsCmd.Flags().BoolVarP(&allNamespaces, "all-namespaces", "A", false, "list across all namespaces")
|
||||
}
|
||||
EOF
|
||||
|
||||
cat > cmd/get/services.go << 'EOF'
|
||||
package get
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var servicesCmd = &cobra.Command{
|
||||
Use: "services [NAME]",
|
||||
Short: "Display services",
|
||||
Long: `Display one or many services`,
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
fmt.Println("Listing services")
|
||||
if len(args) > 0 {
|
||||
fmt.Printf("Showing service: %s\n", args[0])
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
EOF
|
||||
|
||||
cat > cmd/create/create.go << 'EOF'
|
||||
package create
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var CreateCmd = &cobra.Command{
|
||||
Use: "create",
|
||||
Short: "Create resources",
|
||||
Long: `Create resources from files or stdin`,
|
||||
GroupID: "management",
|
||||
}
|
||||
|
||||
func init() {
|
||||
CreateCmd.AddCommand(deploymentCmd)
|
||||
}
|
||||
EOF
|
||||
|
||||
cat > cmd/create/deployment.go << 'EOF'
|
||||
package create
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
image string
|
||||
replicas int
|
||||
)
|
||||
|
||||
var deploymentCmd = &cobra.Command{
|
||||
Use: "deployment NAME",
|
||||
Short: "Create a deployment",
|
||||
Long: `Create a deployment with the specified name`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
name := args[0]
|
||||
fmt.Printf("Creating deployment: %s\n", name)
|
||||
fmt.Printf(" Image: %s\n", image)
|
||||
fmt.Printf(" Replicas: %d\n", replicas)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
deploymentCmd.Flags().StringVar(&image, "image", "", "container image (required)")
|
||||
deploymentCmd.Flags().IntVar(&replicas, "replicas", 1, "number of replicas")
|
||||
deploymentCmd.MarkFlagRequired("image")
|
||||
}
|
||||
EOF
|
||||
|
||||
cat > cmd/delete/delete.go << 'EOF'
|
||||
package delete
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
force bool
|
||||
)
|
||||
|
||||
var DeleteCmd = &cobra.Command{
|
||||
Use: "delete",
|
||||
Short: "Delete resources",
|
||||
Long: `Delete resources by names, stdin, or resources`,
|
||||
GroupID: "management",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
for _, resource := range args {
|
||||
if force {
|
||||
fmt.Printf("Force deleting: %s\n", resource)
|
||||
} else {
|
||||
fmt.Printf("Deleting: %s\n", resource)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
DeleteCmd.Flags().BoolVarP(&force, "force", "f", false, "force deletion")
|
||||
}
|
||||
EOF
|
||||
|
||||
# Update root to add nested commands
|
||||
cat >> cmd/root.go << 'EOF'
|
||||
|
||||
func init() {
|
||||
// Add command imports at the top of your root.go:
|
||||
// import (
|
||||
// "CLI_NAME/cmd/get"
|
||||
// "CLI_NAME/cmd/create"
|
||||
// "CLI_NAME/cmd/delete"
|
||||
// )
|
||||
|
||||
// Uncomment after fixing imports:
|
||||
// rootCmd.AddCommand(get.GetCmd)
|
||||
// rootCmd.AddCommand(create.CreateCmd)
|
||||
// rootCmd.AddCommand(delete.DeleteCmd)
|
||||
}
|
||||
EOF
|
||||
|
||||
cat > main.go << 'EOF'
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"CLI_NAME/cmd"
|
||||
_ "CLI_NAME/cmd/get"
|
||||
_ "CLI_NAME/cmd/create"
|
||||
_ "CLI_NAME/cmd/delete"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := cmd.Execute(); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
EOF
|
||||
sed -i "s/CLI_NAME/$CLI_NAME/g" main.go cmd/root.go
|
||||
;;
|
||||
|
||||
plugin)
|
||||
echo "Plugin structure not yet implemented"
|
||||
exit 1
|
||||
;;
|
||||
|
||||
hybrid)
|
||||
echo "Hybrid structure not yet implemented"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Create .gitignore
|
||||
cat > .gitignore << 'EOF'
|
||||
# Binaries
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
/CLI_NAME
|
||||
|
||||
# Test binary
|
||||
*.test
|
||||
|
||||
# Coverage
|
||||
*.out
|
||||
*.prof
|
||||
|
||||
# Go workspace
|
||||
go.work
|
||||
go.work.sum
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
EOF
|
||||
sed -i "s/CLI_NAME/$CLI_NAME/g" .gitignore
|
||||
|
||||
# Create README
|
||||
cat > README.md << 'EOF'
|
||||
# CLI_NAME
|
||||
|
||||
A CLI tool built with Cobra.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
go install
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
CLI_NAME --help
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
Build:
|
||||
```bash
|
||||
go build -o CLI_NAME
|
||||
```
|
||||
|
||||
Run:
|
||||
```bash
|
||||
./CLI_NAME
|
||||
```
|
||||
|
||||
Test:
|
||||
```bash
|
||||
go test ./...
|
||||
```
|
||||
|
||||
## Structure
|
||||
|
||||
This CLI uses STRUCTURE_TYPE command structure.
|
||||
EOF
|
||||
sed -i "s/CLI_NAME/$CLI_NAME/g" README.md
|
||||
sed -i "s/STRUCTURE_TYPE/$STRUCTURE_TYPE/g" README.md
|
||||
|
||||
# Initialize dependencies
|
||||
echo "Downloading dependencies..."
|
||||
go mod tidy
|
||||
|
||||
echo ""
|
||||
echo "✓ CLI created successfully: $CLI_NAME"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " cd $CLI_NAME"
|
||||
echo " go build -o $CLI_NAME"
|
||||
echo " ./$CLI_NAME --help"
|
||||
echo ""
|
||||
181
skills/cobra-patterns/scripts/validate-cobra-cli.sh
Executable file
181
skills/cobra-patterns/scripts/validate-cobra-cli.sh
Executable file
@@ -0,0 +1,181 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Validate Cobra CLI structure and patterns
|
||||
# Usage: ./validate-cobra-cli.sh <cli-directory>
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
CLI_DIR="${1:-.}"
|
||||
|
||||
if [ ! -d "$CLI_DIR" ]; then
|
||||
echo "Error: Directory not found: $CLI_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Validating Cobra CLI structure in: $CLI_DIR"
|
||||
echo ""
|
||||
|
||||
ERRORS=0
|
||||
WARNINGS=0
|
||||
|
||||
# Check Go module
|
||||
if [ ! -f "$CLI_DIR/go.mod" ]; then
|
||||
echo "❌ ERROR: go.mod not found"
|
||||
((ERRORS++))
|
||||
else
|
||||
echo "✓ go.mod found"
|
||||
fi
|
||||
|
||||
# Check main.go
|
||||
if [ ! -f "$CLI_DIR/main.go" ]; then
|
||||
echo "❌ ERROR: main.go not found"
|
||||
((ERRORS++))
|
||||
else
|
||||
echo "✓ main.go found"
|
||||
|
||||
# Check if main.go has proper structure
|
||||
if ! grep -q "func main()" "$CLI_DIR/main.go"; then
|
||||
echo "❌ ERROR: main() function not found in main.go"
|
||||
((ERRORS++))
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check cmd directory
|
||||
if [ ! -d "$CLI_DIR/cmd" ]; then
|
||||
echo "⚠ WARNING: cmd/ directory not found (acceptable for simple CLIs)"
|
||||
((WARNINGS++))
|
||||
else
|
||||
echo "✓ cmd/ directory found"
|
||||
|
||||
# Check root command
|
||||
if [ -f "$CLI_DIR/cmd/root.go" ]; then
|
||||
echo "✓ cmd/root.go found"
|
||||
|
||||
# Validate root command structure
|
||||
if ! grep -q "var rootCmd" "$CLI_DIR/cmd/root.go"; then
|
||||
echo "❌ ERROR: rootCmd variable not found in root.go"
|
||||
((ERRORS++))
|
||||
fi
|
||||
|
||||
if ! grep -q "func Execute()" "$CLI_DIR/cmd/root.go"; then
|
||||
echo "❌ ERROR: Execute() function not found in root.go"
|
||||
((ERRORS++))
|
||||
fi
|
||||
else
|
||||
echo "⚠ WARNING: cmd/root.go not found"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check for Cobra dependency
|
||||
if [ -f "$CLI_DIR/go.mod" ]; then
|
||||
if ! grep -q "github.com/spf13/cobra" "$CLI_DIR/go.mod"; then
|
||||
echo "❌ ERROR: Cobra dependency not found in go.mod"
|
||||
((ERRORS++))
|
||||
else
|
||||
echo "✓ Cobra dependency found"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Validate command files have proper structure
|
||||
if [ -d "$CLI_DIR/cmd" ]; then
|
||||
for cmd_file in "$CLI_DIR/cmd"/*.go; do
|
||||
if [ -f "$cmd_file" ]; then
|
||||
filename=$(basename "$cmd_file")
|
||||
|
||||
# Check for command variable
|
||||
if grep -q "var.*Cmd = &cobra.Command" "$cmd_file"; then
|
||||
echo "✓ Command structure found in $filename"
|
||||
|
||||
# Check for Use field
|
||||
if ! grep -A5 "var.*Cmd = &cobra.Command" "$cmd_file" | grep -q "Use:"; then
|
||||
echo "⚠ WARNING: Use field missing in $filename"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
|
||||
# Check for Short description
|
||||
if ! grep -A10 "var.*Cmd = &cobra.Command" "$cmd_file" | grep -q "Short:"; then
|
||||
echo "⚠ WARNING: Short description missing in $filename"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
|
||||
# Check for Run or RunE
|
||||
if ! grep -A15 "var.*Cmd = &cobra.Command" "$cmd_file" | grep -qE "Run:|RunE:"; then
|
||||
echo "⚠ WARNING: Run/RunE function missing in $filename"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
|
||||
# Prefer RunE over Run for error handling
|
||||
if grep -A15 "var.*Cmd = &cobra.Command" "$cmd_file" | grep -q "Run:" && \
|
||||
! grep -A15 "var.*Cmd = &cobra.Command" "$cmd_file" | grep -q "RunE:"; then
|
||||
echo "⚠ WARNING: Consider using RunE instead of Run in $filename for better error handling"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Check for .gitignore
|
||||
if [ ! -f "$CLI_DIR/.gitignore" ]; then
|
||||
echo "⚠ WARNING: .gitignore not found"
|
||||
((WARNINGS++))
|
||||
else
|
||||
echo "✓ .gitignore found"
|
||||
fi
|
||||
|
||||
# Check for README
|
||||
if [ ! -f "$CLI_DIR/README.md" ]; then
|
||||
echo "⚠ WARNING: README.md not found"
|
||||
((WARNINGS++))
|
||||
else
|
||||
echo "✓ README.md found"
|
||||
fi
|
||||
|
||||
# Check if Go code compiles
|
||||
echo ""
|
||||
echo "Checking if code compiles..."
|
||||
cd "$CLI_DIR"
|
||||
if go build -o /tmp/cobra-cli-test 2>&1 | head -20; then
|
||||
echo "✓ Code compiles successfully"
|
||||
rm -f /tmp/cobra-cli-test
|
||||
else
|
||||
echo "❌ ERROR: Code does not compile"
|
||||
((ERRORS++))
|
||||
fi
|
||||
|
||||
# Check for common anti-patterns
|
||||
echo ""
|
||||
echo "Checking for anti-patterns..."
|
||||
|
||||
# Check for os.Exit in command handlers
|
||||
if grep -r "os.Exit" "$CLI_DIR/cmd" 2>/dev/null | grep -v "import" | grep -v "//"; then
|
||||
echo "⚠ WARNING: Found os.Exit() in command handlers - prefer returning errors"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
|
||||
# Check for panic in command handlers
|
||||
if grep -r "panic(" "$CLI_DIR/cmd" 2>/dev/null | grep -v "import" | grep -v "//"; then
|
||||
echo "⚠ WARNING: Found panic() in command handlers - prefer returning errors"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
echo "================================"
|
||||
echo "Validation Summary"
|
||||
echo "================================"
|
||||
echo "Errors: $ERRORS"
|
||||
echo "Warnings: $WARNINGS"
|
||||
echo ""
|
||||
|
||||
if [ $ERRORS -eq 0 ]; then
|
||||
echo "✓ Validation passed!"
|
||||
if [ $WARNINGS -gt 0 ]; then
|
||||
echo " ($WARNINGS warnings to review)"
|
||||
fi
|
||||
exit 0
|
||||
else
|
||||
echo "❌ Validation failed with $ERRORS errors"
|
||||
exit 1
|
||||
fi
|
||||
Reference in New Issue
Block a user