225 lines
5.2 KiB
Bash
225 lines
5.2 KiB
Bash
#!/bin/bash
|
|
# validate-workflows.sh - Validate Gitea Actions workflows
|
|
# Sprint: SPRINT_20251226_001_CICD
|
|
#
|
|
# Usage:
|
|
# ./validate-workflows.sh # Validate all workflows
|
|
# ./validate-workflows.sh --strict # Fail on any warning
|
|
# ./validate-workflows.sh --verbose # Show detailed output
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
|
|
WORKFLOWS_DIR="$REPO_ROOT/.gitea/workflows"
|
|
SCRIPTS_DIR="$REPO_ROOT/.gitea/scripts"
|
|
|
|
# Configuration
|
|
STRICT_MODE=false
|
|
VERBOSE=false
|
|
|
|
# Counters
|
|
PASSED=0
|
|
FAILED=0
|
|
WARNINGS=0
|
|
|
|
# Colors (if terminal supports it)
|
|
if [[ -t 1 ]]; then
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[0;33m'
|
|
NC='\033[0m' # No Color
|
|
else
|
|
RED=''
|
|
GREEN=''
|
|
YELLOW=''
|
|
NC=''
|
|
fi
|
|
|
|
# Parse arguments
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--strict)
|
|
STRICT_MODE=true
|
|
shift
|
|
;;
|
|
--verbose)
|
|
VERBOSE=true
|
|
shift
|
|
;;
|
|
--help)
|
|
echo "Usage: $0 [OPTIONS]"
|
|
echo ""
|
|
echo "Options:"
|
|
echo " --strict Fail on any warning"
|
|
echo " --verbose Show detailed output"
|
|
echo " --help Show this help message"
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "Unknown option: $1"
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
echo "=== Gitea Workflow Validation ==="
|
|
echo "Workflows: $WORKFLOWS_DIR"
|
|
echo "Scripts: $SCRIPTS_DIR"
|
|
echo ""
|
|
|
|
# Check if workflows directory exists
|
|
if [[ ! -d "$WORKFLOWS_DIR" ]]; then
|
|
echo -e "${RED}ERROR: Workflows directory not found${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
# Function to validate YAML syntax
|
|
validate_yaml_syntax() {
|
|
local file=$1
|
|
local name=$(basename "$file")
|
|
|
|
# Try python yaml parser first
|
|
if command -v python3 &>/dev/null; then
|
|
if python3 -c "import yaml; yaml.safe_load(open('$file'))" 2>/dev/null; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
# Fallback to ruby if available
|
|
elif command -v ruby &>/dev/null; then
|
|
if ruby -ryaml -e "YAML.load_file('$file')" 2>/dev/null; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
else
|
|
# Can't validate YAML, warn and skip
|
|
return 2
|
|
fi
|
|
}
|
|
|
|
# Function to extract script references from a workflow
|
|
extract_script_refs() {
|
|
local file=$1
|
|
# Look for patterns like: .gitea/scripts/*, scripts/*, ./devops/scripts/*
|
|
grep -oE '(\.gitea/scripts|scripts|devops/scripts)/[a-zA-Z0-9_/-]+\.(sh|py|js|mjs)' "$file" 2>/dev/null | sort -u || true
|
|
}
|
|
|
|
# Function to check if a script exists
|
|
check_script_exists() {
|
|
local script_path=$1
|
|
local full_path="$REPO_ROOT/$script_path"
|
|
|
|
if [[ -f "$full_path" ]]; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Validate each workflow file
|
|
echo "=== Validating Workflow Syntax ==="
|
|
for workflow in "$WORKFLOWS_DIR"/*.yml "$WORKFLOWS_DIR"/*.yaml; do
|
|
[[ -e "$workflow" ]] || continue
|
|
|
|
name=$(basename "$workflow")
|
|
|
|
if [[ "$VERBOSE" == "true" ]]; then
|
|
echo "Checking: $name"
|
|
fi
|
|
|
|
result=$(validate_yaml_syntax "$workflow")
|
|
exit_code=$?
|
|
|
|
if [[ $exit_code -eq 0 ]]; then
|
|
echo -e " ${GREEN}[PASS]${NC} $name - YAML syntax valid"
|
|
((PASSED++))
|
|
elif [[ $exit_code -eq 2 ]]; then
|
|
echo -e " ${YELLOW}[SKIP]${NC} $name - No YAML parser available"
|
|
((WARNINGS++))
|
|
else
|
|
echo -e " ${RED}[FAIL]${NC} $name - YAML syntax error"
|
|
((FAILED++))
|
|
fi
|
|
done
|
|
|
|
echo ""
|
|
echo "=== Validating Script References ==="
|
|
|
|
# Check all script references
|
|
MISSING_SCRIPTS=()
|
|
for workflow in "$WORKFLOWS_DIR"/*.yml "$WORKFLOWS_DIR"/*.yaml; do
|
|
[[ -e "$workflow" ]] || continue
|
|
|
|
name=$(basename "$workflow")
|
|
refs=$(extract_script_refs "$workflow")
|
|
|
|
if [[ -z "$refs" ]]; then
|
|
if [[ "$VERBOSE" == "true" ]]; then
|
|
echo " $name: No script references found"
|
|
fi
|
|
continue
|
|
fi
|
|
|
|
while IFS= read -r script_ref; do
|
|
[[ -z "$script_ref" ]] && continue
|
|
|
|
if check_script_exists "$script_ref"; then
|
|
if [[ "$VERBOSE" == "true" ]]; then
|
|
echo -e " ${GREEN}[OK]${NC} $name -> $script_ref"
|
|
fi
|
|
else
|
|
echo -e " ${RED}[MISSING]${NC} $name -> $script_ref"
|
|
MISSING_SCRIPTS+=("$name: $script_ref")
|
|
((WARNINGS++))
|
|
fi
|
|
done <<< "$refs"
|
|
done
|
|
|
|
# Check that .gitea/scripts directories exist
|
|
echo ""
|
|
echo "=== Validating Script Directory Structure ==="
|
|
EXPECTED_DIRS=(build test validate sign release metrics evidence util)
|
|
for dir in "${EXPECTED_DIRS[@]}"; do
|
|
dir_path="$SCRIPTS_DIR/$dir"
|
|
if [[ -d "$dir_path" ]]; then
|
|
script_count=$(find "$dir_path" -maxdepth 1 -name "*.sh" -o -name "*.py" 2>/dev/null | wc -l)
|
|
echo -e " ${GREEN}[OK]${NC} $dir/ ($script_count scripts)"
|
|
else
|
|
echo -e " ${YELLOW}[WARN]${NC} $dir/ - Directory not found"
|
|
((WARNINGS++))
|
|
fi
|
|
done
|
|
|
|
# Summary
|
|
echo ""
|
|
echo "=== Validation Summary ==="
|
|
echo -e " Passed: ${GREEN}$PASSED${NC}"
|
|
echo -e " Failed: ${RED}$FAILED${NC}"
|
|
echo -e " Warnings: ${YELLOW}$WARNINGS${NC}"
|
|
|
|
if [[ ${#MISSING_SCRIPTS[@]} -gt 0 ]]; then
|
|
echo ""
|
|
echo "Missing script references:"
|
|
for ref in "${MISSING_SCRIPTS[@]}"; do
|
|
echo " - $ref"
|
|
done
|
|
fi
|
|
|
|
# Exit code
|
|
if [[ $FAILED -gt 0 ]]; then
|
|
echo ""
|
|
echo -e "${RED}FAILED: $FAILED validation(s) failed${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "$STRICT_MODE" == "true" && $WARNINGS -gt 0 ]]; then
|
|
echo ""
|
|
echo -e "${YELLOW}STRICT MODE: $WARNINGS warning(s) treated as errors${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
echo ""
|
|
echo -e "${GREEN}All validations passed!${NC}"
|