Skip to content
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
477dc97
Bump actions/upload-artifact from 6 to 7
dependabot[bot] Mar 2, 2026
9650b11
Bump github/codeql-action from 4.32.3 to 4.32.4
dependabot[bot] Mar 2, 2026
21e5a02
[BUG] SCRIPT ERROR: Parse Error: Could not resolve external class mem…
ikostan Mar 3, 2026
6ce492a
Merge pull request #449 from ikostan/dependabot/github_actions/action…
ikostan Mar 3, 2026
8306897
Merge pull request #450 from ikostan/dependabot/github_actions/github…
ikostan Mar 3, 2026
f990af2
Update settings.gd
ikostan Mar 3, 2026
b238b07
Merge branch 'parse-error-could-not-resolve-external-class-member-set…
ikostan Mar 3, 2026
0deb225
Update globals.gd
ikostan Mar 3, 2026
4b977b9
Update input_remap_button.gd
ikostan Mar 3, 2026
67ea986
Update settings.gd
ikostan Mar 3, 2026
25c9a0e
issue (bug_risk): Guard for Globals/Globals.settings still calls Glob…
ikostan Mar 3, 2026
ecef73b
Version comment is incorrect and creates inconsistency across workflows.
ikostan Mar 3, 2026
1328cd9
Update scripts/globals.gd
ikostan Mar 3, 2026
71e7fd8
Merge branch 'parse-error-could-not-resolve-external-class-member-set…
ikostan Mar 3, 2026
03a1dd6
Update game_settings_resource.gd
ikostan Mar 3, 2026
b136e3c
Update test_globals.gd
ikostan Mar 3, 2026
5d002c6
Update scripts/input_remap_button.gd
ikostan Mar 3, 2026
1266ea2
Update input_remap_button.gd
ikostan Mar 3, 2026
33c348b
suggestion: Exported settings is always overwritten in _ready, which …
ikostan Mar 4, 2026
cdbfbaf
Create no_error_logs_test.py
ikostan Mar 4, 2026
1124a5c
style: format code with Black and isort
deepsource-autofix[bot] Mar 4, 2026
92fa42f
Create validate_clean_load_test.py
ikostan Mar 4, 2026
dea4852
Merge branch 'parse-error-could-not-resolve-external-class-member-set…
ikostan Mar 4, 2026
3eea81e
style: format code with Black and isort
deepsource-autofix[bot] Mar 4, 2026
86d79c6
Unused import json
ikostan Mar 4, 2026
42616f5
Apply Ruff RUF010 explicit f-string conversion.
ikostan Mar 4, 2026
4660c53
Use explicit UTF-8 when writing artifacts and coverage files.
ikostan Mar 4, 2026
a627706
Use f-string conversion flag instead of str()
ikostan Mar 4, 2026
5815b49
style: format code with Black and isort
deepsource-autofix[bot] Mar 4, 2026
2fe5314
question (bug_risk): Guarding modifiers behind physical_keycode != 0 …
ikostan Mar 4, 2026
8f0136a
Remove redundant commented debug prints in tests
ikostan Mar 4, 2026
0d02fc2
suggestion (testing): Also capture pageerror events to catch uncaught…
ikostan Mar 4, 2026
e0f590f
style: format code with Black and isort
deepsource-autofix[bot] Mar 4, 2026
8aa0bbc
suggestion (testing): Consider making the timeouts less brittle or co…
ikostan Mar 4, 2026
57ced50
Merge branch 'parse-error-could-not-resolve-external-class-member-set…
ikostan Mar 4, 2026
b483a2d
style: format code with Black and isort
deepsource-autofix[bot] Mar 4, 2026
eff433f
question (testing): Clarify whether matching critical error patterns …
ikostan Mar 4, 2026
98e6d12
style: format code with Black and isort
deepsource-autofix[bot] Mar 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/browser_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,18 @@
id: "export"
uses: "firebelley/godot-export@930577654862a320eef793f399ee911b4479efb9"
with:
godot_executable_download_url: "https://github.com/godotengine/godot/releases/download/4.5-stable/Godot_v4.5-stable_linux.x86_64.zip"

Check warning on line 33 in .github/workflows/browser_test.yml

View workflow job for this annotation

GitHub Actions / YAML Lint / build (3.x)

