CtrlK
BlogDocsLog inGet started
Tessl Logo

pantheon-ai/ansible-validator

Comprehensive toolkit for validating, linting, testing, and automating Ansible playbooks, roles, and collections. Use this skill when working with Ansible files (.yml, .yaml playbooks, roles, inventories), validating automation code, debugging playbook execution, performing dry-run testing with check mode, or working with custom modules and collections.

Overall
score

93%

Does it follow best practices?

Validation for skill structure

Overview
Skills
Evals
Files

validate_role.shscripts/

#!/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

Install with Tessl CLI

npx tessl i pantheon-ai/ansible-validator@0.1.0

SKILL.md

tile.json