544 lines
17 KiB
Bash
Executable File
544 lines
17 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# quality.sh — Local quality gate for GDScript projects.
|
|
# Usage: ./tools/quality.sh [--changed]
|
|
# --changed Only check changed .gd files (gdformat/gdlint only)
|
|
set -uo pipefail
|
|
IFS=$'\n\t'
|
|
|
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
cd "$ROOT" || exit 1
|
|
LOG="logs/quality/latest"
|
|
mkdir -p "$LOG"
|
|
GODOT_PROFILE="$ROOT/logs/quality/godot_profile"
|
|
mkdir -p "$GODOT_PROFILE"
|
|
|
|
CHANGED=false
|
|
[[ "${1:-}" == "--changed" ]] && CHANGED=true
|
|
OVERALL=false
|
|
ERRORS=()
|
|
FIXES=()
|
|
OWNED_GDSCRIPT_ROOTS=(player simulation tests tools world)
|
|
CROSS_PLATFORM_PLUGIN_FILES=(
|
|
addons/terrain_3d/terrain.gdextension
|
|
addons/terrain_3d/bin/libterrain.macos.debug.framework/libterrain.macos.debug
|
|
addons/terrain_3d/bin/libterrain.macos.release.framework/libterrain.macos.release
|
|
addons/terrain_3d/bin/libterrain.windows.debug.x86_64.dll
|
|
addons/terrain_3d/bin/libterrain.windows.release.x86_64.dll
|
|
)
|
|
REQUIRED_GODOT_SERIES="4.7"
|
|
RUNNING_ON_MACOS=false
|
|
[[ "${OSTYPE:-}" == darwin* ]] && RUNNING_ON_MACOS=true
|
|
|
|
# -- tool detection -----------------------------------------------------------
|
|
console_godot_path() {
|
|
local candidate="${1:-}"
|
|
[[ -z "$candidate" ]] && return 1
|
|
candidate="${candidate%\"}"
|
|
candidate="${candidate#\"}"
|
|
candidate="${candidate//\\//}"
|
|
if [[ "$candidate" == *.exe ]]; then
|
|
local console_candidate="${candidate%.exe}_console.exe"
|
|
if [[ -f "$console_candidate" ]]; then echo "$console_candidate"; return 0; fi
|
|
fi
|
|
if [[ -f "$candidate" ]]; then echo "$candidate"; return 0; fi
|
|
return 1
|
|
}
|
|
|
|
find_project_godot() {
|
|
local metadata="$ROOT/.godot/editor/project_metadata.cfg"
|
|
[[ -f "$metadata" ]] || return 1
|
|
local line path
|
|
line="$(grep -m 1 '^executable_path=' "$metadata" 2>/dev/null || true)"
|
|
[[ -n "$line" ]] || return 1
|
|
path="${line#executable_path=}"
|
|
path="${path%\"}"
|
|
path="${path#\"}"
|
|
console_godot_path "$path"
|
|
}
|
|
|
|
find_godot() {
|
|
if [[ -n "${GODOT_BIN:-}" ]]; then
|
|
if console_godot_path "$GODOT_BIN"; then return 0; fi
|
|
echo "ERROR: GODOT_BIN does not point to a Godot executable: $GODOT_BIN" >&2
|
|
return 1
|
|
fi
|
|
find_project_godot && return 0
|
|
for c in godot godot4; do command -v "$c" &>/dev/null && echo "$c" && return 0; done
|
|
console_godot_path "/Applications/Godot.app/Contents/MacOS/Godot" && return 0
|
|
return 1
|
|
}
|
|
|
|
has_python_module() {
|
|
local module="$1"
|
|
local python_command
|
|
for python_command in python python3; do
|
|
if command -v "$python_command" &>/dev/null &&
|
|
"$python_command" -m "$module" --help &>/dev/null 2>&1; then
|
|
return 0
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
run_python_module() {
|
|
local module="$1"
|
|
shift
|
|
local python_command
|
|
for python_command in python python3; do
|
|
if command -v "$python_command" &>/dev/null &&
|
|
"$python_command" -m "$module" --help &>/dev/null 2>&1; then
|
|
"$python_command" -m "$module" "$@"
|
|
return $?
|
|
fi
|
|
done
|
|
return 127
|
|
}
|
|
|
|
has_gdformat() {
|
|
[[ -x "$ROOT/.venv/bin/gdformat" ]] && return 0
|
|
command -v gdformat &>/dev/null && return 0
|
|
has_python_module gdtoolkit.formatter
|
|
}
|
|
|
|
run_gdformat() {
|
|
if [[ -x "$ROOT/.venv/bin/gdformat" ]]; then
|
|
"$ROOT/.venv/bin/gdformat" "$@"
|
|
elif command -v gdformat &>/dev/null; then
|
|
gdformat "$@"
|
|
else
|
|
run_python_module gdtoolkit.formatter "$@"
|
|
fi
|
|
}
|
|
|
|
has_gdlint() {
|
|
[[ -x "$ROOT/.venv/bin/gdlint" ]] && return 0
|
|
command -v gdlint &>/dev/null && return 0
|
|
has_python_module gdtoolkit.linter
|
|
}
|
|
|
|
run_gdlint() {
|
|
if [[ -x "$ROOT/.venv/bin/gdlint" ]]; then
|
|
"$ROOT/.venv/bin/gdlint" "$@"
|
|
elif command -v gdlint &>/dev/null; then
|
|
gdlint "$@"
|
|
else
|
|
run_python_module gdtoolkit.linter "$@"
|
|
fi
|
|
}
|
|
|
|
changed_gd() {
|
|
{
|
|
git diff --name-only HEAD -- '*.gd' 2>/dev/null
|
|
git ls-files --others --exclude-standard -- '*.gd' 2>/dev/null
|
|
} | sort -u || true
|
|
}
|
|
|
|
res_path() {
|
|
local p="$1"
|
|
p="${p//\\//}"
|
|
if [[ "$p" == "$ROOT"* ]]; then p="${p#"$ROOT"/}"; fi
|
|
p="${p#./}"
|
|
echo "res://$p"
|
|
}
|
|
|
|
contains_element() {
|
|
local e; for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done; return 1
|
|
}
|
|
|
|
is_allowed_scenario_diagnostic() {
|
|
local diagnostic="$1"
|
|
|
|
# Godot 4.7 can fail to read the system certificate store on macOS when HOME
|
|
# is intentionally isolated. Scenario tests do not perform network access.
|
|
if $RUNNING_ON_MACOS &&
|
|
[[ "$diagnostic" == 'ERROR: Condition "ret != noErr" is true. Returning: ""' ]]; then
|
|
return 0
|
|
fi
|
|
|
|
# Pinned Terrain3D 1.0.2 invokes this Godot 4.7 compatibility method while
|
|
# loading its vendored GDExtension. Remove this exception with that vendor fix.
|
|
if [[ "$diagnostic" == "WARNING: instance_reset_physics_interpolation() is deprecated." ]]; then
|
|
return 0
|
|
fi
|
|
|
|
# Godot 4.7's dummy headless renderer retains exactly one DummyShader RID at
|
|
# process teardown. A different RID type or count remains a gate failure.
|
|
if [[ "$diagnostic" == "ERROR: 1 RID allocations of type 'N13RendererDummy15MaterialStorage11DummyShaderE' were leaked at exit." ]]; then
|
|
return 0
|
|
fi
|
|
|
|
return 1
|
|
}
|
|
|
|
run_with_timeout() {
|
|
local seconds="$1"
|
|
shift
|
|
if command -v timeout &>/dev/null; then
|
|
timeout "$seconds" "$@"
|
|
return $?
|
|
fi
|
|
|
|
local marker="$LOG/.timeout-$$-${RANDOM}"
|
|
: > "$marker"
|
|
"$@" &
|
|
local command_pid=$!
|
|
(
|
|
sleep "$seconds"
|
|
if kill -0 "$command_pid" 2>/dev/null; then
|
|
echo "timeout" > "$marker"
|
|
kill "$command_pid" 2>/dev/null || true
|
|
sleep 2
|
|
kill -KILL "$command_pid" 2>/dev/null || true
|
|
fi
|
|
) &
|
|
local watchdog_pid=$!
|
|
|
|
wait "$command_pid"
|
|
local status=$?
|
|
kill "$watchdog_pid" 2>/dev/null || true
|
|
wait "$watchdog_pid" 2>/dev/null || true
|
|
if [[ -s "$marker" ]]; then
|
|
status=124
|
|
fi
|
|
rm -f "$marker"
|
|
return "$status"
|
|
}
|
|
|
|
# -- godot --------------------------------------------------------------------
|
|
GODOT="$(find_godot)" || true
|
|
if [[ -z "$GODOT" ]]; then
|
|
echo "ERROR: Godot binary not found. Set GODOT_BIN or add godot/godot4 to PATH."
|
|
exit 1
|
|
fi
|
|
if ! GODOT_VERSION="$("$GODOT" --version 2>&1)"; then
|
|
echo "ERROR: Could not query the Godot version from '$GODOT'."
|
|
exit 1
|
|
fi
|
|
GODOT_VERSION="${GODOT_VERSION%%$'\n'*}"
|
|
GODOT_VERSION="${GODOT_VERSION%$'\r'}"
|
|
case "$GODOT_VERSION" in
|
|
"$REQUIRED_GODOT_SERIES"|"$REQUIRED_GODOT_SERIES".*|"$REQUIRED_GODOT_SERIES"-*) ;;
|
|
*)
|
|
echo "ERROR: Godot $REQUIRED_GODOT_SERIES.x is required; found '$GODOT_VERSION'."
|
|
exit 1
|
|
;;
|
|
esac
|
|
GODOT_ENV=(env "HOME=$GODOT_PROFILE" "APPDATA=$GODOT_PROFILE" "LOCALAPPDATA=$GODOT_PROFILE")
|
|
|
|
# -- 0. Cross-platform plugin payload -----------------------------------------
|
|
platform_assets_result="PASS"
|
|
: > "$LOG/platform-assets.log"
|
|
for plugin_file in "${CROSS_PLATFORM_PLUGIN_FILES[@]}"; do
|
|
if [[ ! -f "$plugin_file" ]]; then
|
|
platform_assets_result="FAIL"
|
|
echo "Missing $plugin_file" >> "$LOG/platform-assets.log"
|
|
ERRORS+=("platform-assets: missing res://$plugin_file")
|
|
fi
|
|
done
|
|
if [[ "$platform_assets_result" == "FAIL" ]]; then
|
|
FIXES+=("Restore the complete pinned Terrain3D 1.0.2 package")
|
|
fi
|
|
|
|
# -- 1. Godot import bootstrap ------------------------------------------------
|
|
import_result="PASS"
|
|
: > "$LOG/godot-import.log"
|
|
class_cache="$ROOT/.godot/global_script_class_cache.cfg"
|
|
needs_import=false
|
|
[[ ! -f "$class_cache" ]] && needs_import=true
|
|
if [[ -f "addons/gut/gut_cmdln.gd" ]] && ! grep -q '"class": &"GutTest"' "$class_cache" 2>/dev/null; then
|
|
needs_import=true
|
|
fi
|
|
[[ -n "$(changed_gd)" ]] && needs_import=true
|
|
if $needs_import; then
|
|
run_with_timeout 60 "${GODOT_ENV[@]}" "$GODOT" --headless --path "$ROOT" --import >> "$LOG/godot-import.log" 2>&1
|
|
import_exit=$?
|
|
if [[ $import_exit -ne 0 ]]; then
|
|
import_result="FAIL"
|
|
if [[ $import_exit -eq 124 ]]; then
|
|
echo "[timed out after 60s]" >> "$LOG/godot-import.log"
|
|
fi
|
|
fi
|
|
fi
|
|
if [[ ! -f "$class_cache" ]] || ! grep -q '"class": &"SimNPC"' "$class_cache"; then
|
|
import_result="FAIL"
|
|
fi
|
|
if [[ -f "addons/gut/gut_cmdln.gd" ]] && ! grep -q '"class": &"GutTest"' "$class_cache" 2>/dev/null; then
|
|
import_result="FAIL"
|
|
fi
|
|
if grep -qE "SCRIPT ERROR:|Parse Error:|Failed to load script" "$LOG/godot-import.log"; then
|
|
import_result="FAIL"
|
|
fi
|
|
if [[ "$import_result" == "FAIL" ]]; then
|
|
ERRORS+=("godot-import: failed to build the global class cache")
|
|
FIXES+=("Fix Godot import errors before running headless checks")
|
|
fi
|
|
|
|
# -- 2. gdformat --------------------------------------------------------------
|
|
fmt_result="PASS"
|
|
fmt_failed=false
|
|
: > "$LOG/gdformat.log"
|
|
if ! has_gdformat; then
|
|
fmt_result="FAIL"
|
|
echo "gdformat not found — install requirements-dev.txt (see docs/local_quality_gate.md)" > "$LOG/gdformat.log"
|
|
ERRORS+=("gdformat: required formatter not found")
|
|
FIXES+=("Install the pinned dev tools from requirements-dev.txt")
|
|
else
|
|
if $CHANGED; then
|
|
files=$(changed_gd)
|
|
if [[ -z "$files" ]]; then
|
|
echo "No changed .gd files to check." > "$LOG/gdformat.log"
|
|
else
|
|
total=0; bad=0
|
|
while IFS= read -r f; do
|
|
[[ -f "$f" ]] || continue
|
|
total=$((total+1))
|
|
if ! run_gdformat --check "$f" >> "$LOG/gdformat.log" 2>&1; then
|
|
bad=$((bad+1))
|
|
fmt_failed=true
|
|
fi
|
|
done <<< "$files"
|
|
echo "($bad/$total files need formatting)" >> "$LOG/gdformat.log"
|
|
fi
|
|
else
|
|
if ! run_gdformat --check "${OWNED_GDSCRIPT_ROOTS[@]}" >> "$LOG/gdformat.log" 2>&1; then
|
|
fmt_failed=true
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if $fmt_failed; then
|
|
fmt_result="FAIL"
|
|
fi
|
|
if grep -q "would reformat" "$LOG/gdformat.log" 2>/dev/null; then
|
|
while IFS= read -r line; do
|
|
line="${line#would reformat }"
|
|
rp=$(res_path "$line")
|
|
ERRORS+=("$rp needs formatting")
|
|
done < <(grep "would reformat" "$LOG/gdformat.log")
|
|
FIXES+=("Run gdformat to auto-format files")
|
|
elif $fmt_failed; then
|
|
ERRORS+=("gdformat: formatter exited non-zero; see $LOG/gdformat.log")
|
|
FIXES+=("Fix the gdformat error before continuing")
|
|
fi
|
|
|
|
# -- 3. gdlint ----------------------------------------------------------------
|
|
lint_result="PASS"
|
|
lint_failed=false
|
|
: > "$LOG/gdlint.log"
|
|
if ! has_gdlint; then
|
|
lint_result="FAIL"
|
|
echo "gdlint not found — install requirements-dev.txt (see docs/local_quality_gate.md)" > "$LOG/gdlint.log"
|
|
ERRORS+=("gdlint: required linter not found")
|
|
FIXES+=("Install the pinned dev tools from requirements-dev.txt")
|
|
else
|
|
if $CHANGED; then
|
|
files=$(changed_gd)
|
|
if [[ -z "$files" ]]; then
|
|
echo "No changed .gd files to lint." > "$LOG/gdlint.log"
|
|
else
|
|
while IFS= read -r f; do
|
|
[[ -f "$f" ]] || continue
|
|
if ! run_gdlint "$f" >> "$LOG/gdlint.log" 2>&1; then
|
|
lint_failed=true
|
|
fi
|
|
done <<< "$files"
|
|
fi
|
|
else
|
|
if ! run_gdlint "${OWNED_GDSCRIPT_ROOTS[@]}" >> "$LOG/gdlint.log" 2>&1; then
|
|
lint_failed=true
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if $lint_failed; then
|
|
lint_result="FAIL"
|
|
fi
|
|
if grep -qE "^[^ ]+:[0-9]+:" "$LOG/gdlint.log" 2>/dev/null; then
|
|
while IFS= read -r line; do
|
|
[[ "$line" =~ ^([^:]+):([0-9]+):([0-9]+):[^:]+:(.*) ]] || continue
|
|
file="${BASH_REMATCH[1]}"
|
|
lineno="${BASH_REMATCH[2]}"
|
|
msg="${BASH_REMATCH[4]}"
|
|
msg="${msg## }"
|
|
rp=$(res_path "$file")
|
|
ERRORS+=("$rp:$lineno $msg")
|
|
done < <(grep -E "^[^ ]+:[0-9]+:" "$LOG/gdlint.log")
|
|
|
|
while IFS= read -r line; do
|
|
[[ "$line" =~ ^([^:]+):([0-9]+):([0-9]+):([^:]+):(.*) ]] || continue
|
|
rule="${BASH_REMATCH[4]}"
|
|
fname=$(basename "${BASH_REMATCH[1]}")
|
|
FIXES+=("Fix $rule in $fname")
|
|
done < <(grep -E "^[^ ]+:[0-9]+:" "$LOG/gdlint.log")
|
|
elif $lint_failed; then
|
|
ERRORS+=("gdlint: linter exited non-zero; see $LOG/gdlint.log")
|
|
FIXES+=("Fix the gdlint error before continuing")
|
|
fi
|
|
|
|
# -- 4. godot headless check --------------------------------------------------
|
|
godot_result="PASS"
|
|
: > "$LOG/godot-check.log"
|
|
if run_with_timeout 30 "${GODOT_ENV[@]}" "$GODOT" --headless --path "$ROOT" --script res://tests/simulation_definitions_test.gd >> "$LOG/godot-check.log" 2>&1; then
|
|
godot_result="PASS"
|
|
else
|
|
ec=$?
|
|
godot_result="FAIL"
|
|
if [[ $ec -eq 124 ]]; then
|
|
echo "[timed out after 30s]" >> "$LOG/godot-check.log"
|
|
fi
|
|
fi
|
|
if grep -qE "SCRIPT ERROR:|Parse Error:|Failed to load script" "$LOG/godot-check.log"; then
|
|
godot_result="FAIL"
|
|
fi
|
|
if [[ "$godot_result" == "FAIL" ]]; then
|
|
while IFS= read -r errline; do
|
|
ERRORS+=("godot: $errline")
|
|
done < <(grep -iE "error|warning|parse|syntax" "$LOG/godot-check.log" 2>/dev/null | head -20)
|
|
FIXES+=("Fix Godot parser errors")
|
|
fi
|
|
|
|
# -- 5. project scenario tests ------------------------------------------------
|
|
scenario_result="PASS"
|
|
: > "$LOG/scenarios.log"
|
|
for test in tests/*_test.gd; do
|
|
echo "[RUN] $(basename "$test")" >> "$LOG/scenarios.log"
|
|
if ! run_with_timeout 30 "${GODOT_ENV[@]}" "$GODOT" --headless --path "$ROOT" --script "res://$test" >> "$LOG/scenarios.log" 2>&1; then
|
|
scenario_result="FAIL"
|
|
ERRORS+=("scenario: $(basename "$test") failed or timed out")
|
|
fi
|
|
done
|
|
compatibility_test="tests/jajce_presentation_quality_test.gd"
|
|
compatibility_label="$(basename "$compatibility_test") (gl_compatibility)"
|
|
echo "[RUN] $compatibility_label" >> "$LOG/scenarios.log"
|
|
if ! run_with_timeout 30 "${GODOT_ENV[@]}" "$GODOT" --headless --rendering-method gl_compatibility --path "$ROOT" --script "res://$compatibility_test" >> "$LOG/scenarios.log" 2>&1; then
|
|
scenario_result="FAIL"
|
|
ERRORS+=("scenario: $compatibility_label failed or timed out")
|
|
fi
|
|
unexpected_scenario_diagnostics=()
|
|
current_scenario="unknown scenario"
|
|
while IFS= read -r raw_line; do
|
|
raw_line="${raw_line%$'\r'}"
|
|
if [[ "$raw_line" == "[RUN] "* ]]; then
|
|
current_scenario="${raw_line#* }"
|
|
continue
|
|
fi
|
|
diagnostic="$raw_line"
|
|
diagnostic="${diagnostic#"${diagnostic%%[![:space:]]*}"}"
|
|
diagnostic="${diagnostic%"${diagnostic##*[![:space:]]}"}"
|
|
if [[ ! "$diagnostic" =~ ^((SCRIPT[[:space:]])?ERROR|WARNING): ]]; then
|
|
continue
|
|
fi
|
|
if ! is_allowed_scenario_diagnostic "$diagnostic"; then
|
|
unexpected_scenario_diagnostics+=("$current_scenario: $diagnostic")
|
|
fi
|
|
done < "$LOG/scenarios.log"
|
|
if [[ ${#unexpected_scenario_diagnostics[@]} -gt 0 ]]; then
|
|
scenario_result="FAIL"
|
|
ERRORS+=(
|
|
"scenario: Godot reported ${#unexpected_scenario_diagnostics[@]} unallowlisted ERROR/WARNING diagnostic(s)"
|
|
)
|
|
for diagnostic in "${unexpected_scenario_diagnostics[@]:0:20}"; do
|
|
ERRORS+=("scenario: $diagnostic")
|
|
done
|
|
fi
|
|
if grep -qE "SCRIPT ERROR:|Parse Error:|Failed to load script" "$LOG/scenarios.log"; then
|
|
scenario_result="FAIL"
|
|
ERRORS+=("scenario: Godot reported a script load or parse error")
|
|
fi
|
|
if [[ "$scenario_result" == "FAIL" ]]; then
|
|
FIXES+=("Fix failing project scenario tests")
|
|
fi
|
|
|
|
# -- 6. GUT -------------------------------------------------------------------
|
|
gut_result="PASS"
|
|
: > "$LOG/gut.log"
|
|
if [[ ! -f "addons/gut/gut_cmdln.gd" ]]; then
|
|
gut_result="FAIL"
|
|
ERRORS+=("gut: pinned addon is missing")
|
|
FIXES+=("Restore addons/gut from pinned GUT v9.7.1")
|
|
else
|
|
gut_command=(
|
|
"${GODOT_ENV[@]}" "$GODOT" --headless --path "$ROOT"
|
|
-s addons/gut/gut_cmdln.gd -gexit -gdisable_colors
|
|
)
|
|
run_with_timeout 30 "${gut_command[@]}" >> "$LOG/gut.log" 2>&1
|
|
gut_exit=$?
|
|
if [[ $gut_exit -eq 0 ]]; then
|
|
gut_result="PASS"
|
|
else
|
|
gut_result="FAIL"
|
|
FIXES+=("Fix failing GUT tests")
|
|
fi
|
|
if grep -qE "Some GUT class_names have not been imported|SCRIPT ERROR:|Parse Error:|Failed to load script" "$LOG/gut.log"; then
|
|
gut_result="FAIL"
|
|
ERRORS+=("gut: addon or unit tests failed to load")
|
|
FIXES+=("Run Godot 4.7 headless import and fix GUT load errors")
|
|
fi
|
|
fi
|
|
|
|
# -- summary ------------------------------------------------------------------
|
|
if [[ "$platform_assets_result" == "FAIL" || "$import_result" == "FAIL" ||
|
|
"$fmt_result" == "FAIL" || "$lint_result" == "FAIL" ||
|
|
"$godot_result" == "FAIL" || "$scenario_result" == "FAIL" ||
|
|
"$gut_result" == "FAIL" ]]; then
|
|
OVERALL=true
|
|
fi
|
|
|
|
echo ""
|
|
if $OVERALL; then
|
|
echo "QUALITY RESULT: FAIL"
|
|
else
|
|
echo "QUALITY RESULT: PASS"
|
|
fi
|
|
echo "Godot: $GODOT_VERSION"
|
|
echo ""
|
|
echo " platform-assets $platform_assets_result"
|
|
echo " godot-import $import_result"
|
|
echo " gdformat $fmt_result"
|
|
echo " gdlint $lint_result"
|
|
echo " godot-check $godot_result"
|
|
echo " scenarios $scenario_result"
|
|
echo " gut $gut_result"
|
|
|
|
seen_err=("__quality_sentinel__")
|
|
for e in "${ERRORS[@]:-}"; do
|
|
[[ -z "$e" ]] && continue
|
|
contains_element "$e" "${seen_err[@]}" && continue
|
|
seen_err+=("$e")
|
|
echo " $e"
|
|
done
|
|
|
|
if $OVERALL && [[ ${#FIXES[@]} -gt 0 ]]; then
|
|
echo ""
|
|
echo " NEXT FIX:"
|
|
seen_fix=("__quality_sentinel__")
|
|
idx=1
|
|
for fix in "${FIXES[@]:-}"; do
|
|
[[ -z "$fix" ]] && continue
|
|
contains_element "$fix" "${seen_fix[@]}" && continue
|
|
seen_fix+=("$fix")
|
|
echo " $idx. $fix"
|
|
idx=$((idx+1))
|
|
done
|
|
fi
|
|
|
|
echo ""
|
|
echo "Full logs:"
|
|
echo " $LOG/"
|
|
echo ""
|
|
|
|
{
|
|
echo "QUALITY RESULT: $($OVERALL && echo FAIL || echo PASS)"
|
|
echo "Godot: $GODOT_VERSION"
|
|
echo ""
|
|
echo " platform-assets $platform_assets_result"
|
|
echo " godot-import $import_result"
|
|
echo " gdformat $fmt_result"
|
|
echo " gdlint $lint_result"
|
|
echo " godot-check $godot_result"
|
|
echo " scenarios $scenario_result"
|
|
echo " gut $gut_result"
|
|
} > "$LOG/summary.txt"
|
|
|
|
$OVERALL && exit 1 || exit 0
|