33:91 [line-length] line too long (143 > 90 characters)
godot_export_templates_download_url: "https://github.com/godotengine/godot/releases/download/4.5-stable/Godot_v4.5-stable_export_templates.tpz"

Check warning on line 34 in .github/workflows/browser_test.yml

View workflow job for this annotation

GitHub Actions / YAML Lint / build (3.x)

34:91 [line-length] line too long (153 > 90 characters)
relative_project_path: "./"
relative_export_path: "./export/web_thread_off"
archive_output: false
cache: false
verbose: true
presets_to_export: "Web_thread_off"
use_preset_export_path: true # Move exports to the directory defined in export_presets.cfg

Check warning on line 41 in .github/workflows/browser_test.yml

View workflow job for this annotation

GitHub Actions / YAML Lint / build (3.x)

41:91 [line-length] line too long (101 > 90 characters)
- name: "Flatten Export Directory"
run: |
bash ./.github/scripts/flatten_export.sh "export/web_thread_off" "Web_thread_off"

Check warning on line 44 in .github/workflows/browser_test.yml

View workflow job for this annotation

GitHub Actions / YAML Lint / build (3.x)

44:91 [line-length] line too long (91 > 90 characters)
- name: "List Export Directory Contents"
run: |
ls -la export/web_thread_off
Expand All @@ -54,11 +54,11 @@
python-version: "3.12"
- name: "Cache PIP Dependencies"
uses: "actions/cache@v5"
id: cache

Check warning on line 57 in .github/workflows/browser_test.yml

View workflow job for this annotation

GitHub Actions / YAML Lint / build (3.x)

57:13 [quoted-strings] string value is not quoted with double quotes
with:
path: "~/.cache/pip"
key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}

Check warning on line 60 in .github/workflows/browser_test.yml

View workflow job for this annotation

GitHub Actions / YAML Lint / build (3.x)

60:16 [quoted-strings] string value is not quoted with double quotes
restore-keys: ${{ runner.os }}-pip-

Check warning on line 61 in .github/workflows/browser_test.yml

View workflow job for this annotation

GitHub Actions / YAML Lint / build (3.x)

61:25 [quoted-strings] string value is not quoted with double quotes
- name: "Install Playwright"
run: |
sudo apt-get update && sudo apt-get install -y libxml2-utils
Expand All @@ -66,8 +66,8 @@
- name: "Cache Playwright Browsers"
uses: "actions/cache@v5"
with:
path: ~/.cache/ms-playwright

Check warning on line 69 in .github/workflows/browser_test.yml

View workflow job for this annotation

GitHub Actions / YAML Lint / build (3.x)

69:17 [quoted-strings] string value is not quoted with double quotes
key: ${{ runner.os }}-playwright-${{ hashFiles('**/requirements.txt') }} # Or your dependency file

Check warning on line 70 in .github/workflows/browser_test.yml

View workflow job for this annotation

GitHub Actions / YAML Lint / build (3.x)

70:91 [line-length] line too long (109 > 90 characters)

Check warning on line 70 in .github/workflows/browser_test.yml

View workflow job for this annotation

GitHub Actions / YAML Lint / build (3.x)

70:16 [quoted-strings] string value is not quoted with double quotes
restore-keys: ${{ runner.os }}-playwright-

- name: "Install Playwright Browsers"
Expand Down Expand Up @@ -150,7 +150,7 @@

- name: "Upload LCOV Artifact"
if: always()
uses: "actions/upload-artifact@v6"
uses: "actions/upload-artifact@v7"
with:
name: lcov-report
path: "./coverage/lcov/lcov.info"
Expand All @@ -176,14 +176,14 @@

- name: "Upload Test Report Artifact"
if: always()
uses: "actions/upload-artifact@v6"
uses: "actions/upload-artifact@v7"
with:
name: test-report
path: junit.xml

- name: "Upload Screenshot and Coverage Artifacts"
if: always()
uses: "actions/upload-artifact@v6"
uses: "actions/upload-artifact@v7"
with:
name: test-screenshots
path: |
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ jobs:
run: |
bash ./.github/scripts/patch_index_js.sh "export/web"
- name: "Initialize CodeQL"
uses: "github/codeql-action/init@v4.32.3"
uses: "github/codeql-action/init@v4.32.4"
with:
# yamllint disable rule:quoted-strings
languages: ${{ matrix.language }}
Expand All @@ -74,10 +74,10 @@ jobs:
debug: "false"

