#!/bin/bash # # Enhanced validation framework for Maven artifacts before deployment # This script validates JAR files, POM files, and deployment readiness # set -euo pipefail # Configuration SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" ARTIFACTS_DIR="${1:-./artifacts}" VALIDATION_LOG="/tmp/maven-validation-$(date +%s).log" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Logging functions log_info() { echo -e "${BLUE}[INFO]${NC} $*" | tee -a "${VALIDATION_LOG}" } log_warn() { echo -e "${YELLOW}[WARN]${NC} $*" | tee -a "${VALIDATION_LOG}" } log_error() { echo -e "${RED}[ERROR]${NC} $*" | tee -a "${VALIDATION_LOG}" } log_success() { echo -e "${GREEN}[SUCCESS]${NC} $*" | tee -a "${VALIDATION_LOG}" } # Validation counters VALIDATION_ERRORS=0 VALIDATION_WARNINGS=0 # Error tracking add_error() { VALIDATION_ERRORS=$((VALIDATION_ERRORS + 1)) log_error "$*" } add_warning() { VALIDATION_WARNINGS=$((VALIDATION_WARNINGS + 1)) log_warn "$*" } # Java/Maven environment validation validate_environment() { log_info "Validating build environment..." # Check Java availability if ! command -v java &> /dev/null; then add_error "Java is not installed or not in PATH" return 1 fi JAVA_VERSION=$(java -version 2>&1 | head -n1 | cut -d'"' -f2) log_info "Java version: ${JAVA_VERSION}" # Check Maven availability if ! command -v mvn &> /dev/null; then add_warning "Maven is not installed - some validations will be skipped" else MVN_VERSION=$(mvn -version | head -n1 | cut -d' ' -f3) log_info "Maven version: ${MVN_VERSION}" fi # Check JAR command if ! command -v jar &> /dev/null; then add_error "jar command is not available" return 1 fi log_success "Environment validation completed" return 0 } # JAR file validation validate_jar_file() { local jar_file="$1" local jar_basename jar_basename=$(basename "${jar_file}") log_info "Validating JAR: ${jar_basename}" # Check file exists and is readable if [[ ! -f "${jar_file}" ]]; then add_error "JAR file not found: ${jar_file}" return 1 fi if [[ ! -r "${jar_file}" ]]; then add_error "JAR file not readable: ${jar_file}" return 1 fi # Check file size (must be > 1KB) local file_size file_size=$(stat -c%s "${jar_file}" 2>/dev/null || stat -f%z "${jar_file}" 2>/dev/null || echo "0") if [[ ${file_size} -lt 1024 ]]; then add_error "JAR file too small: ${jar_file} (${file_size} bytes)" return 1 fi log_info "JAR size: ${file_size} bytes" # Test JAR integrity if ! jar tf "${jar_file}" > /dev/null 2>&1; then add_error "JAR file is corrupted or invalid: ${jar_file}" return 1 fi # Check for required HDF5 Java classes local temp_dir temp_dir=$(mktemp -d) trap "rm -rf '${temp_dir}'" EXIT if ! (cd "${temp_dir}" && jar xf "${jar_file}"); then add_error "Failed to extract JAR: ${jar_file}" rm -rf "${temp_dir}" return 1 fi # Check for essential HDF5 classes based on JAR type # FFM builds have two separate JARs: # - javahdf5-*.jar: FFM bindings (org/hdfgroup/javahdf5/*) # - jarhdf5-*.jar: Wrapper classes (hdf/hdf5lib/*) # JNI builds have single JAR with hdf/hdf5lib/* classes if [[ "${jar_basename}" == *"javahdf5"* ]]; then # This is the FFM bindings JAR - check for FFM classes local ffm_classes=( "org/hdfgroup/javahdf5/hdf5_h.class" ) local has_ffm=false for class_file in "${ffm_classes[@]}"; do if [[ -f "${temp_dir}/${class_file}" ]]; then has_ffm=true log_info "Found FFM binding class: ${class_file}" break fi done if [[ "${has_ffm}" == "false" ]]; then add_error "FFM bindings JAR missing required FFM classes (expected org/hdfgroup/javahdf5/hdf5_h.class)" fi else # This is a wrapper/JNI JAR - check for hdf.hdf5lib classes local required_classes=( "hdf/hdf5lib/H5.class" "hdf/hdf5lib/HDF5Constants.class" "hdf/hdf5lib/HDFArray.class" "hdf/hdf5lib/HDFNativeData.class" ) for class_file in "${required_classes[@]}"; do if [[ ! -f "${temp_dir}/${class_file}" ]]; then add_error "Missing required class in JAR: ${class_file}" fi done fi # Check manifest if [[ -f "${temp_dir}/META-INF/MANIFEST.MF" ]]; then if grep -q "Enable-Native-Access: ALL-UNNAMED" "${temp_dir}/META-INF/MANIFEST.MF"; then log_info "Native access enabled in manifest" else add_warning "Native access not found in manifest - may cause runtime issues" fi else add_warning "No manifest found in JAR" fi rm -rf "${temp_dir}" log_success "JAR validation completed: ${jar_basename}" return 0 } # POM file validation validate_pom_file() { local pom_file="$1" log_info "Validating POM: $(basename "${pom_file}")" # Check file exists if [[ ! -f "${pom_file}" ]]; then add_error "POM file not found: ${pom_file}" return 1 fi # Check XML validity if command -v xmllint &> /dev/null; then if ! xmllint --noout "${pom_file}" 2>/dev/null; then add_error "POM file is not valid XML: ${pom_file}" return 1 fi else add_warning "xmllint not available - skipping XML validation" fi # Check required Maven coordinates if ! grep -q "org.hdfgroup" "${pom_file}"; then add_error "Invalid or missing groupId in POM" fi if ! grep -qE "hdf5-java(-ffm|-jni)?" "${pom_file}"; then add_error "Invalid or missing artifactId in POM (expected hdf5-java, hdf5-java-ffm, or hdf5-java-jni)" fi # Extract version local version version=$(grep -o '[^<]*' "${pom_file}" | head -1 | sed 's/<[^>]*>//g' || echo "") if [[ -z "${version}" ]]; then add_error "No version found in POM" else log_info "POM version: ${version}" # Validate version format if [[ ! "${version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+)?(-SNAPSHOT)?$ ]]; then add_warning "Version format may not comply with Maven conventions: ${version}" fi fi # Check for required sections local required_sections=( "" "" "" "" "" "" ) for section in "${required_sections[@]}"; do if ! grep -q "${section}" "${pom_file}"; then add_warning "Missing recommended section in POM: ${section}" fi done # Check dependencies if grep -q "" "${pom_file}"; then log_info "Dependencies section found in POM" else add_warning "No dependencies section in POM" fi log_success "POM validation completed" return 0 } # Version consistency validation validate_version_consistency() { local pom_file="$1" shift local jar_files=("$@") log_info "Validating version consistency across artifacts..." # Extract version from POM local pom_version pom_version=$(grep -o '[^<]*' "${pom_file}" | head -1 | sed 's/<[^>]*>//g' || echo "") if [[ -z "${pom_version}" ]]; then add_error "Cannot extract version from POM for consistency check" return 1 fi log_info "POM version: ${pom_version}" # Check JAR filenames for version consistency for jar_file in "${jar_files[@]}"; do local jar_basename jar_basename=$(basename "${jar_file}") # Extract version from JAR filename (allowing for classifiers) local jar_version jar_version=$(echo "${jar_basename}" | sed -E 's/.*-([0-9]+\.[0-9]+\.[0-9]+(-[0-9]+)?(-SNAPSHOT)?)(-[^.]+)?\.jar$/\1/' || echo "") if [[ -z "${jar_version}" ]]; then add_warning "Cannot extract version from JAR filename: ${jar_basename}" elif [[ "${jar_version}" != "${pom_version}" ]]; then add_error "Version mismatch: POM=${pom_version}, JAR=${jar_version} (${jar_basename})" else log_info "Version consistency verified: ${jar_basename}" fi done return 0 } # Platform classifier validation validate_platform_classifiers() { local jar_files=("$@") log_info "Validating platform classifiers..." local valid_classifiers=( "linux-x86_64" "windows-x86_64" "macos-x86_64" "macos-aarch64" ) for jar_file in "${jar_files[@]}"; do local jar_basename jar_basename=$(basename "${jar_file}") # Skip universal JARs (no classifier) if [[ ! "${jar_basename}" =~ -[a-z]+-[a-z0-9_]+\.jar$ ]]; then log_info "Universal JAR (no classifier): ${jar_basename}" continue fi # Extract classifier local classifier classifier=$(echo "${jar_basename}" | sed -E 's/.*-([a-z]+-[a-z0-9_]+)\.jar$/\1/' || echo "") if [[ -z "${classifier}" ]]; then add_warning "Cannot extract classifier from JAR: ${jar_basename}" continue fi # Validate classifier local valid=false for valid_classifier in "${valid_classifiers[@]}"; do if [[ "${classifier}" == "${valid_classifier}" ]]; then valid=true break fi done if [[ "${valid}" == "true" ]]; then log_info "Valid platform classifier: ${classifier} (${jar_basename})" else add_error "Invalid platform classifier: ${classifier} (${jar_basename})" fi done return 0 } # Maven dependency simulation simulate_maven_dependency() { local pom_file="$1" if ! command -v mvn &> /dev/null; then add_warning "Maven not available - skipping dependency simulation" return 0 fi log_info "Simulating Maven dependency resolution..." # Create temporary Maven project local temp_project temp_project=$(mktemp -d) trap "rm -rf '${temp_project}'" EXIT # Extract coordinates from POM local group_id artifact_id version group_id=$(grep -o '[^<]*' "${pom_file}" | head -1 | sed 's/<[^>]*>//g' || echo "") artifact_id=$(grep -o '[^<]*' "${pom_file}" | head -1 | sed 's/<[^>]*>//g' || echo "") version=$(grep -o '[^<]*' "${pom_file}" | head -1 | sed 's/<[^>]*>//g' || echo "") # Find the JAR file in the artifacts directory local jar_file jar_file=$(find "$(dirname "${pom_file}")" -maxdepth 2 -name "${artifact_id}-${version}.jar" -o -name "${artifact_id}-*.jar" | head -1) if [ -z "${jar_file}" ]; then add_warning "Could not find JAR file for ${artifact_id}:${version} - skipping dependency simulation" return 0 fi # Install artifact to local Maven repository first log_info "Installing artifact to local Maven repository: ${group_id}:${artifact_id}:${version}" if ! mvn install:install-file \ -Dfile="${jar_file}" \ -DgroupId="${group_id}" \ -DartifactId="${artifact_id}" \ -Dversion="${version}" \ -Dpackaging=jar \ -DpomFile="${pom_file}" \ -q 2>&1 | tee -a "${VALIDATION_LOG}"; then add_warning "Failed to install artifact to local Maven repository" return 0 fi # Create test POM cat > "${temp_project}/pom.xml" << EOF 4.0.0 test maven-validation-test 1.0.0 ${group_id} ${artifact_id} ${version} EOF # Test dependency resolution if (cd "${temp_project}" && mvn dependency:resolve -q); then log_success "Maven dependency simulation passed" else add_warning "Maven dependency simulation failed - may indicate packaging issues" fi rm -rf "${temp_project}" return 0 } # Deployment readiness check check_deployment_readiness() { local artifacts_dir="$1" log_info "Checking deployment readiness..." # Check for required files local jar_files pom_files # Only count HDF5 JAR files, exclude dependencies like slf4j jar_files=($(find "${artifacts_dir}" -name "*hdf5*.jar" -not -name "*test*" 2>/dev/null || true)) pom_files=($(find "${artifacts_dir}" -name "pom.xml" 2>/dev/null || true)) if [[ ${#jar_files[@]} -eq 0 ]]; then add_error "No JAR files found in artifacts directory" return 1 fi if [[ ${#pom_files[@]} -eq 0 ]]; then add_error "No POM files found in artifacts directory" return 1 fi log_info "Found ${#jar_files[@]} JAR file(s) and ${#pom_files[@]} POM file(s)" # Check environment variables for deployment if [[ -z "${MAVEN_USERNAME:-}" ]]; then add_warning "MAVEN_USERNAME not set - deployment will fail" fi if [[ -z "${MAVEN_PASSWORD:-}" ]]; then add_warning "MAVEN_PASSWORD not set - deployment will fail" fi return 0 } # Generate validation report generate_report() { local artifacts_dir="$1" log_info "=== Maven Artifact Validation Report ===" log_info "Timestamp: $(date)" log_info "Artifacts directory: ${artifacts_dir}" log_info "Validation log: ${VALIDATION_LOG}" echo # Summary if [[ ${VALIDATION_ERRORS} -eq 0 ]]; then if [[ ${VALIDATION_WARNINGS} -eq 0 ]]; then log_success "✅ All validations passed with no warnings" else log_warn "⚠️ All validations passed with ${VALIDATION_WARNINGS} warning(s)" fi else log_error "❌ Validation failed with ${VALIDATION_ERRORS} error(s) and ${VALIDATION_WARNINGS} warning(s)" fi echo log_info "Full validation log available at: ${VALIDATION_LOG}" return ${VALIDATION_ERRORS} } # Main validation function main() { local artifacts_dir="${1:-./artifacts}" log_info "Starting Maven artifact validation..." log_info "Artifacts directory: ${artifacts_dir}" # Check artifacts directory if [[ ! -d "${artifacts_dir}" ]]; then add_error "Artifacts directory not found: ${artifacts_dir}" generate_report "${artifacts_dir}" exit 1 fi # Environment validation validate_environment # Find artifacts local jar_files pom_files all_jars # Only validate HDF5 JAR files, exclude dependencies like slf4j jar_files=($(find "${artifacts_dir}" -name "*hdf5*.jar" -not -name "*test*" 2>/dev/null || true)) pom_files=($(find "${artifacts_dir}" -name "pom.xml" 2>/dev/null || true)) all_jars=($(find "${artifacts_dir}" -name "*.jar" 2>/dev/null || true)) # Log what we found log_info "Found ${#all_jars[@]} total JAR file(s), ${#jar_files[@]} HDF5 JAR file(s) to validate" if [[ ${#all_jars[@]} -gt ${#jar_files[@]} ]]; then log_info "Skipping non-HDF5 JAR files (dependencies like slf4j, etc.)" for jar in "${all_jars[@]}"; do if [[ ! "$(basename "$jar")" =~ hdf5 ]]; then log_info " Skipping: $(basename "$jar")" fi done fi # Basic readiness check check_deployment_readiness "${artifacts_dir}" # Validate each JAR file for jar_file in "${jar_files[@]}"; do validate_jar_file "${jar_file}" done # Validate each POM file for pom_file in "${pom_files[@]}"; do validate_pom_file "${pom_file}" done # Version consistency check if [[ ${#pom_files[@]} -gt 0 && ${#jar_files[@]} -gt 0 ]]; then validate_version_consistency "${pom_files[0]}" "${jar_files[@]}" fi # Platform classifier validation if [[ ${#jar_files[@]} -gt 0 ]]; then validate_platform_classifiers "${jar_files[@]}" fi # Maven dependency simulation if [[ ${#pom_files[@]} -gt 0 ]]; then simulate_maven_dependency "${pom_files[0]}" fi # Generate final report generate_report "${artifacts_dir}" exit ${VALIDATION_ERRORS} } # Show usage if no arguments provided if [[ $# -eq 0 ]]; then echo "Usage: $0 " echo echo "Enhanced validation framework for Maven artifacts before deployment" echo echo "This script validates:" echo " - JAR file integrity and content" echo " - POM file structure and compliance" echo " - Version consistency across artifacts" echo " - Platform classifier conventions" echo " - Maven dependency resolution simulation" echo " - Deployment readiness" echo echo "Environment variables:" echo " MAVEN_USERNAME - Maven repository username (optional for validation)" echo " MAVEN_PASSWORD - Maven repository password (optional for validation)" echo exit 1 fi # Run main function with arguments main "$@"