#!/usr/bin/env bash # ============================================================================ # Schema Differ Script # ============================================================================ # Purpose: Compare configuration against reference schemas and validate plugin entries # Version: 1.0.0 # Usage: ./schema-differ.sh [index] # Returns: 0=all valid, 1=validation errors, 2=error # ============================================================================ set -euo pipefail # Source shared library SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "${SCRIPT_DIR}/../../../scripts/validate-lib.sh" # ==================== # Configuration # ==================== readonly MARKETPLACE_FILE="${1:-}" readonly INDEX="${2:-all}" # ==================== # Plugin Entry Validation # ==================== validate_plugin_entry() { local index=$1 local entry_json=$2 local strict=${3:-false} local has_errors=0 local has_warnings=0 # Extract fields using json_get would be complex here, so use jq/python inline local name version source description author keywords license name=$(echo "${entry_json}" | jq -r '.name // empty' 2>/dev/null || echo "") version=$(echo "${entry_json}" | jq -r '.version // empty' 2>/dev/null || echo "") source=$(echo "${entry_json}" | jq -r '.source // empty' 2>/dev/null || echo "") description=$(echo "${entry_json}" | jq -r '.description // empty' 2>/dev/null || echo "") author=$(echo "${entry_json}" | jq -r '.author // empty' 2>/dev/null || echo "") keywords=$(echo "${entry_json}" | jq -r '.keywords // empty' 2>/dev/null || echo "") license=$(echo "${entry_json}" | jq -r '.license // empty' 2>/dev/null || echo "") echo "" print_section "Entry ${index}: ${name:-}" # Required fields echo " Required (3):" # name (required, lowercase-hyphen) if [[ -z "${name}" ]]; then print_error " name: Missing (REQUIRED)" ((has_errors++)) elif ! validate_name_format "${name}"; then print_error " name: \"${name}\" - Invalid format" print_info " Expected: lowercase-hyphen (my-plugin)" ((has_errors++)) else print_success " name: \"${name}\"" fi # source (required, valid format) if [[ -z "${source}" ]]; then print_error " source: Missing (REQUIRED)" ((has_errors++)) elif ! validate_source_format "${source}"; then print_error " source: \"${source}\" - Invalid format" print_info " Valid: ./path, github:user/repo, https://url" ((has_errors++)) else print_success " source: \"${source}\"" fi # description (required, non-empty) if [[ -z "${description}" ]]; then print_error " description: Missing (REQUIRED)" ((has_errors++)) else # Truncate for display local desc_display="${description}" if [[ ${#description} -gt 50 ]]; then desc_display="${description:0:47}..." fi print_success " description: \"${desc_display}\"" fi echo "" echo " Recommended (4):" # version (recommended, semver) if [[ -z "${version}" ]]; then print_warning " version: Missing" ((has_warnings++)) elif ! validate_semver "${version}"; then print_warning " version: \"${version}\" - Invalid semver" ((has_warnings++)) else print_success " version: \"${version}\"" fi # author (recommended) if [[ -z "${author}" || "${author}" == "null" ]]; then print_warning " author: Missing" ((has_warnings++)) else print_success " author: Present" fi # keywords (recommended) if [[ -z "${keywords}" || "${keywords}" == "null" || "${keywords}" == "[]" ]]; then print_warning " keywords: Missing" ((has_warnings++)) else local keyword_count keyword_count=$(echo "${entry_json}" | jq '.keywords | length' 2>/dev/null || echo "0") print_success " keywords: ${keyword_count} items" fi # license (recommended, SPDX) if [[ -z "${license}" ]]; then print_warning " license: Missing" ((has_warnings++)) elif ! validate_license "${license}"; then print_warning " license: \"${license}\" - Unknown SPDX identifier" ((has_warnings++)) else print_success " license: \"${license}\"" fi # Entry status echo "" if [[ ${has_errors} -eq 0 ]]; then if [[ ${has_warnings} -eq 0 ]]; then print_success "Status: PASS (no issues)" else print_info "Status: PASS with ${has_warnings} warning(s)" fi return 0 else print_error "Status: FAIL (${has_errors} critical issues, ${has_warnings} warnings)" return 1 fi } # ==================== # Main Logic # ==================== main() { # Validate arguments if [[ -z "${MARKETPLACE_FILE}" ]]; then print_error "Usage: $0 [index]" exit 2 fi if [[ ! -f "${MARKETPLACE_FILE}" ]]; then print_error "Marketplace file not found: ${MARKETPLACE_FILE}" exit 2 fi # Validate JSON syntax if ! validate_json_syntax "${MARKETPLACE_FILE}"; then print_error "Invalid JSON in ${MARKETPLACE_FILE}" print_info "Run JSON validation first to fix syntax errors" exit 2 fi # Print header print_header "Plugin Entries Validation" echo "Marketplace: ${MARKETPLACE_FILE}" echo "" # Get plugins array length local plugin_count plugin_count=$(get_json_array_length "${MARKETPLACE_FILE}" ".plugins") if [[ ${plugin_count} -eq 0 ]]; then print_warning "No plugin entries found in marketplace" print_info "The plugins array is empty" exit 0 fi echo "Total Entries: ${plugin_count}" # Determine which entries to validate local entries_to_check=() if [[ "${INDEX}" == "all" ]]; then for ((i=0; i /dev/null; then entry_json=$(jq ".plugins[${idx}]" "${MARKETPLACE_FILE}" 2>/dev/null) else # Fallback to python entry_json=$(python3 </dev/null import json with open('${MARKETPLACE_FILE}') as f: data = json.load(f) print(json.dumps(data['plugins'][${idx}])) EOF ) fi # Validate entry if ! validate_plugin_entry "${idx}" "${entry_json}" "false"; then ((failed_count++)) fi done # Summary echo "" print_header "Summary" local passed_count=$((${#entries_to_check[@]} - failed_count)) local pass_percentage=$((passed_count * 100 / ${#entries_to_check[@]})) echo "Total Entries: ${#entries_to_check[@]}" if [[ ${passed_count} -gt 0 ]]; then print_success "Passed: ${passed_count} (${pass_percentage}%)" fi if [[ ${failed_count} -gt 0 ]]; then print_error "Failed: ${failed_count} ($((100 - pass_percentage))%)" fi echo "" if [[ ${failed_count} -eq 0 ]]; then print_header "✅ PASS: All plugin entries valid" exit 0 else print_header "❌ FAIL: ${failed_count} plugin entries have errors" echo "" print_info "Action Required:" echo " Fix validation errors in plugin entries" echo " Ensure all required fields are present" echo " Use correct formats for names, versions, and sources" exit 1 fi } main "$@"