set -euo pipefail
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
readonly GENERATED_DIR="${PROJECT_ROOT}/generated"
readonly ONTOLOGY_DIR="${PROJECT_ROOT}/ontologies"
readonly LOG_DIR="${PROJECT_ROOT}/logs"
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly BLUE='\033[0;34m'
readonly CYAN='\033[0;36m'
readonly NC='\033[0m'
VERBOSE=false
QUICK=false
mkdir -p "${LOG_DIR}"
print_pass() { echo -e "${GREEN}✅ PASS${NC}: $*"; }
print_fail() { echo -e "${RED}❌ FAIL${NC}: $*"; }
print_info() { echo -e "${BLUE}ℹ️ INFO${NC}: $*"; }
print_warn() { echo -e "${YELLOW}⚠️ WARN${NC}: $*"; }
print_step() { echo -e "${CYAN}▶${NC} $*"; }
usage() {
cat << 'EOF'
ln_ctrl Verification Script - Verify Generated Code
Runs verification checks on generated artifacts without triggering sync.
Usage: ./verify.sh [options]
Options:
-q, --quick Quick verification (skip expensive checks)
-v, --verbose Enable verbose output
-h, --help Show this help message
Verification Steps:
1. Check generated directory exists
2. Verify file structure
3. Run cargo check (if Rust code generated)
4. Run cargo test (if tests exist)
5. Verify hash consistency (determinism check)
Examples:
./verify.sh # Full verification
./verify.sh --quick # Quick checks only
./verify.sh --verbose # Detailed output
Exit codes:
0 - Verification passed
1 - Verification failed
2 - Error (missing files, invalid setup)
EOF
}
check_generated_dir() {
print_step "Checking generated directory..."
if [[ ! -d "${GENERATED_DIR}" ]]; then
print_fail "Generated directory not found: ${GENERATED_DIR}"
print_info "Run ./run.sh first to generate code"
return 1
fi
local file_count
file_count=$(find "${GENERATED_DIR}" -type f 2>/dev/null | wc -l)
if [[ $file_count -eq 0 ]]; then
print_fail "Generated directory is empty"
return 1
fi
print_pass "Found ${file_count} generated files"
return 0
}
verify_file_structure() {
print_step "Verifying file structure..."
local failures=0
local expected_items=(
"Cargo.toml"
"src"
)
for item in "${expected_items[@]}"; do
local item_path="${GENERATED_DIR}/${item}"
if [[ -e "$item_path" ]]; then
if [[ "$VERBOSE" == "true" ]]; then
print_info "Found: ${item}"
fi
else
if [[ "$VERBOSE" == "true" ]]; then
print_warn "Missing: ${item}"
fi
fi
done
print_pass "File structure verified"
return 0
}
run_cargo_check() {
print_step "Running cargo check..."
local cargo_toml="${GENERATED_DIR}/Cargo.toml"
if [[ ! -f "$cargo_toml" ]]; then
print_info "No Cargo.toml found, skipping cargo check"
return 0
fi
local log_file="${LOG_DIR}/cargo-check-$(date +%Y%m%d-%H%M%S).log"
if [[ "$VERBOSE" == "true" ]]; then
if cargo check --manifest-path "$cargo_toml" 2>&1 | tee "$log_file"; then
print_pass "Cargo check passed"
return 0
else
print_fail "Cargo check failed (see ${log_file})"
return 1
fi
else
if cargo check --manifest-path "$cargo_toml" > "$log_file" 2>&1; then
print_pass "Cargo check passed"
return 0
else
print_fail "Cargo check failed (see ${log_file})"
tail -n 20 "$log_file"
return 1
fi
fi
}
run_cargo_test() {
print_step "Running cargo test..."
if [[ "$QUICK" == "true" ]]; then
print_info "Skipping tests (quick mode)"
return 0
fi
local cargo_toml="${GENERATED_DIR}/Cargo.toml"
if [[ ! -f "$cargo_toml" ]]; then
print_info "No Cargo.toml found, skipping cargo test"
return 0
fi
local log_file="${LOG_DIR}/cargo-test-$(date +%Y%m%d-%H%M%S).log"
if [[ "$VERBOSE" == "true" ]]; then
if cargo test --manifest-path "$cargo_toml" 2>&1 | tee "$log_file"; then
print_pass "All tests passed"
return 0
else
print_fail "Some tests failed (see ${log_file})"
return 1
fi
else
if cargo test --manifest-path "$cargo_toml" > "$log_file" 2>&1; then
local test_count
test_count=$(grep -oP '\d+(?= passed)' "$log_file" | tail -1 || echo "0")
print_pass "All ${test_count} tests passed"
return 0
else
print_fail "Some tests failed (see ${log_file})"
tail -n 20 "$log_file"
return 1
fi
fi
}
verify_hash_consistency() {
print_step "Verifying hash consistency..."
if [[ "$QUICK" == "true" ]]; then
print_info "Skipping hash verification (quick mode)"
return 0
fi
local hash_file="${GENERATED_DIR}/.ggen-hash"
local current_hash
current_hash=$(find "${GENERATED_DIR}" -type f ! -path "*/target/*" ! -name ".ggen-hash" -exec sha256sum {} \; 2>/dev/null | sort | sha256sum | cut -d' ' -f1)
if [[ -f "$hash_file" ]]; then
local stored_hash
stored_hash=$(cat "$hash_file")
if [[ "$current_hash" == "$stored_hash" ]]; then
print_pass "Hash consistency verified (deterministic generation)"
else
print_warn "Hash mismatch (non-deterministic generation detected)"
if [[ "$VERBOSE" == "true" ]]; then
print_info "Stored: ${stored_hash}"
print_info "Current: ${current_hash}"
fi
fi
else
echo "$current_hash" > "$hash_file"
print_info "Baseline hash stored"
fi
return 0
}
print_summary() {
local failures="$1"
echo ""
echo "=============================================="
echo "ln_ctrl Verification Summary"
echo "=============================================="
echo "Project: ${PROJECT_ROOT}"
echo "Generated: ${GENERATED_DIR}"
echo "Timestamp: $(date -u +"%Y-%m-%dT%H:%M:%SZ")"
echo "----------------------------------------------"
if [[ $failures -eq 0 ]]; then
echo -e "${GREEN}RESULT: PASS${NC}"
echo "=============================================="
echo ""
return 0
else
echo -e "${RED}RESULT: FAIL (${failures} failures)${NC}"
echo "=============================================="
echo ""
return 1
fi
}
main() {
while [[ $# -gt 0 ]]; do
case "$1" in
-q|--quick)
QUICK=true
shift
;;
-v|--verbose)
VERBOSE=true
shift
;;
-h|--help)
usage
exit 0
;;
*)
echo "Unknown option: $1"
usage
exit 2
;;
esac
done
echo ""
echo "=============================================="
echo "ln_ctrl Verification"
echo "=============================================="
echo "Verifying generated artifacts..."
echo "----------------------------------------------"
echo ""
local failures=0
check_generated_dir || ((failures++))
if [[ $failures -eq 0 ]]; then
verify_file_structure || ((failures++))
run_cargo_check || ((failures++))
run_cargo_test || ((failures++))
verify_hash_consistency || ((failures++))
else
print_fail "Cannot proceed with verification (generated directory check failed)"
fi
print_summary "$failures"
}
main "$@"