Initial commit

This commit is contained in:
Crimsofall 2026-06-15 12:56:29 -05:00
commit ed87277ef8
64 changed files with 4639 additions and 0 deletions

372
tools/validate-code.sh Normal file
View file

@ -0,0 +1,372 @@
#!/usr/bin/env bash
set -u -o pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
REPORT_DIR="$ROOT_DIR/reports"
ERROR_REPORT="$REPORT_DIR/errors.md"
GENERATED_REPORT="$REPORT_DIR/generated-files.md"
STATUS_FILE="$REPORT_DIR/customer-code-status.txt"
TMP_DIR=""
FILES_CHECKED=0
WARNINGS=0
ERRORS=0
DETAILS=()
GENERATED_FILES=()
EXCLUDES=(
".git"
".forgejo"
"node_modules"
"vendor"
"Library"
"Temp"
"Logs"
"obj"
"bin"
)
cleanup() {
if [[ -n "$TMP_DIR" && -d "$TMP_DIR" ]]; then
rm -rf "$TMP_DIR"
fi
}
trap cleanup EXIT
record_generated_file() {
local file="$1"
local rel="${file#"$ROOT_DIR"/}"
local existing
for existing in "${GENERATED_FILES[@]}"; do
if [[ "$existing" == "$rel" ]]; then
return
fi
done
GENERATED_FILES+=("$rel")
}
escape_md() {
printf '%s' "$1" | sed 's/|/\\|/g'
}
have_tool() {
command -v "$1" >/dev/null 2>&1
}
ensure_report_dir() {
mkdir -p "$REPORT_DIR" || {
printf 'Failed to create report directory: %s\n' "$REPORT_DIR" >&2
return 1
}
}
add_warning() {
local message="$1"
WARNINGS=$((WARNINGS + 1))
DETAILS+=("- Warning: $(escape_md "$message")")
}
add_error() {
local message="$1"
ERRORS=$((ERRORS + 1))
DETAILS+=("- Error: $(escape_md "$message")")
}
add_ok() {
local message="$1"
DETAILS+=("- OK: $(escape_md "$message")")
}
relative_path() {
local path="$1"
path="${path#"$ROOT_DIR"/}"
printf '%s' "$path"
}
join_by() {
local delimiter="$1"
shift || true
local first=1
local item
for item in "$@"; do
[[ -z "$item" ]] && continue
if [[ $first -eq 1 ]]; then
printf '%s' "$item"
first=0
else
printf '%s%s' "$delimiter" "$item"
fi
done
}
collect_files() {
local find_args=("$ROOT_DIR")
local exclude
if ((${#EXCLUDES[@]} > 0)); then
find_args+=("(")
for exclude in "${EXCLUDES[@]}"; do
find_args+=(-name "$exclude" -o)
done
unset 'find_args[${#find_args[@]}-1]'
find_args+=(")" -prune -o)
fi
find_args+=(-type f -print0)
find "${find_args[@]}"
}
run_check() {
local label="$1"
local file="$2"
shift 2
local stdout_file="$TMP_DIR/stdout.log"
local stderr_file="$TMP_DIR/stderr.log"
local stdout=""
local stderr=""
local combined=""
FILES_CHECKED=$((FILES_CHECKED + 1))
: >"$stdout_file"
: >"$stderr_file"
if "$@" >"$stdout_file" 2>"$stderr_file"; then
add_ok "$label passed: $(relative_path "$file")"
else
stdout="$(tr '\n' ' ' <"$stdout_file" | sed 's/[[:space:]]\+/ /g; s/^ //; s/ $//')"
stderr="$(tr '\n' ' ' <"$stderr_file" | sed 's/[[:space:]]\+/ /g; s/^ //; s/ $//')"
combined="$(join_by ' | ' "${stdout:-}" "${stderr:-}")"
add_error "$label failed: $(relative_path "$file")${combined:+ | $combined}"
fi
}
run_repo_build_checks() {
local sln_files=()
local csproj_files=()
local cmake_file="$ROOT_DIR/CMakeLists.txt"
local tmp_cmake_dir=""
while IFS= read -r -d '' file; do
sln_files+=("$file")
done < <(find "$ROOT_DIR" -path "$ROOT_DIR/.git" -prune -o -path "$ROOT_DIR/.forgejo" -prune -o -name '*.sln' -type f -print0)
while IFS= read -r -d '' file; do
csproj_files+=("$file")
done < <(find "$ROOT_DIR" -path "$ROOT_DIR/.git" -prune -o -path "$ROOT_DIR/.forgejo" -prune -o -name '*.csproj' -type f -print0)
if ((${#sln_files[@]} > 0 || ${#csproj_files[@]} > 0)); then
if have_tool dotnet; then
if ((${#sln_files[@]} > 0)); then
local file
for file in "${sln_files[@]}"; do
run_check "dotnet build" "$file" dotnet build "$file" --nologo --verbosity minimal
done
else
local file
for file in "${csproj_files[@]}"; do
run_check "dotnet build" "$file" dotnet build "$file" --nologo --verbosity minimal
done
fi
else
add_warning "dotnet not installed; skipping C# project validation."
fi
fi
if [[ -f "$cmake_file" ]]; then
if have_tool cmake; then
FILES_CHECKED=$((FILES_CHECKED + 1))
tmp_cmake_dir="$(mktemp -d)"
if cmake -S "$ROOT_DIR" -B "$tmp_cmake_dir" >"$TMP_DIR/stdout.log" 2>"$TMP_DIR/stderr.log"; then
add_ok "cmake configure passed: CMakeLists.txt"
else
local stdout stderr combined
stdout="$(tr '\n' ' ' <"$TMP_DIR/stdout.log" | sed 's/[[:space:]]\+/ /g; s/^ //; s/ $//')"
stderr="$(tr '\n' ' ' <"$TMP_DIR/stderr.log" | sed 's/[[:space:]]\+/ /g; s/^ //; s/ $//')"
combined="$(join_by ' | ' "${stdout:-}" "${stderr:-}")"
add_error "cmake configure failed: CMakeLists.txt${combined:+ | $combined}"
fi
rm -rf "$tmp_cmake_dir"
else
add_warning "cmake not installed; skipping C/C++ validation."
fi
fi
}
validate_file() {
local file="$1"
local base
base="$(basename "$file")"
case "$file" in
*.php)
if have_tool php; then
run_check "php -l" "$file" php -l "$file"
else
add_warning "php not installed; skipping $(relative_path "$file")"
fi
;;
*.py)
if have_tool python3; then
run_check "python3 -m py_compile" "$file" python3 -m py_compile "$file"
else
add_warning "python3 not installed; skipping $(relative_path "$file")"
fi
;;
*.rb)
if have_tool ruby; then
run_check "ruby -c" "$file" ruby -c "$file"
else
add_warning "ruby not installed; skipping $(relative_path "$file")"
fi
;;
*.pl|*.pm)
if have_tool perl; then
run_check "perl -c" "$file" perl -c "$file"
else
add_warning "perl not installed; skipping $(relative_path "$file")"
fi
;;
*.sh|*.bash)
if have_tool bash; then
run_check "bash -n" "$file" bash -n "$file"
else
add_warning "bash not installed; skipping $(relative_path "$file")"
fi
;;
*.js|*.mjs|*.cjs)
if have_tool node; then
run_check "node --check" "$file" node --check "$file"
else
add_warning "node not installed; skipping $(relative_path "$file")"
fi
;;
*.json)
if have_tool jq; then
run_check "jq validation" "$file" jq empty "$file"
else
add_warning "jq not installed; skipping $(relative_path "$file")"
fi
;;
*.yml|*.yaml)
if have_tool yamllint; then
run_check "yamllint" "$file" yamllint -f parsable "$file"
else
add_warning "yamllint not installed; skipping $(relative_path "$file")"
fi
;;
*.html|*.htm)
if have_tool tidy; then
run_check "tidy validation" "$file" tidy -qe "$file"
else
add_warning "tidy not installed; skipping $(relative_path "$file")"
fi
;;
*)
if [[ "$base" == "Dockerfile" ]]; then
FILES_CHECKED=$((FILES_CHECKED + 1))
add_ok "Detected Dockerfile: $(relative_path "$file")"
fi
;;
esac
}
write_generated_files_report() {
{
printf '# Generated Files\n\n'
if ((${#GENERATED_FILES[@]} == 0)); then
printf 'No report files were generated.\n'
else
local file
for file in "${GENERATED_FILES[@]}"; do
printf -- '- %s\n' "$file"
done
fi
} >"$GENERATED_REPORT" || return 1
record_generated_file "$GENERATED_REPORT"
}
write_status_file() {
local status_line="NO CUSTOMER CODE ERRORS FOUND"
if ((ERRORS > 0)); then
status_line="CUSTOMER CODE ERRORS FOUND"
fi
printf '%s\n' "$status_line" >"$STATUS_FILE" || return 1
record_generated_file "$STATUS_FILE"
}
write_error_report() {
local status_line="NO CUSTOMER CODE ERRORS FOUND"
if ((ERRORS > 0)); then
status_line="CUSTOMER CODE ERRORS FOUND"
fi
{
printf '# Validation Summary\n\n'
printf '%s\n\n' "$status_line"
printf -- '- Files Checked: %s\n' "$FILES_CHECKED"
printf -- '- Warnings: %s\n' "$WARNINGS"
printf -- '- Errors: %s\n\n' "$ERRORS"
printf '## Detailed Results\n\n'
if ((${#DETAILS[@]} == 0)); then
printf 'No validation steps were run.\n'
else
printf '%s\n' "${DETAILS[@]}"
fi
} >"$ERROR_REPORT" || return 1
record_generated_file "$ERROR_REPORT"
}
print_terminal_summary() {
local status_line="NO CUSTOMER CODE ERRORS FOUND"
if ((ERRORS > 0)); then
status_line="CUSTOMER CODE ERRORS FOUND"
fi
printf '===== VALIDATION SUMMARY =====\n'
printf '%s\n' "$status_line"
printf 'Files checked: %s\n' "$FILES_CHECKED"
printf 'Warnings: %s\n' "$WARNINGS"
printf 'Errors: %s\n' "$ERRORS"
printf 'Reports written:\n'
local file
for file in "${GENERATED_FILES[@]}"; do
printf -- '- %s\n' "$file"
done
}
main() {
ensure_report_dir || return 1
TMP_DIR="$(mktemp -d)" || {
printf 'Failed to create temporary directory.\n' >&2
return 1
}
while IFS= read -r -d '' file; do
validate_file "$file"
done < <(collect_files)
run_repo_build_checks
write_status_file || return 1
write_error_report || return 1
write_generated_files_report || return 1
print_terminal_summary
return 0
}
main "$@"