fix: run project scenarios in quality gate
This commit is contained in:
@@ -20,17 +20,17 @@ parser errors, and LLM-generated garbage before committing.
|
|||||||
./tools/quality.sh
|
./tools/quality.sh
|
||||||
|
|
||||||
# Windows PowerShell
|
# Windows PowerShell
|
||||||
pwsh ./tools/quality.ps1
|
& ./tools/quality.ps1
|
||||||
```
|
```
|
||||||
|
|
||||||
### Fast changed-files mode
|
### Fast changed-files mode
|
||||||
|
|
||||||
Only runs `gdformat` / `gdlint` on `.gd` files modified since the last commit.
|
Only runs `gdformat` / `gdlint` on `.gd` files modified since the last commit.
|
||||||
The Godot parser check still runs on the whole project.
|
The Godot dependency check and all project scenarios still run.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./tools/quality.sh --changed
|
./tools/quality.sh --changed
|
||||||
pwsh ./tools/quality.ps1 -Changed
|
& ./tools/quality.ps1 -Changed
|
||||||
```
|
```
|
||||||
|
|
||||||
### Auto-format (not part of the gate)
|
### Auto-format (not part of the gate)
|
||||||
@@ -43,9 +43,10 @@ pwsh ./tools/quality.ps1 -Changed
|
|||||||
|
|
||||||
| Check | Tool | What it detects |
|
| Check | Tool | What it detects |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **gdformat** | `gdformat --check .` | Unformatted code (indentation, spacing) |
|
| **gdformat** | Game-owned GDScript roots | Formatting without rewriting addons or demos |
|
||||||
| **gdlint** | `gdlint .` | Lint violations (unused args, long lines, naming, etc.) |
|
| **gdlint** | Game-owned GDScript roots | Lint violations such as unused arguments and naming |
|
||||||
| **godot-check** | `godot --headless --check-only` | Parser/syntax errors, missing dependencies |
|
| **godot-check** | Headless definitions scenario | Parser errors and missing dependencies |
|
||||||
|
| **scenarios** | Every `tests/*_test.gd` script | Simulation, persistence, navigation, and presentation regressions |
|
||||||
| **gut** | `godot -s gut_cmdln.gd` | Failing GUT unit tests (only if addon exists) |
|
| **gut** | `godot -s gut_cmdln.gd` | Failing GUT unit tests (only if addon exists) |
|
||||||
|
|
||||||
## Output
|
## Output
|
||||||
@@ -58,6 +59,7 @@ QUALITY RESULT: PASS
|
|||||||
gdformat PASS
|
gdformat PASS
|
||||||
gdlint PASS
|
gdlint PASS
|
||||||
godot-check PASS
|
godot-check PASS
|
||||||
|
scenarios PASS
|
||||||
gut SKIPPED
|
gut SKIPPED
|
||||||
|
|
||||||
Full logs:
|
Full logs:
|
||||||
@@ -72,6 +74,7 @@ QUALITY RESULT: FAIL
|
|||||||
gdformat FAIL
|
gdformat FAIL
|
||||||
gdlint FAIL
|
gdlint FAIL
|
||||||
godot-check PASS
|
godot-check PASS
|
||||||
|
scenarios PASS
|
||||||
gut SKIPPED
|
gut SKIPPED
|
||||||
|
|
||||||
res://simulation/SimNPC.gd:42 Unused argument 'delta'
|
res://simulation/SimNPC.gd:42 Unused argument 'delta'
|
||||||
|
|||||||
+44
-15
@@ -13,6 +13,7 @@ New-Item -ItemType Directory -Path $LOG -Force | Out-Null
|
|||||||
$OVERALL = $false
|
$OVERALL = $false
|
||||||
$ERRORS = @()
|
$ERRORS = @()
|
||||||
$FIXES = @()
|
$FIXES = @()
|
||||||
|
$OWNED_GDSCRIPT_ROOTS = @("player", "simulation", "tests", "tools", "world")
|
||||||
|
|
||||||
# -- tool detection -----------------------------------------------------------
|
# -- tool detection -----------------------------------------------------------
|
||||||
function Find-Godot {
|
function Find-Godot {
|
||||||
@@ -26,6 +27,10 @@ function Find-Godot {
|
|||||||
"$env:USERPROFILE\AppData\Local\Godot\godot.exe"
|
"$env:USERPROFILE\AppData\Local\Godot\godot.exe"
|
||||||
)
|
)
|
||||||
foreach ($p in $common) { if (Test-Path $p) { return $p } }
|
foreach ($p in $common) { if (Test-Path $p) { return $p } }
|
||||||
|
$bundled = Get-ChildItem "${env:ProgramFiles(x86)}\Godot" -Filter "Godot*_console.exe" -ErrorAction SilentlyContinue |
|
||||||
|
Sort-Object Name -Descending |
|
||||||
|
Select-Object -First 1
|
||||||
|
if ($bundled) { return $bundled.FullName }
|
||||||
return $null
|
return $null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,24 +91,27 @@ function To-ResPath($p) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Invoke-GodotWithTimeout {
|
function Invoke-GodotWithTimeout {
|
||||||
param([string]$Args)
|
param([string]$ArgumentString)
|
||||||
$psi = New-Object Diagnostics.ProcessStartInfo
|
$psi = New-Object Diagnostics.ProcessStartInfo
|
||||||
$psi.FileName = $GODOT
|
$psi.FileName = $GODOT
|
||||||
$psi.Arguments = $Args
|
$psi.Arguments = $ArgumentString
|
||||||
$psi.RedirectStandardOutput = $true
|
$psi.RedirectStandardOutput = $true
|
||||||
$psi.RedirectStandardError = $true
|
$psi.RedirectStandardError = $true
|
||||||
$psi.UseShellExecute = $false
|
$psi.UseShellExecute = $false
|
||||||
$psi.CreateNoWindow = $true
|
$psi.CreateNoWindow = $true
|
||||||
$p = [Diagnostics.Process]::Start($psi)
|
$p = [Diagnostics.Process]::Start($psi)
|
||||||
|
$outputTask = $p.StandardOutput.ReadToEndAsync()
|
||||||
|
$errorTask = $p.StandardError.ReadToEndAsync()
|
||||||
$exited = $p.WaitForExit(30000)
|
$exited = $p.WaitForExit(30000)
|
||||||
if (-not $exited) {
|
if (-not $exited) {
|
||||||
$p.Kill()
|
$p.Kill()
|
||||||
$output = $p.StandardOutput.ReadToEnd()
|
$p.WaitForExit()
|
||||||
$err = $p.StandardError.ReadToEnd()
|
$output = $outputTask.Result
|
||||||
|
$err = $errorTask.Result
|
||||||
return $output + $err, $null
|
return $output + $err, $null
|
||||||
}
|
}
|
||||||
$output = $p.StandardOutput.ReadToEnd()
|
$output = $outputTask.Result
|
||||||
$err = $p.StandardError.ReadToEnd()
|
$err = $errorTask.Result
|
||||||
return ($output + $err), $p.ExitCode
|
return ($output + $err), $p.ExitCode
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,7 +147,8 @@ if (-not $fmtAvail) {
|
|||||||
Add-Content -Path "$LOG/gdformat.log" -Value $msg -Encoding utf8
|
Add-Content -Path "$LOG/gdformat.log" -Value $msg -Encoding utf8
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$out = Invoke-GdFormat "--check" "."
|
$formatArgs = @("--check") + $OWNED_GDSCRIPT_ROOTS
|
||||||
|
$out = Invoke-GdFormat @formatArgs
|
||||||
$out | Add-Content -Path "$LOG/gdformat.log" -Encoding utf8
|
$out | Add-Content -Path "$LOG/gdformat.log" -Encoding utf8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -175,7 +184,7 @@ if (-not $lintAvail) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$out = Invoke-GdLint "."
|
$out = Invoke-GdLint @OWNED_GDSCRIPT_ROOTS
|
||||||
$out | Add-Content -Path "$LOG/gdlint.log" -Encoding utf8
|
$out | Add-Content -Path "$LOG/gdlint.log" -Encoding utf8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -201,12 +210,12 @@ if ($lintLines) {
|
|||||||
# -- 3. godot headless check --------------------------------------------------
|
# -- 3. godot headless check --------------------------------------------------
|
||||||
$godotResult = "PASS"
|
$godotResult = "PASS"
|
||||||
Set-Content -Path "$LOG/godot-check.log" -Value "" -Encoding utf8
|
Set-Content -Path "$LOG/godot-check.log" -Value "" -Encoding utf8
|
||||||
$godotArgs = "--headless --path `"$ROOT`" --quit"
|
$godotArgs = "--headless --path `"$ROOT`" --script res://tests/simulation_definitions_test.gd"
|
||||||
$godotOutput, $godotExit = Invoke-GodotWithTimeout -Args $godotArgs
|
$godotOutput, $godotExit = Invoke-GodotWithTimeout -ArgumentString $godotArgs
|
||||||
$godotOutput | Out-File -FilePath "$LOG/godot-check.log" -Encoding utf8
|
$godotOutput | Out-File -FilePath "$LOG/godot-check.log" -Encoding utf8
|
||||||
if ($godotExit -eq $null) {
|
if ($godotExit -eq $null) {
|
||||||
$godotResult = "PASS"
|
$godotResult = "FAIL"
|
||||||
Add-Content -Path "$LOG/godot-check.log" -Value "[timed out after 30s - project runs continuously]" -Encoding utf8
|
Add-Content -Path "$LOG/godot-check.log" -Value "[timed out after 30s]" -Encoding utf8
|
||||||
} elseif ($godotExit -eq 0) {
|
} elseif ($godotExit -eq 0) {
|
||||||
$godotResult = "PASS"
|
$godotResult = "PASS"
|
||||||
} else {
|
} else {
|
||||||
@@ -217,12 +226,30 @@ if ($godotExit -eq $null) {
|
|||||||
$FIXES += "Fix Godot parser errors"
|
$FIXES += "Fix Godot parser errors"
|
||||||
}
|
}
|
||||||
|
|
||||||
# -- 4. GUT (optional) --------------------------------------------------------
|
# -- 4. project scenario tests ------------------------------------------------
|
||||||
|
$scenarioResult = "PASS"
|
||||||
|
Set-Content -Path "$LOG/scenarios.log" -Value "" -Encoding utf8
|
||||||
|
$scenarioTests = Get-ChildItem "tests" -Filter "*_test.gd" | Sort-Object Name
|
||||||
|
foreach ($test in $scenarioTests) {
|
||||||
|
Add-Content -Path "$LOG/scenarios.log" -Value "[RUN] $($test.Name)" -Encoding utf8
|
||||||
|
$testArgs = "--headless --path `"$ROOT`" --script res://tests/$($test.Name)"
|
||||||
|
$testOutput, $testExit = Invoke-GodotWithTimeout -ArgumentString $testArgs
|
||||||
|
$testOutput | Add-Content -Path "$LOG/scenarios.log" -Encoding utf8
|
||||||
|
if ($testExit -eq $null -or $testExit -ne 0) {
|
||||||
|
$scenarioResult = "FAIL"
|
||||||
|
$ERRORS += "scenario: $($test.Name) failed or timed out"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($scenarioResult -eq "FAIL") {
|
||||||
|
$FIXES += "Fix failing project scenario tests"
|
||||||
|
}
|
||||||
|
|
||||||
|
# -- 5. GUT (optional) --------------------------------------------------------
|
||||||
$gutResult = "SKIPPED"
|
$gutResult = "SKIPPED"
|
||||||
Set-Content -Path "$LOG/gut.log" -Value "" -Encoding utf8
|
Set-Content -Path "$LOG/gut.log" -Value "" -Encoding utf8
|
||||||
if (Test-Path "addons/gut/gut_cmdln.gd") {
|
if (Test-Path "addons/gut/gut_cmdln.gd") {
|
||||||
$gutArgs = "--headless --path `"$ROOT`" -s addons/gut/gut_cmdln.gd --quit"
|
$gutArgs = "--headless --path `"$ROOT`" -s addons/gut/gut_cmdln.gd --quit"
|
||||||
$gutOutput, $gutExit = Invoke-GodotWithTimeout -Args $gutArgs
|
$gutOutput, $gutExit = Invoke-GodotWithTimeout -ArgumentString $gutArgs
|
||||||
$gutOutput | Out-File -FilePath "$LOG/gut.log" -Encoding utf8
|
$gutOutput | Out-File -FilePath "$LOG/gut.log" -Encoding utf8
|
||||||
if ($gutExit -eq $null) {
|
if ($gutExit -eq $null) {
|
||||||
$gutResult = "FAIL"
|
$gutResult = "FAIL"
|
||||||
@@ -241,7 +268,7 @@ if (Test-Path "addons/gut/gut_cmdln.gd") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# -- summary ------------------------------------------------------------------
|
# -- summary ------------------------------------------------------------------
|
||||||
if ($fmtResult -eq 'FAIL' -or $lintResult -eq 'FAIL' -or $godotResult -eq 'FAIL' -or $gutResult -eq 'FAIL') {
|
if ($fmtResult -eq 'FAIL' -or $lintResult -eq 'FAIL' -or $godotResult -eq 'FAIL' -or $scenarioResult -eq 'FAIL' -or $gutResult -eq 'FAIL') {
|
||||||
$OVERALL = $true
|
$OVERALL = $true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,6 +278,7 @@ Write-Host ""
|
|||||||
Write-Host " gdformat $fmtResult"
|
Write-Host " gdformat $fmtResult"
|
||||||
Write-Host " gdlint $lintResult"
|
Write-Host " gdlint $lintResult"
|
||||||
Write-Host " godot-check $godotResult"
|
Write-Host " godot-check $godotResult"
|
||||||
|
Write-Host " scenarios $scenarioResult"
|
||||||
Write-Host " gut $gutResult"
|
Write-Host " gut $gutResult"
|
||||||
|
|
||||||
$seen = @{}
|
$seen = @{}
|
||||||
@@ -286,6 +314,7 @@ QUALITY RESULT: $resultText
|
|||||||
gdformat $fmtResult
|
gdformat $fmtResult
|
||||||
gdlint $lintResult
|
gdlint $lintResult
|
||||||
godot-check $godotResult
|
godot-check $godotResult
|
||||||
|
scenarios $scenarioResult
|
||||||
gut $gutResult
|
gut $gutResult
|
||||||
"@
|
"@
|
||||||
Set-Content -Path "$LOG/summary.txt" -Value $summary -Encoding utf8
|
Set-Content -Path "$LOG/summary.txt" -Value $summary -Encoding utf8
|
||||||
|
|||||||
+29
-8
@@ -15,6 +15,7 @@ CHANGED=false
|
|||||||
OVERALL=false
|
OVERALL=false
|
||||||
ERRORS=()
|
ERRORS=()
|
||||||
FIXES=()
|
FIXES=()
|
||||||
|
OWNED_GDSCRIPT_ROOTS=(player simulation tests tools world)
|
||||||
|
|
||||||
# -- tool detection -----------------------------------------------------------
|
# -- tool detection -----------------------------------------------------------
|
||||||
find_godot() {
|
find_godot() {
|
||||||
@@ -86,7 +87,7 @@ else
|
|||||||
echo "($bad/$total files need formatting)" >> "$LOG/gdformat.log"
|
echo "($bad/$total files need formatting)" >> "$LOG/gdformat.log"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
$fmt_tool --check . >> "$LOG/gdformat.log" 2>&1 || true
|
$fmt_tool --check "${OWNED_GDSCRIPT_ROOTS[@]}" >> "$LOG/gdformat.log" 2>&1 || true
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -119,7 +120,7 @@ else
|
|||||||
done <<< "$files"
|
done <<< "$files"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
$lint_tool . >> "$LOG/gdlint.log" 2>&1 || true
|
$lint_tool "${OWNED_GDSCRIPT_ROOTS[@]}" >> "$LOG/gdlint.log" 2>&1 || true
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -146,21 +147,20 @@ fi
|
|||||||
# -- 3. godot headless check --------------------------------------------------
|
# -- 3. godot headless check --------------------------------------------------
|
||||||
godot_result="PASS"
|
godot_result="PASS"
|
||||||
: > "$LOG/godot-check.log"
|
: > "$LOG/godot-check.log"
|
||||||
GODOT_CMD="$GODOT --headless --path \"$ROOT\" --check-only --quit"
|
|
||||||
if need_timeout; then
|
if need_timeout; then
|
||||||
if timeout 30 $GODOT_CMD >> "$LOG/godot-check.log" 2>&1; then
|
if timeout 30 "$GODOT" --headless --path "$ROOT" --script res://tests/simulation_definitions_test.gd >> "$LOG/godot-check.log" 2>&1; then
|
||||||
godot_result="PASS"
|
godot_result="PASS"
|
||||||
else
|
else
|
||||||
ec=$?
|
ec=$?
|
||||||
if [[ $ec -eq 124 ]]; then
|
if [[ $ec -eq 124 ]]; then
|
||||||
godot_result="PASS"
|
godot_result="FAIL"
|
||||||
echo "[timed out after 30s — project runs continuously]" >> "$LOG/godot-check.log"
|
echo "[timed out after 30s — project runs continuously]" >> "$LOG/godot-check.log"
|
||||||
else
|
else
|
||||||
godot_result="FAIL"
|
godot_result="FAIL"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
if $GODOT --headless --path "$ROOT" --quit >> "$LOG/godot-check.log" 2>&1; then
|
if "$GODOT" --headless --path "$ROOT" --script res://tests/simulation_definitions_test.gd >> "$LOG/godot-check.log" 2>&1; then
|
||||||
godot_result="PASS"
|
godot_result="PASS"
|
||||||
else
|
else
|
||||||
godot_result="FAIL"
|
godot_result="FAIL"
|
||||||
@@ -173,7 +173,26 @@ if [[ "$godot_result" == "FAIL" ]]; then
|
|||||||
FIXES+=("Fix Godot parser errors")
|
FIXES+=("Fix Godot parser errors")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# -- 4. GUT (optional) --------------------------------------------------------
|
# -- 4. project scenario tests ------------------------------------------------
|
||||||
|
scenario_result="PASS"
|
||||||
|
: > "$LOG/scenarios.log"
|
||||||
|
for test in tests/*_test.gd; do
|
||||||
|
echo "[RUN] $(basename "$test")" >> "$LOG/scenarios.log"
|
||||||
|
if need_timeout; then
|
||||||
|
if ! timeout 30 "$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
|
||||||
|
elif ! "$GODOT" --headless --path "$ROOT" --script "res://$test" >> "$LOG/scenarios.log" 2>&1; then
|
||||||
|
scenario_result="FAIL"
|
||||||
|
ERRORS+=("scenario: $(basename "$test") failed")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [[ "$scenario_result" == "FAIL" ]]; then
|
||||||
|
FIXES+=("Fix failing project scenario tests")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# -- 5. GUT (optional) --------------------------------------------------------
|
||||||
gut_result="SKIPPED"
|
gut_result="SKIPPED"
|
||||||
: > "$LOG/gut.log"
|
: > "$LOG/gut.log"
|
||||||
if [[ -f "addons/gut/gut_cmdln.gd" ]]; then
|
if [[ -f "addons/gut/gut_cmdln.gd" ]]; then
|
||||||
@@ -189,7 +208,7 @@ if [[ -f "addons/gut/gut_cmdln.gd" ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# -- summary ------------------------------------------------------------------
|
# -- summary ------------------------------------------------------------------
|
||||||
if [[ "$fmt_result" == "FAIL" || "$lint_result" == "FAIL" || "$godot_result" == "FAIL" || "$gut_result" == "FAIL" ]]; then
|
if [[ "$fmt_result" == "FAIL" || "$lint_result" == "FAIL" || "$godot_result" == "FAIL" || "$scenario_result" == "FAIL" || "$gut_result" == "FAIL" ]]; then
|
||||||
OVERALL=true
|
OVERALL=true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -203,6 +222,7 @@ echo ""
|
|||||||
echo " gdformat $fmt_result"
|
echo " gdformat $fmt_result"
|
||||||
echo " gdlint $lint_result"
|
echo " gdlint $lint_result"
|
||||||
echo " godot-check $godot_result"
|
echo " godot-check $godot_result"
|
||||||
|
echo " scenarios $scenario_result"
|
||||||
echo " gut $gut_result"
|
echo " gut $gut_result"
|
||||||
|
|
||||||
seen_err=()
|
seen_err=()
|
||||||
@@ -236,6 +256,7 @@ echo ""
|
|||||||
echo " gdformat $fmt_result"
|
echo " gdformat $fmt_result"
|
||||||
echo " gdlint $lint_result"
|
echo " gdlint $lint_result"
|
||||||
echo " godot-check $godot_result"
|
echo " godot-check $godot_result"
|
||||||
|
echo " scenarios $scenario_result"
|
||||||
echo " gut $gut_result"
|
echo " gut $gut_result"
|
||||||
} > "$LOG/summary.txt"
|
} > "$LOG/summary.txt"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user