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>
This commit is contained in:
@@ -184,13 +184,46 @@ Error messages must be:
|
|||||||
- **Clear**: Explain what went wrong
|
- **Clear**: Explain what went wrong
|
||||||
- **Actionable**: Tell user how to fix it
|
- **Actionable**: Tell user how to fix it
|
||||||
- **Contextual**: Include relevant details
|
- **Contextual**: Include relevant details
|
||||||
|
- **Verbose**: Provide comprehensive information by default
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Bad
|
# Bad
|
||||||
die "Error"
|
die "Error"
|
||||||
|
|
||||||
# Good
|
# Good - Verbose with context and solutions
|
||||||
die "Required file not found: ${CONFIG_FILE}. Run setup first."
|
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 <filename>' on individual files for detailed error messages.
|
||||||
```
|
```
|
||||||
|
|
||||||
### Validation
|
### Validation
|
||||||
|
|||||||
@@ -71,6 +71,20 @@ log_error() {
|
|||||||
|
|
||||||
die() {
|
die() {
|
||||||
log_error "$*"
|
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
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,13 +158,46 @@ fail_if_root() {
|
|||||||
# Check for required dependencies at script start
|
# Check for required dependencies at script start
|
||||||
check_dependencies() {
|
check_dependencies() {
|
||||||
local missing=0
|
local missing=0
|
||||||
|
local missing_cmds=()
|
||||||
|
|
||||||
for cmd in "$@"; do
|
for cmd in "$@"; do
|
||||||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||||||
log_error "Required command not found: $cmd"
|
log_error "Required command not found: $cmd"
|
||||||
missing=$((missing + 1))
|
missing=$((missing + 1))
|
||||||
|
missing_cmds+=("$cmd")
|
||||||
fi
|
fi
|
||||||
done
|
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
|
# Timeout wrapper for long-running commands
|
||||||
|
|||||||
@@ -150,16 +150,29 @@ log_info "Checking PHP syntax..."
|
|||||||
|
|
||||||
if command -v php >/dev/null 2>&1; then
|
if command -v php >/dev/null 2>&1; then
|
||||||
php_errors=0
|
php_errors=0
|
||||||
|
failed_files=()
|
||||||
while IFS= read -r -d '' f; do
|
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"
|
log_error "PHP syntax error in: $f"
|
||||||
|
echo " Error details:" >&2
|
||||||
|
echo "$php_output" | sed 's/^/ /' >&2
|
||||||
|
echo "" >&2
|
||||||
php_errors=$((php_errors + 1))
|
php_errors=$((php_errors + 1))
|
||||||
|
failed_files+=("$f")
|
||||||
fi
|
fi
|
||||||
done < <(find src -type f -name '*.php' -print0 2>/dev/null)
|
done < <(find src -type f -name '*.php' -print0 2>/dev/null)
|
||||||
|
|
||||||
if [ "${php_errors}" -eq 0 ]; then
|
if [ "${php_errors}" -eq 0 ]; then
|
||||||
log_info "✓ PHP syntax validation passed"
|
log_info "✓ PHP syntax validation passed"
|
||||||
else
|
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 <filename>' on each failed file for detailed error messages." >&2
|
||||||
die "Found ${php_errors} PHP syntax errors"
|
die "Found ${php_errors} PHP syntax errors"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -139,22 +139,20 @@ for check in "${REQUIRED_CHECKS[@]}"; do
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
log_step "Running: ${check}"
|
log_step "Running: ${check}"
|
||||||
if [ "${VERBOSE}" = "true" ]; then
|
# Always capture output for better error reporting
|
||||||
if "${script}"; then
|
output=""
|
||||||
|
if output=$("${script}" 2>&1); then
|
||||||
log_success "✓ ${check}"
|
log_success "✓ ${check}"
|
||||||
required_passed=$((required_passed + 1))
|
required_passed=$((required_passed + 1))
|
||||||
|
[ "${VERBOSE}" = "true" ] && echo "$output"
|
||||||
else
|
else
|
||||||
log_error "✗ ${check} (FAILED)"
|
log_error "✗ ${check} (FAILED)"
|
||||||
required_failed=$((required_failed + 1))
|
required_failed=$((required_failed + 1))
|
||||||
fi
|
# Show error output for required checks regardless of verbose flag
|
||||||
else
|
echo "" >&2
|
||||||
if "${script}" >/dev/null 2>&1; then
|
echo "Error output:" >&2
|
||||||
log_success "✓ ${check}"
|
echo "$output" >&2
|
||||||
required_passed=$((required_passed + 1))
|
echo "" >&2
|
||||||
else
|
|
||||||
log_error "✗ ${check} (FAILED - run with -v for details)"
|
|
||||||
required_failed=$((required_failed + 1))
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
@@ -170,21 +168,27 @@ for check in "${OPTIONAL_CHECKS[@]}"; do
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
log_step "Running: ${check}"
|
log_step "Running: ${check}"
|
||||||
if [ "${VERBOSE}" = "true" ]; then
|
# Capture output for better error reporting
|
||||||
if "${script}"; then
|
output=""
|
||||||
|
if output=$("${script}" 2>&1); then
|
||||||
log_success "✓ ${check}"
|
log_success "✓ ${check}"
|
||||||
optional_passed=$((optional_passed + 1))
|
optional_passed=$((optional_passed + 1))
|
||||||
|
[ "${VERBOSE}" = "true" ] && echo "$output"
|
||||||
else
|
else
|
||||||
log_warn "✗ ${check} (warnings/issues found)"
|
log_warn "✗ ${check} (warnings/issues found)"
|
||||||
optional_failed=$((optional_failed + 1))
|
optional_failed=$((optional_failed + 1))
|
||||||
fi
|
# 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
|
else
|
||||||
if "${script}" >/dev/null 2>&1; then
|
# Show first few lines of error
|
||||||
log_success "✓ ${check}"
|
echo "" >&2
|
||||||
optional_passed=$((optional_passed + 1))
|
echo "Error summary (run with -v for full details):" >&2
|
||||||
else
|
echo "$output" | head -5 >&2
|
||||||
log_warn "✗ ${check} (warnings/issues found - run with -v for details)"
|
echo "" >&2
|
||||||
optional_failed=$((optional_failed + 1))
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|||||||
@@ -92,7 +92,23 @@ if [ "${#manifest_candidates[@]}" -eq 0 ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "${#manifest_candidates[@]}" -eq 0 ]; then
|
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 <extension> root element" >&2
|
||||||
|
echo "" >&2
|
||||||
|
} >&2
|
||||||
|
fail "No manifest found"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# De-duplicate while preserving order.
|
# De-duplicate while preserving order.
|
||||||
@@ -115,11 +131,21 @@ manifest_candidates=("${unique_candidates[@]}")
|
|||||||
if [ "${#manifest_candidates[@]}" -gt 1 ]; then
|
if [ "${#manifest_candidates[@]}" -gt 1 ]; then
|
||||||
{
|
{
|
||||||
log "ERROR: Multiple manifest candidates detected. Resolve to exactly one primary manifest." >&2
|
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
|
for c in "${manifest_candidates[@]}"; do
|
||||||
log "- ${c}" >&2
|
log " - ${c}" >&2
|
||||||
done
|
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
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -39,21 +39,39 @@ set -euo pipefail
|
|||||||
# Uses git ls-files -z and searches file contents for a literal backslash.
|
# Uses git ls-files -z and searches file contents for a literal backslash.
|
||||||
|
|
||||||
hits=()
|
hits=()
|
||||||
|
hit_lines=()
|
||||||
|
|
||||||
while IFS= read -r -d '' f; do
|
while IFS= read -r -d '' f; do
|
||||||
# Skip common binary files by mime-type
|
# Skip common binary files by mime-type
|
||||||
if file --brief --mime-type "$f" | grep -qE '^(application|audio|image|video)/'; then
|
if file --brief --mime-type "$f" | grep -qE '^(application|audio|image|video)/'; then
|
||||||
continue
|
continue
|
||||||
fi
|
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")
|
hits+=("$f")
|
||||||
|
hit_lines+=("$backslash_lines")
|
||||||
fi
|
fi
|
||||||
done < <(git ls-files -z)
|
done < <(git ls-files -z)
|
||||||
|
|
||||||
if [ "${#hits[@]}" -gt 0 ]; then
|
if [ "${#hits[@]}" -gt 0 ]; then
|
||||||
echo "ERROR: windows_path_literal_detected"
|
echo "ERROR: Windows-style path literals detected" >&2
|
||||||
for h in "${hits[@]}"; do
|
echo "" >&2
|
||||||
echo " - ${h}"
|
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
|
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
|
exit 2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -59,25 +59,44 @@ fi
|
|||||||
failed=0
|
failed=0
|
||||||
checked=0
|
checked=0
|
||||||
failed_files=()
|
failed_files=()
|
||||||
|
failed_errors=()
|
||||||
|
|
||||||
while IFS= read -r -d '' f; do
|
while IFS= read -r -d '' f; do
|
||||||
checked=$((checked+1))
|
checked=$((checked+1))
|
||||||
|
|
||||||
|
# Capture actual error output
|
||||||
|
error_output=""
|
||||||
|
|
||||||
# Use timeout if available to prevent hangs
|
# Use timeout if available to prevent hangs
|
||||||
if command -v timeout >/dev/null 2>&1; then
|
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=1
|
||||||
failed_files+=("$f")
|
failed_files+=("$f")
|
||||||
|
failed_errors+=("$error_output")
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
if ! php -l "$f" >/dev/null 2>&1; then
|
if ! error_output=$(php -l "$f" 2>&1); then
|
||||||
failed=1
|
failed=1
|
||||||
failed_files+=("$f")
|
failed_files+=("$f")
|
||||||
|
failed_errors+=("$error_output")
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
done < <(find "${SRC_DIR}" -type f -name '*.php' -print0)
|
done < <(find "${SRC_DIR}" -type f -name '*.php' -print0)
|
||||||
|
|
||||||
if [ "${failed}" -ne 0 ]; then
|
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 <filename>' 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[@]}"
|
printf '{"status":"fail","error":"php_lint_failed","files_checked":%s,"failed_count":%s,"failed_files":[' "${checked}" "${#failed_files[@]}"
|
||||||
for i in "${!failed_files[@]}"; do
|
for i in "${!failed_files[@]}"; do
|
||||||
|
|||||||
@@ -48,15 +48,39 @@ if [ -z "${files}" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
bad=0
|
bad=0
|
||||||
|
bad_files=()
|
||||||
|
bad_lines=()
|
||||||
|
|
||||||
while IFS= read -r f; do
|
while IFS= read -r f; do
|
||||||
if grep -n $'\t' -- "$f" >/dev/null 2>&1; then
|
# Find lines with tabs and store them
|
||||||
echo "TAB found in $f"
|
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=1
|
||||||
|
bad_files+=("$f")
|
||||||
fi
|
fi
|
||||||
done <<< "${files}"
|
done <<< "${files}"
|
||||||
|
|
||||||
if [ "${bad}" -ne 0 ]; then
|
if [ "${bad}" -ne 0 ]; then
|
||||||
|
echo "" >&2
|
||||||
echo "ERROR: Tabs found in repository files" >&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
|
exit 2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user