- name: "Autobuild (optional for JS but included for completeness)"
uses: "github/codeql-action/autobuild@v4.32.3"
uses: "github/codeql-action/autobuild@v4.32.4"

- name: "Perform CodeQL Analysis"
uses: "github/codeql-action/analyze@v4.32.3"
uses: "github/codeql-action/analyze@v4.32.4"

- name: "Post-scan summary (optional)"
if: "always()" # Run even if previous steps fail
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/gdunit4_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ jobs:
ls -la ${{ steps.find_report.outputs.latest_report }}/
- name: "Upload Test Reports Artifacts"
if: always()
uses: "actions/upload-artifact@v6"
uses: "actions/upload-artifact@v7"
with:
name: gdunit-reports
path: reports/**
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/gut_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ jobs:
ls -la ${{ steps.find_report.outputs.latest_report }}/
- name: "Upload Test Reports Artifacts"
if: always()
uses: "actions/upload-artifact@v6"
uses: "actions/upload-artifact@v7"
with:
name: gut-reports
path: gut-reports/**
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/snyk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:
continue-on-error: true

- name: "Upload Snyk Code SARIF to GitHub"
uses: "github/codeql-action/upload-sarif@ef618feace3c4838ae42b239ab86e8fb46437508"
uses: "github/codeql-action/upload-sarif@0ec47d036c68ae0cf94c629009b1029407111281"
if: "always() && hashFiles('snyk-code.sarif') != ''"
with:
sarif_file: "snyk-code.sarif"
Expand All @@ -54,7 +54,7 @@ jobs:
continue-on-error: true

- name: "Upload Snyk Open Source SARIF to GitHub"
uses: "github/codeql-action/upload-sarif@ef618feace3c4838ae42b239ab86e8fb46437508"
uses: "github/codeql-action/upload-sarif@0ec47d036c68ae0cf94c629009b1029407111281"
if: "always() && hashFiles('snyk-os.sarif') != ''"
with:
sarif_file: "snyk-os.sarif"
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/trivy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
persist-credentials: false
- name: "Run Trivy FS Scan"
uses: "aquasecurity/trivy-action@master"
# uses: "aquasecurity/trivy-action@c1824fd6edce30d7ab345a9989de00bbd46ef284" # Pinned to SHA for v0.34.0
# uses: "aquasecurity/trivy-action@c1824fd6edce30d7ab345a9989de00bbd46ef284"
env: # Suppress version check notification
TRIVY_SKIP_VERSION_CHECK: 'true'
with:
Expand All @@ -36,7 +36,7 @@ jobs:
ignore-unfixed: true # Ignore vulns without fixes available

- name: "Upload Trivy scan results to GitHub Security tab"
uses: "github/codeql-action/upload-sarif@ef618feace3c4838ae42b239ab86e8fb46437508" # Pinned to SHA for v4.32.2
uses: "github/codeql-action/upload-sarif@0ec47d036c68ae0cf94c629009b1029407111281" # Pinned to SHA for v4.32.2
if: always() # Upload even if scan fails
with:
sarif_file: 'trivy-results.sarif'
Comment thread
coderabbitai[bot] marked this conversation as resolved.
File renamed without changes.
5 changes: 0 additions & 5 deletions scripts/game_settings_resource.gd
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,6 @@ extends Resource
# game_settings_resource.gd
@export var difficulty: float = 1.0:
set(value):
if value < 0.5 or value > 2.0:
Globals.log_message(
"Invalid difficulty loaded (" + str(value) + ") - clamping to valid range.",
Globals.LogLevel.WARNING
)
_difficulty = clamp(value, 0.5, 2.0)
get:
return _difficulty
Expand Down
12 changes: 10 additions & 2 deletions scripts/globals.gd
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ enum LogLevel { DEBUG, INFO, WARNING, ERROR, NONE = 4 }
# @export var difficulty: float = 1.0 # Multiplier: 1.0=Normal, <1=Easy, >1=Hard

# Add the resource reference here
@export var settings: GameSettingsResource = preload("res://settings/default_settings.tres")

var settings: GameSettingsResource
# In globals.gd (add after @export vars)
var options_instance: CanvasLayer = null
# var hidden_menu: Node = null
Expand All @@ -37,6 +36,15 @@ var current_input_device: String = "keyboard" # "keyboard" or "gamepad"


func _ready() -> void:
# Load the resource here instead of preloading at the top
settings = load("res://config_resources/default_settings.tres") as GameSettingsResource
if settings == null:
# Use push_error since Globals logging might not be ready
push_error("CRITICAL: 'GameSettingsResource' failed to load at path.")
# Fallback to in-memory defaults so Globals remains operational
settings = GameSettingsResource.new()
settings.current_log_level = LogLevel.WARNING

if Engine.is_editor_hint() or settings.enable_debug_logging:
settings.current_log_level = LogLevel.DEBUG
log_message("Log level set to: " + LogLevel.keys()[settings.current_log_level], LogLevel.DEBUG)
Expand Down
19 changes: 13 additions & 6 deletions scripts/input_remap_button.gd
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,19 @@ func _ready() -> void:
func _on_pressed() -> void:
listening = button_pressed
if listening:
# FIXED: Check the actual current_device to return the correct prompt
text = (
Globals.settings.remap_prompt_keyboard
if current_device == DeviceType.KEYBOARD
else Globals.settings.remap_prompt_gamepad
)
# Safely check if Globals and settings are ready
if is_instance_valid(Globals) and Globals.settings:
# FIXED: Check the actual current_device to return the correct prompt
text = (
Globals.settings.remap_prompt_keyboard
if current_device == DeviceType.KEYBOARD
else Globals.settings.remap_prompt_gamepad
)
else:
# Globals.log_message("'Globals.settings' resource is NULL", Globals.LogLevel.ERROR)
push_error("ERROR: 'Globals.settings' resource is NULL")
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
# Fallback prompt so the UI still reflects that we are listening
text = ("Press a key" if current_device == DeviceType.KEYBOARD else "Press a button")
else:
Comment thread
coderabbitai[bot] marked this conversation as resolved.
update_button_text()

Expand Down
1 change: 1 addition & 0 deletions scripts/key_mapping.gd
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ func _on_reset_pressed() -> void:
# Function resets only the selected device type (keyboard or gamepad)
var device_type: String = "keyboard" if keyboard.button_pressed else "gamepad"
Settings.reset_to_defaults(device_type)
# You must update the UI buttons so they show the new defaults!
update_all_remap_buttons()
Globals.log_message("Resetting " + device_type + " controls.", Globals.LogLevel.DEBUG)
# NEW: Clear critical warning flag when player fixes unbound (once-per-session)
Expand Down
29 changes: 14 additions & 15 deletions scripts/settings.gd
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,9 @@ func _remove_event_from_conflicts(event: InputEvent, conflicts: Array[String]) -
## Deserializes a string back to InputEvent.
## Handles "key:code", "joybtn:index:device", "joyaxis:axis:value:device".
func deserialize_event(serialized: String) -> InputEvent:
# 1. Reject plain integers or empty strings immediately
var event_to_return: InputEvent = null

# 1. Reject invalid prefixes immediately
if not (
serialized.begins_with("key:")
or serialized.begins_with("joybtn:")
Expand All @@ -386,25 +388,22 @@ func deserialize_event(serialized: String) -> InputEvent:

match parts[0]:
"key":
if parts[1].is_valid_int():
if parts.size() >= 2 and parts[1].is_valid_int():
var ev := InputEventKey.new()
ev.physical_keycode = parts[1].to_int()
# Logic for combinations (Shift + Tab etc)
if "shift" in parts:
ev.shift_pressed = true
if "ctrl" in parts:
ev.ctrl_pressed = true
if "alt" in parts: # NEW: Restore Alt
ev.alt_pressed = true
if "meta" in parts: # NEW: Restore Meta
ev.meta_pressed = true
return ev

if ev.physical_keycode != 0:
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
Outdated
ev.shift_pressed = "shift" in parts
ev.ctrl_pressed = "ctrl" in parts
ev.alt_pressed = "alt" in parts
ev.meta_pressed = "meta" in parts
event_to_return = ev
"joybtn":
if parts.size() == 3 and parts[1].is_valid_int() and parts[2].is_valid_int():
var ev := InputEventJoypadButton.new()
ev.button_index = parts[1].to_int()
ev.device = parts[2].to_int()
return ev
event_to_return = ev
"joyaxis":
if (
parts.size() == 4
Expand All @@ -416,9 +415,9 @@ func deserialize_event(serialized: String) -> InputEvent:
ev.axis = parts[1].to_int()
ev.axis_value = parts[2].to_float()
ev.device = parts[3].to_int()
return ev
event_to_return = ev

return null
return event_to_return


## Deserializes a string to an InputEvent and adds it to the specified action.
Expand Down
12 changes: 9 additions & 3 deletions test/gdunit4/test_globals.gd
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@ extends GdUnitTestSuite
var globals: Node
var test_path: String = "user://test_globals.cfg" # Temp for isolation


func before_test() -> void:
## Per-test setup: Instantiate globals.
##
## :rtype: void
# Instantiate the script
globals = auto_free(load("res://scripts/globals.gd").new())

# FIX: Manually initialize the settings resource
# because _ready() hasn't run yet.
globals.settings = GameSettingsResource.new()


func after_test() -> void:
## Per-test cleanup: Remove test file.
Expand All @@ -23,6 +27,7 @@ func after_test() -> void:
if FileAccess.file_exists(test_path):
DirAccess.remove_absolute(test_path)


func test_save_settings_preserves_other_sections() -> void:
## Tests settings save preserves unrelated sections (e.g., "audio").
##
Expand All @@ -42,6 +47,7 @@ func test_save_settings_preserves_other_sections() -> void:
assert_float(config.get_value("Settings", "difficulty", 1.0)).is_equal(1.2)
assert_float(config.get_value("audio", "master_volume", 1.0)).is_equal(0.6)


func test_load_settings_with_other_sections() -> void:
## Tests load ignores/preserves other sections.
##
Expand Down
91 changes: 91 additions & 0 deletions tests/no_error_logs_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Copyright (C) 2025 Egor Kostan
# SPDX-License-Identifier: GPL-3.0-or-later
# tests/no_error_logs_test.py
"""
Console Error Integrity Test (Playwright + UI Automation)
=========================================================

Overview
--------
Verifies that the SkyLockAssault HTML5 build loads without triggering
any 'error' level logs in the browser console.

Test Flow
---------
- Listen to all console messages.
- Navigate to the index page and wait for network idle.
- Wait for window.godotInitialized (Godot _ready() signal).
- Assert that no logs with type="error" exist.
"""

import json
import os
import time

from playwright.sync_api import Page


def test_no_error_logs_after_load(page: Page) -> None:
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
"""
E2E test to ensure zero console errors on initial load.

:param page: The Playwright page object.
:type page: Page
:rtype: None
"""
logs: list[dict[str, str]] = []
cdp_session = None

def on_console(msg) -> None:
"""Capture all console messages for inspection."""
logs.append({"type": msg.type, "text": msg.text})

# Attach the listener before navigation
page.on("console", on_console)

try:
# Start CDP session for coverage (consistent with your other tests)
cdp_session = page.context.new_cdp_session(page)
cdp_session.send("Profiler.enable")
cdp_session.send(
"Profiler.startPreciseCoverage", {"callCount": True, "detailed": True}
)

# Navigate and wait for the game to initialize
page.goto(
"http://localhost:8080/index.html", wait_until="networkidle", timeout=5000
)
page.wait_for_function("() => window.godotInitialized", timeout=5000)

# Allow a short buffer for any delayed post-load errors
page.wait_for_timeout(1000)
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
Outdated

# Filter for error logs
error_logs = [log for log in logs if log["type"] == "error"]

# Detailed assertion message for easier debugging
error_details = "\n".join(
[f"[{err['type']}] {err['text']}" for err in error_logs]
)
assert (
len(error_logs) == 0
), f"Found {len(error_logs)} error(s) in console:\n{error_details}"

except Exception as e:
print(f"Test: 'test_no_error_logs_after_load' failed: {str(e)}")
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
os.makedirs("artifacts", exist_ok=True)
timestamp = int(time.time())
page.screenshot(path=f"artifacts/test_error_logs_failure_{timestamp}.png")

# Save logs for inspection
with open(f"artifacts/test_error_logs_console_{timestamp}.txt", "w") as f:
for log in logs:
f.write(f"[{log['type']}] {log['text']}\n")
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
raise
finally:
if cdp_session:
coverage = cdp_session.send("Profiler.takePreciseCoverage")["result"]
cdp_session.send("Profiler.stopPreciseCoverage")
cdp_session.send("Profiler.disable")
with open("v8_coverage_no_error_logs_test.json", "w") as f:
json.dump(coverage, f)
Loading
Loading