CtrlK
BlogDocsLog inGet started
Tessl Logo

pantheon-ai/ansible-toolkit

Complete ansible toolkit with generation and validation capabilities

97

Quality

97%

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Advisory

Suggest reviewing before use

Overview
Quality
Evals
Security
Files

validate_role.shvalidator/scripts/

#!/usr/bin/env bash

# Comprehensive Ansible Role Validation Script
# Automatically installs ansible and ansible-lint in temporary venv if not available

set -e

ROLE_DIR="$1"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SKILL_DIR="$(dirname "$SCRIPT_DIR")"

COLOR_GREEN='\033[0;32m'
COLOR_YELLOW='\033[1;33m'
COLOR_RED='\033[0;31m'
COLOR_BLUE='\033[0;34m'
COLOR_RESET='\033[0m'

# Usage check
if [ -z "$ROLE_DIR" ]; then
    echo "Usage: $0 <role-directory>"
    exit 1
fi

if [ ! -d "$ROLE_DIR" ]; then
    echo -e "${COLOR_RED}Error: Role directory not found: $ROLE_DIR${COLOR_RESET}"
    exit 1
fi

# Get absolute path to role
ROLE_ABS_PATH=$(cd "$ROLE_DIR" && pwd)

echo -e "${COLOR_BLUE}========================================${COLOR_RESET}"
echo -e "${COLOR_BLUE}Ansible Role Validation${COLOR_RESET}"
echo -e "${COLOR_BLUE}========================================${COLOR_RESET}"
echo ""
echo "Validating: $ROLE_ABS_PATH"
echo ""

# Check for required tools and setup venv if needed
TEMP_VENV=""
CLEANUP_VENV=0
USE_SYSTEM_ANSIBLE=1
USE_SYSTEM_ANSIBLE_LINT=1

# Function to run ansible-playbook command
run_ansible_playbook() {
    if [ -n "$TEMP_VENV" ]; then
        "$TEMP_VENV/bin/ansible-playbook" "$@"
    else
        ansible-playbook "$@"
    fi
}

# Function to run ansible-lint command
run_ansible_lint() {
    if [ -n "$TEMP_VENV" ]; then
        "$TEMP_VENV/bin/ansible-lint" "$@"
    else
        ansible-lint "$@"
    fi
}

# Check if ansible-playbook is available
if ! command -v ansible-playbook >/dev/null 2>&1; then
    USE_SYSTEM_ANSIBLE=0
fi

# Check if ansible-lint is available
if ! command -v ansible-lint >/dev/null 2>&1; then
    USE_SYSTEM_ANSIBLE_LINT=0
fi

# Create temp venv if either tool is missing
if [ $USE_SYSTEM_ANSIBLE -eq 0 ] || [ $USE_SYSTEM_ANSIBLE_LINT -eq 0 ]; then
    echo -e "${COLOR_YELLOW}⚠ Some tools not found in system${COLOR_RESET}"

    if [ $USE_SYSTEM_ANSIBLE -eq 0 ]; then
        echo "  - ansible-playbook: not found"
    else
        echo "  - ansible-playbook: using system version"
    fi

    if [ $USE_SYSTEM_ANSIBLE_LINT -eq 0 ]; then
        echo "  - ansible-lint: not found"
    else
        echo "  - ansible-lint: using system version"
    fi

    echo ""
    echo "Creating temporary environment with missing tools..."
    echo ""

    # Create temporary venv
    TEMP_VENV=$(mktemp -d -t ansible-validator.XXXXXX)
    CLEANUP_VENV=1

    # Setup cleanup trap
    cleanup() {
        if [ $CLEANUP_VENV -eq 1 ] && [ -n "$TEMP_VENV" ]; then
            echo ""
            echo "Cleaning up temporary environment..."
            rm -rf "$TEMP_VENV"
        fi
    }
    trap cleanup EXIT INT TERM

    # Create venv and install tools
    echo "Installing ansible tools (this may take a minute)..."
    python3 -m venv "$TEMP_VENV" >/dev/null 2>&1

    # Activate venv and install
    source "$TEMP_VENV/bin/activate"

    # Install required tools
    pip install --quiet --upgrade pip setuptools wheel
    pip install --quiet ansible-core ansible-lint yamllint

    echo -e "${COLOR_GREEN}✓ Temporary environment ready${COLOR_RESET}"
    echo ""
else
    echo -e "${COLOR_GREEN}✓ Using system ansible tools${COLOR_RESET}"
    echo ""
fi

ERRORS=0
WARNINGS=0

# Stage 1: Role Structure Check
echo -e "${COLOR_BLUE}[1/5] Role Structure Check${COLOR_RESET}"
echo "-----------------------------------"

REQUIRED_DIRS=("tasks")
RECOMMENDED_DIRS=("defaults" "handlers" "meta" "templates" "vars")
OPTIONAL_DIRS=("files" "molecule")

