Initial commit
This commit is contained in:
334
skills/click-patterns/scripts/generate-click-cli.sh
Executable file
334
skills/click-patterns/scripts/generate-click-cli.sh
Executable file
@@ -0,0 +1,334 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# generate-click-cli.sh - Generate Click CLI project structure
|
||||
#
|
||||
# Usage: generate-click-cli.sh <project-name> [cli-type]
|
||||
# cli-type: basic, nested, or advanced (default: basic)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Function to print colored output
|
||||
print_info() { echo -e "${CYAN}ℹ${NC} $1"; }
|
||||
print_success() { echo -e "${GREEN}✓${NC} $1"; }
|
||||
print_error() { echo -e "${RED}✗${NC} $1" >&2; }
|
||||
print_warning() { echo -e "${YELLOW}⚠${NC} $1"; }
|
||||
|
||||
# Validate arguments
|
||||
if [ $# -lt 1 ]; then
|
||||
print_error "Usage: $0 <project-name> [cli-type]"
|
||||
echo " cli-type: basic, nested, or advanced (default: basic)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PROJECT_NAME="$1"
|
||||
CLI_TYPE="${2:-basic}"
|
||||
|
||||
# Validate CLI type
|
||||
if [[ ! "$CLI_TYPE" =~ ^(basic|nested|advanced)$ ]]; then
|
||||
print_error "Invalid CLI type: $CLI_TYPE"
|
||||
echo " Valid types: basic, nested, advanced"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate project name
|
||||
if [[ ! "$PROJECT_NAME" =~ ^[a-z0-9_-]+$ ]]; then
|
||||
print_error "Invalid project name: $PROJECT_NAME"
|
||||
echo " Must contain only lowercase letters, numbers, hyphens, and underscores"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create project directory
|
||||
if [ -d "$PROJECT_NAME" ]; then
|
||||
print_error "Directory already exists: $PROJECT_NAME"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_info "Creating Click CLI project: $PROJECT_NAME (type: $CLI_TYPE)"
|
||||
|
||||
# Create directory structure
|
||||
mkdir -p "$PROJECT_NAME"/{src,tests,docs}
|
||||
|
||||
# Determine which template to use
|
||||
SKILL_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
TEMPLATE_FILE=""
|
||||
|
||||
case "$CLI_TYPE" in
|
||||
basic)
|
||||
TEMPLATE_FILE="$SKILL_DIR/templates/basic-cli.py"
|
||||
;;
|
||||
nested)
|
||||
TEMPLATE_FILE="$SKILL_DIR/templates/nested-commands.py"
|
||||
;;
|
||||
advanced)
|
||||
# For advanced, use nested as base with validators
|
||||
TEMPLATE_FILE="$SKILL_DIR/templates/nested-commands.py"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Copy template
|
||||
if [ ! -f "$TEMPLATE_FILE" ]; then
|
||||
print_error "Template file not found: $TEMPLATE_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cp "$TEMPLATE_FILE" "$PROJECT_NAME/src/cli.py"
|
||||
print_success "Created src/cli.py from template"
|
||||
|
||||
# Copy validators if advanced type
|
||||
if [ "$CLI_TYPE" = "advanced" ]; then
|
||||
VALIDATORS_FILE="$SKILL_DIR/templates/validators.py"
|
||||
if [ -f "$VALIDATORS_FILE" ]; then
|
||||
cp "$VALIDATORS_FILE" "$PROJECT_NAME/src/validators.py"
|
||||
print_success "Created src/validators.py"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Create __init__.py
|
||||
cat > "$PROJECT_NAME/src/__init__.py" <<'EOF'
|
||||
"""
|
||||
CLI application package
|
||||
"""
|
||||
from .cli import cli
|
||||
|
||||
__version__ = "1.0.0"
|
||||
__all__ = ["cli"]
|
||||
EOF
|
||||
print_success "Created src/__init__.py"
|
||||
|
||||
# Create requirements.txt
|
||||
cat > "$PROJECT_NAME/requirements.txt" <<'EOF'
|
||||
click>=8.0.0
|
||||
rich>=13.0.0
|
||||
EOF
|
||||
print_success "Created requirements.txt"
|
||||
|
||||
# Create setup.py
|
||||
cat > "$PROJECT_NAME/setup.py" <<EOF
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name="${PROJECT_NAME}",
|
||||
version="1.0.0",
|
||||
packages=find_packages(),
|
||||
install_requires=[
|
||||
"click>=8.0.0",
|
||||
"rich>=13.0.0",
|
||||
],
|
||||
entry_points={
|
||||
"console_scripts": [
|
||||
"${PROJECT_NAME}=src.cli:cli",
|
||||
],
|
||||
},
|
||||
python_requires=">=3.8",
|
||||
)
|
||||
EOF
|
||||
print_success "Created setup.py"
|
||||
|
||||
# Create pyproject.toml
|
||||
cat > "$PROJECT_NAME/pyproject.toml" <<EOF
|
||||
[build-system]
|
||||
requires = ["setuptools>=45", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "${PROJECT_NAME}"
|
||||
version = "1.0.0"
|
||||
description = "A Click-based CLI tool"
|
||||
requires-python = ">=3.8"
|
||||
dependencies = [
|
||||
"click>=8.0.0",
|
||||
"rich>=13.0.0",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
${PROJECT_NAME} = "src.cli:cli"
|
||||
EOF
|
||||
print_success "Created pyproject.toml"
|
||||
|
||||
# Create README.md
|
||||
cat > "$PROJECT_NAME/README.md" <<EOF
|
||||
# ${PROJECT_NAME}
|
||||
|
||||
A CLI tool built with Click framework.
|
||||
|
||||
## Installation
|
||||
|
||||
\`\`\`bash
|
||||
pip install -e .
|
||||
\`\`\`
|
||||
|
||||
## Usage
|
||||
|
||||
\`\`\`bash
|
||||
# Show help
|
||||
${PROJECT_NAME} --help
|
||||
|
||||
# Run command
|
||||
${PROJECT_NAME} <command>
|
||||
\`\`\`
|
||||
|
||||
## Development
|
||||
|
||||
\`\`\`bash
|
||||
# Install in development mode
|
||||
pip install -e .
|
||||
|
||||
# Run tests
|
||||
pytest tests/
|
||||
|
||||
# Format code
|
||||
black src/ tests/
|
||||
|
||||
# Lint code
|
||||
pylint src/ tests/
|
||||
\`\`\`
|
||||
|
||||
## Project Structure
|
||||
|
||||
\`\`\`
|
||||
${PROJECT_NAME}/
|
||||
├── src/
|
||||
│ ├── __init__.py
|
||||
│ └── cli.py # Main CLI implementation
|
||||
├── tests/
|
||||
│ └── test_cli.py # Unit tests
|
||||
├── docs/
|
||||
│ └── usage.md # Usage documentation
|
||||
├── requirements.txt # Dependencies
|
||||
├── setup.py # Setup configuration
|
||||
└── README.md # This file
|
||||
\`\`\`
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
EOF
|
||||
print_success "Created README.md"
|
||||
|
||||
# Create basic test file
|
||||
cat > "$PROJECT_NAME/tests/test_cli.py" <<'EOF'
|
||||
import pytest
|
||||
from click.testing import CliRunner
|
||||
from src.cli import cli
|
||||
|
||||
|
||||
def test_cli_help():
|
||||
"""Test CLI help output"""
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(cli, ['--help'])
|
||||
assert result.exit_code == 0
|
||||
assert 'Usage:' in result.output
|
||||
|
||||
|
||||
def test_cli_version():
|
||||
"""Test CLI version output"""
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(cli, ['--version'])
|
||||
assert result.exit_code == 0
|
||||
assert '1.0.0' in result.output
|
||||
EOF
|
||||
print_success "Created tests/test_cli.py"
|
||||
|
||||
# Create .gitignore
|
||||
cat > "$PROJECT_NAME/.gitignore" <<'EOF'
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.so
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
|
||||
# Virtual environments
|
||||
venv/
|
||||
ENV/
|
||||
env/
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# Testing
|
||||
.pytest_cache/
|
||||
.coverage
|
||||
htmlcov/
|
||||
|
||||
# Environment
|
||||
.env
|
||||
.env.local
|
||||
EOF
|
||||
print_success "Created .gitignore"
|
||||
|
||||
# Create usage documentation
|
||||
cat > "$PROJECT_NAME/docs/usage.md" <<EOF
|
||||
# ${PROJECT_NAME} Usage Guide
|
||||
|
||||
## Installation
|
||||
|
||||
Install the CLI tool:
|
||||
|
||||
\`\`\`bash
|
||||
pip install -e .
|
||||
\`\`\`
|
||||
|
||||
## Commands
|
||||
|
||||
### Help
|
||||
|
||||
Show available commands:
|
||||
|
||||
\`\`\`bash
|
||||
${PROJECT_NAME} --help
|
||||
\`\`\`
|
||||
|
||||
### Version
|
||||
|
||||
Show version information:
|
||||
|
||||
\`\`\`bash
|
||||
${PROJECT_NAME} --version
|
||||
\`\`\`
|
||||
|
||||
## Examples
|
||||
|
||||
Add specific examples for your CLI commands here.
|
||||
EOF
|
||||
print_success "Created docs/usage.md"
|
||||
|
||||
# Print summary
|
||||
echo ""
|
||||
print_success "Click CLI project created successfully!"
|
||||
echo ""
|
||||
print_info "Next steps:"
|
||||
echo " 1. cd $PROJECT_NAME"
|
||||
echo " 2. python -m venv venv"
|
||||
echo " 3. source venv/bin/activate"
|
||||
echo " 4. pip install -e ."
|
||||
echo " 5. $PROJECT_NAME --help"
|
||||
echo ""
|
||||
print_info "Project type: $CLI_TYPE"
|
||||
print_info "Location: $(pwd)/$PROJECT_NAME"
|
||||
108
skills/click-patterns/scripts/setup-click-project.sh
Executable file
108
skills/click-patterns/scripts/setup-click-project.sh
Executable file
@@ -0,0 +1,108 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# setup-click-project.sh - Setup Click project dependencies and environment
|
||||
#
|
||||
# Usage: setup-click-project.sh [project-directory]
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Function to print colored output
|
||||
print_info() { echo -e "${CYAN}ℹ${NC} $1"; }
|
||||
print_success() { echo -e "${GREEN}✓${NC} $1"; }
|
||||
print_error() { echo -e "${RED}✗${NC} $1" >&2; }
|
||||
print_warning() { echo -e "${YELLOW}⚠${NC} $1"; }
|
||||
|
||||
PROJECT_DIR="${1:-.}"
|
||||
|
||||
print_info "Setting up Click project in: $PROJECT_DIR"
|
||||
|
||||
# Check if Python is installed
|
||||
if ! command -v python3 &> /dev/null; then
|
||||
print_error "Python 3 is not installed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PYTHON_VERSION=$(python3 --version | cut -d' ' -f2)
|
||||
print_success "Python $PYTHON_VERSION detected"
|
||||
|
||||
# Navigate to project directory
|
||||
cd "$PROJECT_DIR"
|
||||
|
||||
# Check if virtual environment exists
|
||||
if [ ! -d "venv" ]; then
|
||||
print_info "Creating virtual environment..."
|
||||
python3 -m venv venv
|
||||
print_success "Virtual environment created"
|
||||
else
|
||||
print_info "Virtual environment already exists"
|
||||
fi
|
||||
|
||||
# Activate virtual environment
|
||||
print_info "Activating virtual environment..."
|
||||
source venv/bin/activate
|
||||
|
||||
# Upgrade pip
|
||||
print_info "Upgrading pip..."
|
||||
pip install --upgrade pip > /dev/null 2>&1
|
||||
print_success "pip upgraded"
|
||||
|
||||
# Install Click and dependencies
|
||||
print_info "Installing Click and dependencies..."
|
||||
if [ -f "requirements.txt" ]; then
|
||||
pip install -r requirements.txt
|
||||
print_success "Installed from requirements.txt"
|
||||
else
|
||||
pip install click rich
|
||||
print_success "Installed click and rich"
|
||||
fi
|
||||
|
||||
# Install development dependencies
|
||||
print_info "Installing development dependencies..."
|
||||
pip install pytest pytest-cov black pylint mypy
|
||||
print_success "Development dependencies installed"
|
||||
|
||||
# Create .env.example if it doesn't exist
|
||||
if [ ! -f ".env.example" ]; then
|
||||
cat > .env.example <<'EOF'
|
||||
# Environment variables for CLI
|
||||
API_KEY=your_api_key_here
|
||||
DEBUG=false
|
||||
LOG_LEVEL=info
|
||||
EOF
|
||||
print_success "Created .env.example"
|
||||
fi
|
||||
|
||||
# Setup pre-commit hooks if git repo
|
||||
if [ -d ".git" ]; then
|
||||
print_info "Setting up git hooks..."
|
||||
cat > .git/hooks/pre-commit <<'EOF'
|
||||
#!/bin/bash
|
||||
# Run tests before commit
|
||||
source venv/bin/activate
|
||||
black src/ tests/ --check || exit 1
|
||||
pylint src/ || exit 1
|
||||
pytest tests/ || exit 1
|
||||
EOF
|
||||
chmod +x .git/hooks/pre-commit
|
||||
print_success "Git hooks configured"
|
||||
fi
|
||||
|
||||
# Verify installation
|
||||
print_info "Verifying installation..."
|
||||
python3 -c "import click; print(f'Click version: {click.__version__}')"
|
||||
print_success "Click is properly installed"
|
||||
|
||||
echo ""
|
||||
print_success "Setup completed successfully!"
|
||||
echo ""
|
||||
print_info "Next steps:"
|
||||
echo " 1. source venv/bin/activate"
|
||||
echo " 2. python src/cli.py --help"
|
||||
echo " 3. pytest tests/"
|
||||
162
skills/click-patterns/scripts/validate-click.sh
Executable file
162
skills/click-patterns/scripts/validate-click.sh
Executable file
@@ -0,0 +1,162 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# validate-click.sh - Validate Click CLI implementation
|
||||
#
|
||||
# Usage: validate-click.sh <cli-file.py>
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Function to print colored output
|
||||
print_info() { echo -e "${CYAN}ℹ${NC} $1"; }
|
||||
print_success() { echo -e "${GREEN}✓${NC} $1"; }
|
||||
print_error() { echo -e "${RED}✗${NC} $1" >&2; }
|
||||
print_warning() { echo -e "${YELLOW}⚠${NC} $1"; }
|
||||
|
||||
# Validate arguments
|
||||
if [ $# -lt 1 ]; then
|
||||
print_error "Usage: $0 <cli-file.py>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CLI_FILE="$1"
|
||||
|
||||
# Check if file exists
|
||||
if [ ! -f "$CLI_FILE" ]; then
|
||||
print_error "File not found: $CLI_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_info "Validating Click CLI: $CLI_FILE"
|
||||
echo ""
|
||||
|
||||
VALIDATION_PASSED=true
|
||||
|
||||
# Check 1: File is a Python file
|
||||
if [[ ! "$CLI_FILE" =~ \.py$ ]]; then
|
||||
print_error "File must be a Python file (.py)"
|
||||
VALIDATION_PASSED=false
|
||||
else
|
||||
print_success "File extension is valid (.py)"
|
||||
fi
|
||||
|
||||
# Check 2: File imports Click
|
||||
if grep -q "import click" "$CLI_FILE"; then
|
||||
print_success "Click module is imported"
|
||||
else
|
||||
print_error "Click module is not imported"
|
||||
VALIDATION_PASSED=false
|
||||
fi
|
||||
|
||||
# Check 3: Has at least one Click decorator
|
||||
DECORATOR_COUNT=$(grep -c "@click\." "$CLI_FILE" || true)
|
||||
if [ "$DECORATOR_COUNT" -gt 0 ]; then
|
||||
print_success "Found $DECORATOR_COUNT Click decorator(s)"
|
||||
else
|
||||
print_error "No Click decorators found"
|
||||
VALIDATION_PASSED=false
|
||||
fi
|
||||
|
||||
# Check 4: Has main entry point or group
|
||||
if grep -q "@click.command()\|@click.group()" "$CLI_FILE"; then
|
||||
print_success "Has Click command or group decorator"
|
||||
else
|
||||
print_error "Missing @click.command() or @click.group()"
|
||||
VALIDATION_PASSED=false
|
||||
fi
|
||||
|
||||
# Check 5: Has if __name__ == '__main__' block
|
||||
if grep -q "if __name__ == '__main__':" "$CLI_FILE"; then
|
||||
print_success "Has main execution block"
|
||||
else
|
||||
print_warning "Missing main execution block (if __name__ == '__main__':)"
|
||||
fi
|
||||
|
||||
# Check 6: Python syntax is valid
|
||||
if python3 -m py_compile "$CLI_FILE" 2>/dev/null; then
|
||||
print_success "Python syntax is valid"
|
||||
else
|
||||
print_error "Python syntax errors detected"
|
||||
VALIDATION_PASSED=false
|
||||
fi
|
||||
|
||||
# Check 7: Has help text
|
||||
if grep -q '"""' "$CLI_FILE"; then
|
||||
print_success "Contains docstrings/help text"
|
||||
else
|
||||
print_warning "No docstrings found (recommended for help text)"
|
||||
fi
|
||||
|
||||
# Check 8: Has option or argument decorators
|
||||
if grep -q "@click.option\|@click.argument" "$CLI_FILE"; then
|
||||
print_success "Has options or arguments defined"
|
||||
else
|
||||
print_warning "No options or arguments defined"
|
||||
fi
|
||||
|
||||
# Check 9: Uses recommended patterns
|
||||
echo ""
|
||||
print_info "Checking best practices..."
|
||||
|
||||
# Check for version option
|
||||
if grep -q "@click.version_option" "$CLI_FILE"; then
|
||||
print_success "Has version option"
|
||||
else
|
||||
print_warning "Consider adding @click.version_option()"
|
||||
fi
|
||||
|
||||
# Check for help parameter
|
||||
if grep -q "help=" "$CLI_FILE"; then
|
||||
print_success "Uses help parameters"
|
||||
else
|
||||
print_warning "Consider adding help text to options"
|
||||
fi
|
||||
|
||||
# Check for context usage
|
||||
if grep -q "@click.pass_context" "$CLI_FILE"; then
|
||||
print_success "Uses context for state sharing"
|
||||
else
|
||||
print_info "No context usage detected (optional)"
|
||||
fi
|
||||
|
||||
# Check for command groups
|
||||
if grep -q "@click.group()" "$CLI_FILE"; then
|
||||
print_success "Uses command groups"
|
||||
# Check for subcommands
|
||||
SUBCOMMAND_COUNT=$(grep -c "\.command()" "$CLI_FILE" || true)
|
||||
if [ "$SUBCOMMAND_COUNT" -gt 0 ]; then
|
||||
print_success "Has $SUBCOMMAND_COUNT subcommand(s)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check for validation
|
||||
if grep -q "click.Choice\|click.IntRange\|click.FloatRange\|click.Path" "$CLI_FILE"; then
|
||||
print_success "Uses Click's built-in validators"
|
||||
else
|
||||
print_info "No built-in validators detected (optional)"
|
||||
fi
|
||||
|
||||
# Check for colored output (Rich or Click's styling)
|
||||
if grep -q "from rich\|click.style\|click.echo.*fg=" "$CLI_FILE"; then
|
||||
print_success "Uses colored output"
|
||||
else
|
||||
print_info "No colored output detected (optional)"
|
||||
fi
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
if [ "$VALIDATION_PASSED" = true ]; then
|
||||
print_success "All critical validations passed!"
|
||||
echo ""
|
||||
print_info "Try running: python3 $CLI_FILE --help"
|
||||
exit 0
|
||||
else
|
||||
print_error "Validation failed. Please fix the errors above."
|
||||
exit 1
|
||||
fi
|
||||
Reference in New Issue
Block a user