From aaa6560d7430f4edfcb0cff2f0d5e4d121648de6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 3 Jan 2026 23:16:45 +0000 Subject: [PATCH] Implement verbose error feedback across all scripts - Enhanced die() function with environment context and stack trace - Added detailed error output with line numbers and file paths - Improved check_dependencies with installation guides - Enhanced PHP syntax validation with full error details - Added verbose error messages to manifest, tabs, and paths validations - Updated validate_all to show error summaries (full output in verbose mode) - Updated smoke_test with detailed PHP error reporting - Updated ENTERPRISE.md with verbose error examples - All error messages now provide actionable troubleshooting steps Co-authored-by: jmiller-moko <230051081+jmiller-moko@users.noreply.github.com> --- scripts/ENTERPRISE.md | 37 +++++++++++++++++++-- scripts/lib/common.sh | 49 ++++++++++++++++++++++++++- scripts/run/smoke_test.sh | 15 ++++++++- scripts/run/validate_all.sh | 60 ++++++++++++++++++---------------- scripts/validate/manifest.sh | 34 ++++++++++++++++--- scripts/validate/paths.sh | 26 ++++++++++++--- scripts/validate/php_syntax.sh | 23 +++++++++++-- scripts/validate/tabs.sh | 28 ++++++++++++++-- 8 files changed, 228 insertions(+), 44 deletions(-) diff --git a/scripts/ENTERPRISE.md b/scripts/ENTERPRISE.md index b40fef0..a89e28a 100644 --- a/scripts/ENTERPRISE.md +++ b/scripts/ENTERPRISE.md @@ -184,13 +184,46 @@ Error messages must be: - **Clear**: Explain what went wrong - **Actionable**: Tell user how to fix it - **Contextual**: Include relevant details +- **Verbose**: Provide comprehensive information by default ```bash # Bad die "Error" -# Good -die "Required file not found: ${CONFIG_FILE}. Run setup first." +# Good - Verbose with context and solutions +die "Required file not found: ${CONFIG_FILE} + + Current directory: $(pwd) + Expected location: ./config/${CONFIG_FILE} + + To fix: + 1. Run setup script: ./scripts/setup.sh + 2. Or create the file manually: touch config/${CONFIG_FILE} + " +``` + +### Error Output + +- Always show full error output for failed operations +- Include line numbers and file paths +- Show error summaries with troubleshooting steps +- Provide installation guides for missing dependencies + +Example verbose error from validation: +``` +ERROR: PHP syntax validation failed +Files checked: 90 +Files with errors: 2 + +Failed files and errors: + File: src/test.php + Error: Parse error: syntax error, unexpected '}' in src/test.php on line 42 + + File: src/helper.php + Error: Parse error: syntax error, unexpected T_STRING in src/helper.php on line 15 + +To fix: Review and correct the syntax errors in the files listed above. +Run 'php -l ' on individual files for detailed error messages. ``` ### Validation diff --git a/scripts/lib/common.sh b/scripts/lib/common.sh index 056e884..ea365f4 100755 --- a/scripts/lib/common.sh +++ b/scripts/lib/common.sh @@ -71,6 +71,20 @@ log_error() { die() { log_error "$*" + if [ "${VERBOSE_ERRORS:-true}" = "true" ]; then + echo "" >&2 + echo "Stack trace (last 10 commands):" >&2 + if [ -n "${BASH_VERSION:-}" ]; then + history | tail -10 >&2 2>/dev/null || true + fi + echo "" >&2 + echo "Environment:" >&2 + echo " PWD: $(pwd)" >&2 + echo " USER: ${USER:-unknown}" >&2 + echo " SHELL: ${SHELL:-unknown}" >&2 + echo " CI: ${CI:-false}" >&2 + echo "" >&2 + fi exit 1 } @@ -144,13 +158,46 @@ fail_if_root() { # Check for required dependencies at script start check_dependencies() { local missing=0 + local missing_cmds=() + for cmd in "$@"; do if ! command -v "$cmd" >/dev/null 2>&1; then log_error "Required command not found: $cmd" missing=$((missing + 1)) + missing_cmds+=("$cmd") fi done - [ "$missing" -eq 0 ] || die "Missing $missing required command(s)" + + if [ "$missing" -gt 0 ]; then + echo "" >&2 + echo "Missing required dependencies:" >&2 + for cmd in "${missing_cmds[@]}"; do + echo " - $cmd" >&2 + done + echo "" >&2 + echo "Installation guides:" >&2 + for cmd in "${missing_cmds[@]}"; do + case "$cmd" in + python3) + echo " python3: apt-get install python3 (Debian/Ubuntu) or brew install python3 (macOS)" >&2 + ;; + git) + echo " git: apt-get install git (Debian/Ubuntu) or brew install git (macOS)" >&2 + ;; + php) + echo " php: apt-get install php-cli (Debian/Ubuntu) or brew install php (macOS)" >&2 + ;; + xmllint) + echo " xmllint: apt-get install libxml2-utils (Debian/Ubuntu) or brew install libxml2 (macOS)" >&2 + ;; + *) + echo " $cmd: Please install via your system package manager" >&2 + ;; + esac + done + echo "" >&2 + die "Missing $missing required command(s)" + fi } # Timeout wrapper for long-running commands diff --git a/scripts/run/smoke_test.sh b/scripts/run/smoke_test.sh index afc3c96..66bec88 100755 --- a/scripts/run/smoke_test.sh +++ b/scripts/run/smoke_test.sh @@ -150,16 +150,29 @@ log_info "Checking PHP syntax..." if command -v php >/dev/null 2>&1; then php_errors=0 +failed_files=() while IFS= read -r -d '' f; do -if ! php -l "$f" >/dev/null 2>&1; then +if ! php_output=$(php -l "$f" 2>&1); then log_error "PHP syntax error in: $f" +echo " Error details:" >&2 +echo "$php_output" | sed 's/^/ /' >&2 +echo "" >&2 php_errors=$((php_errors + 1)) +failed_files+=("$f") fi done < <(find src -type f -name '*.php' -print0 2>/dev/null) if [ "${php_errors}" -eq 0 ]; then log_info "✓ PHP syntax validation passed" else +echo "Summary of PHP syntax errors:" >&2 +echo " Total errors: ${php_errors}" >&2 +echo " Failed files:" >&2 +for f in "${failed_files[@]}"; do +echo " - $f" >&2 +done +echo "" >&2 +echo "To fix: Run 'php -l ' on each failed file for detailed error messages." >&2 die "Found ${php_errors} PHP syntax errors" fi else diff --git a/scripts/run/validate_all.sh b/scripts/run/validate_all.sh index 64473b8..7149176 100755 --- a/scripts/run/validate_all.sh +++ b/scripts/run/validate_all.sh @@ -139,22 +139,20 @@ for check in "${REQUIRED_CHECKS[@]}"; do fi log_step "Running: ${check}" - if [ "${VERBOSE}" = "true" ]; then - if "${script}"; then - log_success "✓ ${check}" - required_passed=$((required_passed + 1)) - else - log_error "✗ ${check} (FAILED)" - required_failed=$((required_failed + 1)) - fi + # Always capture output for better error reporting + output="" + if output=$("${script}" 2>&1); then + log_success "✓ ${check}" + required_passed=$((required_passed + 1)) + [ "${VERBOSE}" = "true" ] && echo "$output" else - if "${script}" >/dev/null 2>&1; then - log_success "✓ ${check}" - required_passed=$((required_passed + 1)) - else - log_error "✗ ${check} (FAILED - run with -v for details)" - required_failed=$((required_failed + 1)) - fi + log_error "✗ ${check} (FAILED)" + required_failed=$((required_failed + 1)) + # Show error output for required checks regardless of verbose flag + echo "" >&2 + echo "Error output:" >&2 + echo "$output" >&2 + echo "" >&2 fi done @@ -170,21 +168,27 @@ for check in "${OPTIONAL_CHECKS[@]}"; do fi log_step "Running: ${check}" - if [ "${VERBOSE}" = "true" ]; then - if "${script}"; then - log_success "✓ ${check}" - optional_passed=$((optional_passed + 1)) - else - log_warn "✗ ${check} (warnings/issues found)" - optional_failed=$((optional_failed + 1)) - fi + # Capture output for better error reporting + output="" + if output=$("${script}" 2>&1); then + log_success "✓ ${check}" + optional_passed=$((optional_passed + 1)) + [ "${VERBOSE}" = "true" ] && echo "$output" else - if "${script}" >/dev/null 2>&1; then - log_success "✓ ${check}" - optional_passed=$((optional_passed + 1)) + log_warn "✗ ${check} (warnings/issues found)" + optional_failed=$((optional_failed + 1)) + # Show brief error summary for optional checks, full output in verbose + if [ "${VERBOSE}" = "true" ]; then + echo "" >&2 + echo "Error output:" >&2 + echo "$output" >&2 + echo "" >&2 else - log_warn "✗ ${check} (warnings/issues found - run with -v for details)" - optional_failed=$((optional_failed + 1)) + # Show first few lines of error + echo "" >&2 + echo "Error summary (run with -v for full details):" >&2 + echo "$output" | head -5 >&2 + echo "" >&2 fi fi done diff --git a/scripts/validate/manifest.sh b/scripts/validate/manifest.sh index 05e1ef6..1574adc 100755 --- a/scripts/validate/manifest.sh +++ b/scripts/validate/manifest.sh @@ -92,7 +92,23 @@ if [ "${#manifest_candidates[@]}" -eq 0 ]; then fi if [ "${#manifest_candidates[@]}" -eq 0 ]; then - fail "No Joomla manifest XML found under ${SRC_DIR}" + { + echo "ERROR: No Joomla manifest XML found under ${SRC_DIR}" >&2 + echo "" >&2 + echo "Expected manifest file patterns:" >&2 + echo " - Template: ${SRC_DIR}/templateDetails.xml" >&2 + echo " - Package: ${SRC_DIR}/**/pkg_*.xml" >&2 + echo " - Component: ${SRC_DIR}/**/com_*.xml" >&2 + echo " - Module: ${SRC_DIR}/**/mod_*.xml" >&2 + echo " - Plugin: ${SRC_DIR}/**/plg_*.xml" >&2 + echo "" >&2 + echo "Troubleshooting:" >&2 + echo " 1. Verify the source directory exists: ls -la ${SRC_DIR}" >&2 + echo " 2. Check for XML files: find ${SRC_DIR} -name '*.xml'" >&2 + echo " 3. Ensure manifest contains root element" >&2 + echo "" >&2 + } >&2 + fail "No manifest found" fi # De-duplicate while preserving order. @@ -115,11 +131,21 @@ manifest_candidates=("${unique_candidates[@]}") if [ "${#manifest_candidates[@]}" -gt 1 ]; then { log "ERROR: Multiple manifest candidates detected. Resolve to exactly one primary manifest." >&2 - log "Candidates:" >&2 + log "" >&2 + log "Found ${#manifest_candidates[@]} candidates:" >&2 for c in "${manifest_candidates[@]}"; do - log "- ${c}" >&2 + log " - ${c}" >&2 done - } + log "" >&2 + log "Resolution options:" >&2 + log " 1. Remove redundant manifest files" >&2 + log " 2. Move extra manifests outside ${SRC_DIR}" >&2 + log " 3. Rename non-primary manifests to not match patterns (templateDetails.xml, pkg_*.xml, etc.)" >&2 + log "" >&2 + log "For package extensions, only the top-level package manifest should be in ${SRC_DIR}." >&2 + log "Child extension manifests should be in subdirectories." >&2 + log "" >&2 + } >&2 exit 1 fi diff --git a/scripts/validate/paths.sh b/scripts/validate/paths.sh index 95ec84e..1a7f0b5 100755 --- a/scripts/validate/paths.sh +++ b/scripts/validate/paths.sh @@ -39,21 +39,39 @@ set -euo pipefail # Uses git ls-files -z and searches file contents for a literal backslash. hits=() +hit_lines=() + while IFS= read -r -d '' f; do # Skip common binary files by mime-type if file --brief --mime-type "$f" | grep -qE '^(application|audio|image|video)/'; then continue fi - if grep -F $'\\' -- "$f" >/dev/null 2>&1; then + # Find lines with backslashes and collect details + if backslash_lines=$(grep -n -F $'\\' -- "$f" 2>/dev/null); then hits+=("$f") + hit_lines+=("$backslash_lines") fi done < <(git ls-files -z) if [ "${#hits[@]}" -gt 0 ]; then - echo "ERROR: windows_path_literal_detected" - for h in "${hits[@]}"; do - echo " - ${h}" + echo "ERROR: Windows-style path literals detected" >&2 + echo "" >&2 + echo "Found backslashes in ${#hits[@]} file(s):" >&2 + for i in "${!hits[@]}"; do + echo "" >&2 + echo " File: ${hits[$i]}" >&2 + echo " Lines with backslashes:" >&2 + echo "${hit_lines[$i]}" | head -5 | sed 's/^/ /' >&2 + if [ "$(echo "${hit_lines[$i]}" | wc -l)" -gt 5 ]; then + echo " ... and $(($(echo "${hit_lines[$i]}" | wc -l) - 5)) more" >&2 + fi done + echo "" >&2 + echo "To fix:" >&2 + echo " 1. Run: ./scripts/fix/paths.sh" >&2 + echo " 2. Or manually replace backslashes (\\) with forward slashes (/)" >&2 + echo " 3. Ensure paths use POSIX separators for cross-platform compatibility" >&2 + echo "" >&2 exit 2 fi diff --git a/scripts/validate/php_syntax.sh b/scripts/validate/php_syntax.sh index ff5801b..cbb6988 100755 --- a/scripts/validate/php_syntax.sh +++ b/scripts/validate/php_syntax.sh @@ -59,25 +59,44 @@ fi failed=0 checked=0 failed_files=() +failed_errors=() while IFS= read -r -d '' f; do checked=$((checked+1)) + # Capture actual error output + error_output="" + # Use timeout if available to prevent hangs if command -v timeout >/dev/null 2>&1; then - if ! timeout "${TIMEOUT}" php -l "$f" >/dev/null 2>&1; then + if ! error_output=$(timeout "${TIMEOUT}" php -l "$f" 2>&1); then failed=1 failed_files+=("$f") + failed_errors+=("$error_output") fi else - if ! php -l "$f" >/dev/null 2>&1; then + if ! error_output=$(php -l "$f" 2>&1); then failed=1 failed_files+=("$f") + failed_errors+=("$error_output") fi fi done < <(find "${SRC_DIR}" -type f -name '*.php' -print0) if [ "${failed}" -ne 0 ]; then + echo "ERROR: PHP syntax validation failed" >&2 + echo "Files checked: ${checked}" >&2 + echo "Files with errors: ${#failed_files[@]}" >&2 + echo "" >&2 + echo "Failed files and errors:" >&2 + for i in "${!failed_files[@]}"; do + echo " File: ${failed_files[$i]}" >&2 + echo " Error: ${failed_errors[$i]}" >&2 + echo "" >&2 + done + echo "" >&2 + echo "To fix: Review and correct the syntax errors in the files listed above." >&2 + echo "Run 'php -l ' on individual files for detailed error messages." >&2 { printf '{"status":"fail","error":"php_lint_failed","files_checked":%s,"failed_count":%s,"failed_files":[' "${checked}" "${#failed_files[@]}" for i in "${!failed_files[@]}"; do diff --git a/scripts/validate/tabs.sh b/scripts/validate/tabs.sh index a472267..0798ba9 100755 --- a/scripts/validate/tabs.sh +++ b/scripts/validate/tabs.sh @@ -48,15 +48,39 @@ if [ -z "${files}" ]; then fi bad=0 +bad_files=() +bad_lines=() + while IFS= read -r f; do - if grep -n $'\t' -- "$f" >/dev/null 2>&1; then - echo "TAB found in $f" + # Find lines with tabs and store them + if tab_lines=$(grep -n $'\t' -- "$f" 2>/dev/null); then + echo "TAB found in $f" >&2 + echo " Lines with tabs:" >&2 + echo "$tab_lines" | head -5 | sed 's/^/ /' >&2 + if [ "$(echo "$tab_lines" | wc -l)" -gt 5 ]; then + echo " ... and $(($(echo "$tab_lines" | wc -l) - 5)) more" >&2 + fi + echo "" >&2 bad=1 + bad_files+=("$f") fi done <<< "${files}" if [ "${bad}" -ne 0 ]; then + echo "" >&2 echo "ERROR: Tabs found in repository files" >&2 + echo "" >&2 + echo "YAML specification forbids tab characters." >&2 + echo "Found tabs in ${#bad_files[@]} file(s):" >&2 + for f in "${bad_files[@]}"; do + echo " - $f" >&2 + done + echo "" >&2 + echo "To fix:" >&2 + echo " 1. Run: ./scripts/fix/tabs.sh" >&2 + echo " 2. Or manually replace tabs with spaces in your editor" >&2 + echo " 3. Configure your editor to use spaces (not tabs) for YAML files" >&2 + echo "" >&2 exit 2 fi