echo "Checking required directories..."
for dir in "${REQUIRED_DIRS[@]}"; do
    if [ -d "$ROLE_ABS_PATH/$dir" ]; then
        echo -e "  ${COLOR_GREEN}✓${COLOR_RESET} $dir/"
    else
        echo -e "  ${COLOR_RED}✗${COLOR_RESET} $dir/ - REQUIRED"
        ERRORS=$((ERRORS + 1))
    fi
done

echo ""
echo "Checking recommended directories..."
for dir in "${RECOMMENDED_DIRS[@]}"; do
    if [ -d "$ROLE_ABS_PATH/$dir" ]; then
        echo -e "  ${COLOR_GREEN}✓${COLOR_RESET} $dir/"
    else
        echo -e "  ${COLOR_YELLOW}⚠${COLOR_RESET} $dir/ - recommended"
        WARNINGS=$((WARNINGS + 1))
    fi
done

echo ""
echo "Checking optional directories..."
for dir in "${OPTIONAL_DIRS[@]}"; do
    if [ -d "$ROLE_ABS_PATH/$dir" ]; then
        echo -e "  ${COLOR_GREEN}✓${COLOR_RESET} $dir/"
    fi
done

echo ""

# Check for main.yml files
echo "Checking main.yml files..."
for dir in tasks defaults handlers vars meta; do
    if [ -d "$ROLE_ABS_PATH/$dir" ]; then
        if [ -f "$ROLE_ABS_PATH/$dir/main.yml" ] || [ -f "$ROLE_ABS_PATH/$dir/main.yaml" ]; then
            echo -e "  ${COLOR_GREEN}✓${COLOR_RESET} $dir/main.yml exists"
        else
            echo -e "  ${COLOR_YELLOW}⚠${COLOR_RESET} $dir/main.yml not found"
            WARNINGS=$((WARNINGS + 1))
        fi
    fi
done

echo ""

# Stage 2: YAML Syntax Check
echo -e "${COLOR_BLUE}[2/5] YAML Syntax Check (yamllint)${COLOR_RESET}"
echo "-----------------------------------"

# yamllint - prefer system, fallback to venv
YAMLLINT_CMD=""
if command -v yamllint >/dev/null 2>&1; then
    YAMLLINT_CMD="yamllint"
elif [ -n "$TEMP_VENV" ] && [ -f "$TEMP_VENV/bin/yamllint" ]; then
    YAMLLINT_CMD="$TEMP_VENV/bin/yamllint"
fi

if [ -n "$YAMLLINT_CMD" ]; then
    # Check if custom config exists
    if [ -f "$SKILL_DIR/assets/.yamllint" ]; then
        YAMLLINT_CONFIG="-c $SKILL_DIR/assets/.yamllint"
    else
        YAMLLINT_CONFIG=""
    fi

    # Find all YAML files in the role
    YAML_FILES=$(find "$ROLE_ABS_PATH" -type f \( -name "*.yml" -o -name "*.yaml" \) ! -path "*/.git/*" ! -path "*/molecule/*" 2>/dev/null || true)

    if [ -n "$YAML_FILES" ]; then
        YAML_ERRORS=0
        for file in $YAML_FILES; do
            if ! $YAMLLINT_CMD $YAMLLINT_CONFIG "$file" 2>&1 | grep -v "warning  found forbidden document start"; then
                YAML_ERRORS=$((YAML_ERRORS + 1))
            fi
        done

        if [ $YAML_ERRORS -eq 0 ]; then
            echo -e "${COLOR_GREEN}✓ YAML syntax check passed${COLOR_RESET}"
        else
            echo -e "${COLOR_RED}✗ YAML syntax check failed on $YAML_ERRORS file(s)${COLOR_RESET}"
            ERRORS=$((ERRORS + 1))
        fi
    else
        echo -e "${COLOR_YELLOW}⚠ No YAML files found in role${COLOR_RESET}"
    fi
else
    echo -e "${COLOR_YELLOW}⚠ yamllint not available - skipping YAML syntax check${COLOR_RESET}"
    WARNINGS=$((WARNINGS + 1))
fi

echo ""

# Stage 3: Ansible Syntax Check
echo -e "${COLOR_BLUE}[3/5] Ansible Syntax Check${COLOR_RESET}"
echo "-----------------------------------"

if [ $USE_SYSTEM_ANSIBLE -eq 1 ] || [ -n "$TEMP_VENV" ]; then
    # Create a temporary test playbook
    TEMP_PLAYBOOK=$(mktemp)
    cat > "$TEMP_PLAYBOOK" <<EOF
---
- hosts: localhost
  gather_facts: no
  roles:
    - role: test_role
EOF

    ROLE_NAME=$(basename "$ROLE_ABS_PATH")

    # Create temporary roles directory
    TEMP_DIR=$(mktemp -d)
    ln -s "$ROLE_ABS_PATH" "$TEMP_DIR/test_role"

    # Run syntax check
    if ANSIBLE_ROLES_PATH="$TEMP_DIR" run_ansible_playbook --syntax-check "$TEMP_PLAYBOOK" >/dev/null 2>&1; then
        echo -e "${COLOR_GREEN}✓ Ansible syntax check passed${COLOR_RESET}"
    else
        echo -e "${COLOR_RED}✗ Ansible syntax check failed${COLOR_RESET}"
        echo "  Run manually for details:"
        echo "  ANSIBLE_ROLES_PATH=$TEMP_DIR ansible-playbook --syntax-check $TEMP_PLAYBOOK"
        ERRORS=$((ERRORS + 1))
    fi

    # Cleanup
    rm -f "$TEMP_PLAYBOOK"
    rm -rf "$TEMP_DIR"
else
    echo -e "${COLOR_RED}✗ ansible-playbook not available${COLOR_RESET}"
    ERRORS=$((ERRORS + 1))
fi

echo ""

# Stage 4: Ansible Lint
echo -e "${COLOR_BLUE}[4/5] Ansible Lint (ansible-lint)${COLOR_RESET}"
echo "-----------------------------------"

if [ $USE_SYSTEM_ANSIBLE_LINT -eq 1 ] || [ -n "$TEMP_VENV" ]; then
    # Check if custom config exists
    if [ -f "$SKILL_DIR/assets/.ansible-lint" ]; then
        ANSIBLE_LINT_CONFIG="-c $SKILL_DIR/assets/.ansible-lint"
    else
        ANSIBLE_LINT_CONFIG=""
    fi

    # Run ansible-lint on the role
    if run_ansible_lint $ANSIBLE_LINT_CONFIG "$ROLE_ABS_PATH" 2>&1; then
        echo -e "${COLOR_GREEN}✓ Ansible lint check passed${COLOR_RESET}"
    else
        LINT_EXIT=$?
        if [ $LINT_EXIT -eq 2 ]; then
            echo -e "${COLOR_RED}✗ Ansible lint found errors${COLOR_RESET}"
            ERRORS=$((ERRORS + 1))
        else
            echo -e "${COLOR_YELLOW}⚠ Ansible lint found warnings${COLOR_RESET}"
            WARNINGS=$((WARNINGS + 1))
        fi
    fi
else
    echo -e "${COLOR_YELLOW}⚠ ansible-lint not available - skipping lint check${COLOR_RESET}"
    WARNINGS=$((WARNINGS + 1))
fi

echo ""

# Stage 5: Molecule Check (if configured)
echo -e "${COLOR_BLUE}[5/5] Molecule Configuration Check${COLOR_RESET}"
echo "-----------------------------------"

if [ -d "$ROLE_ABS_PATH/molecule" ]; then
    echo -e "${COLOR_GREEN}✓ Molecule directory found${COLOR_RESET}"

    # List scenarios
    SCENARIOS=$(ls -1 "$ROLE_ABS_PATH/molecule" 2>/dev/null || true)
    if [ -n "$SCENARIOS" ]; then
        echo "  Available scenarios:"
        for scenario in $SCENARIOS; do
            echo "    - $scenario"
        done

        echo ""
        echo "  Run tests with: bash scripts/test_role.sh $ROLE_ABS_PATH"
        echo "  (Molecule will be automatically installed in a temporary venv if needed)"
    fi
else
    echo -e "${COLOR_YELLOW}⚠ Molecule not configured for this role${COLOR_RESET}"
    echo "  Initialize with: cd $ROLE_ABS_PATH && molecule init scenario"
fi

echo ""
echo -e "${COLOR_BLUE}========================================${COLOR_RESET}"
echo -e "${COLOR_BLUE}Validation Summary${COLOR_RESET}"
echo -e "${COLOR_BLUE}========================================${COLOR_RESET}"

if [ $ERRORS -eq 0 ] && [ $WARNINGS -eq 0 ]; then
    echo -e "${COLOR_GREEN}✓ All checks passed successfully!${COLOR_RESET}"
    echo ""
    echo "For best practices, see: $SKILL_DIR/references/best_practices.md"
    if [ -n "$TEMP_VENV" ]; then
        echo ""
        echo "Note: Some tools were installed in a temporary environment."
        echo "To install permanently: pip install ansible ansible-lint yamllint"
    fi
    exit 0
elif [ $ERRORS -eq 0 ]; then
    echo -e "${COLOR_YELLOW}⚠ Validation completed with $WARNINGS warning(s)${COLOR_RESET}"
    echo ""
    echo "For best practices, see: $SKILL_DIR/references/best_practices.md"
    if [ -n "$TEMP_VENV" ]; then
        echo ""
        echo "Note: Some tools were installed in a temporary environment."
        echo "To install permanently: pip install ansible ansible-lint yamllint"
    fi
    exit 0
else
    echo -e "${COLOR_RED}✗ Validation failed with $ERRORS error(s) and $WARNINGS warning(s)${COLOR_RESET}"
    echo ""
    echo "For troubleshooting, see: $SKILL_DIR/references/common_errors.md"
    echo "For best practices, see: $SKILL_DIR/references/best_practices.md"
    exit 1
fi

tile.json