12 KiB
| name | description | license | compatibility | metadata | |||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| godot-headless-workflow | AI-agent development loop for Godot 4.x without opening the editor: headless validation (--check-only), project import, gdUnit4 testing, safe hand-editing of text scenes (.tscn), CLI export pipeline for Android/iOS, mobile checklist, and RevenueCat plugin integration. Use when building or modifying a Godot game as a coding agent, running Godot in CI, validating GDScript changes, exporting mobile builds, or integrating in-app purchases. | MIT |
|
|
Godot Headless Workflow (Agent Development Loop)
The complete edit→validate→test→run→export loop for Godot projects without ever opening the editor. Every change must pass the headless gate before you claim it works.
Golden Rules
- Never trust an edit you haven't validated headlessly. A script that looks right but was never parsed is not done.
- GDScript only unless the project already uses C# (see
godot-csharp-patterns). Pin the engine version in the project'sAGENTS.md(e.g.,Godot 4.7.2 stable, GDScript). - Scenes are text (
.tscn/.tres) — editable by hand with care, or better, generated by tool scripts. See §Text Asset Safety. - Never touch
.godot/— it's a regenerable cache. - The editor GUI is optional. Use it (or the package's MCP bridge) only for visual layout iteration; logic, tests, and builds are all CLI work.
Prerequisites
godot --version # e.g. 4.7.stable.official — confirm once per machine/session
The Godot binary must be on PATH in the shell you run commands from (Git Bash on Windows, native bash on Linux). Export templates are needed only for §CLI Export:
| Host | Template install path |
|---|---|
| Windows | %APPDATA%\Godot\export_templates\<engine-version>\ |
| Linux | ~/.local/share/godot/export_templates/<engine-version>/ |
| macOS | ~/Library/Application Support/Godot/export_templates/<engine-version>/ |
Cross-Platform (Windows ↔ Ubuntu hosts)
All Godot CLI flags, gdUnit4 runners (runtest.cmd / runtest.sh), res:// paths, and
export behavior are identical across hosts — projects stay fully portable. Watch only:
- Line endings kill
.shscripts: ifverify.shis checked out with CRLF on Linux it fails with$'\r': command not found. Add to every game repo's.gitattributes:*.sh text eol=lf(and commit it from day one). - Runner extension differs by host —
runtest.cmdon Windows,runtest.shelsewhere; detect or parameterize instead of hardcoding one. - Don't mix toolchain worlds: run Godot + Android SDK/adb natively per OS. Building
inside WSL while your emulator/adb runs on Windows causes device-detection pain.
On Ubuntu, USB debugging needs udev rules for your phone vendor before
adb deviceslists it. - Signing env vars are the same on both hosts — just use absolute paths appropriate
per filesystem in
GODOT_ANDROID_KEYSTORE_PATH.
The Core Loop
edit (.gd / .tscn / project settings)
→ full import (after adding/removing assets or scenes)
→ parse check (every changed script)
→ unit tests (gdUnit4, see references/testing.md)
→ smoke run (boot the game headlessly for N frames)
→ commit (only when everything above exits 0)
→ export (when feature-complete; see references/exporting.md)
Step 1 — Full import (required after asset/scene changes)
godot --headless --path . --import
Imports all resources, generates .godot/ cache, resolves UIDs, and reports load errors.
Run this before anything else in a fresh clone, and whenever you added textures/scenes/fonts.
Exit code ≠ 0 means broken resources — fix before continuing.
Step 2 — Parse-check changed scripts
godot --headless --check-only --script res://scripts/player.gd
Syntax + basic type checking without executing. Fast enough to run per-edit. Note: it does NOT catch broken node paths, missing autoloads, or runtime type errors — those need steps 3–4.
Step 3 — Unit tests (gdUnit4)
Standardize every project on gdUnit4. Tests live in
res://tests/; suites extend GdUnitTestSuite. Run from the project root:
# Windows
addons/gdUnit4/runtest.cmd -a res://tests --continue
# Linux/macOS
./addons/gdUnit4/runtest.sh -a res://tests --continue
Non-zero exit = failures (CI-safe). HTML/XML reports land in res://reports/.
Full patterns — assertions, async tests, parametrized cases — in references/testing.md.
Step 4 — Smoke run
Boot the real game headlessly and let it tick a bounded number of frames:
godot --headless --path . --quit-after 300 # ~300 frames of main scene
Runtime errors (bad autoload order, missing nodes, signal typos) print here and fail loudly. For deeper checks (instantiate specific scenes, assert state), use a SceneTree driver script:
# tools/smoke_test.gd — instantiate target scenes, tick frames, assert they survive
extends SceneTree
func _init() -> void:
call_deferred("_run")
func _run() -> void:
var targets := ["res://scenes/main.tscn"]
var errors := 0
for path in targets:
var ps: PackedScene = load(path)
if ps == null:
push_error("FAILED TO LOAD: " + path)
errors += 1
continue
var node := ps.instantiate()
root.add_child(node)
await process_frame
await process_frame
node.queue_free()
await process_frame
print("SMOKE RESULT: ", "FAIL" if errors > 0 else "PASS")
quit(1 if errors > 0 else 0)
godot --headless --path . -s res://tools/smoke_test.gd
Copy-paste verification gate (put this in every game repo)
#!/usr/bin/env bash
# verify.sh — the agent gate. Run after EVERY batch of edits. All steps must exit 0.
set -e
godot --headless --path . --import
for f in $(git diff --name-only HEAD | grep '\.gd$'); do
godot --headless --check-only --script "res://${f#*/}" || exit 1
done
addons/gdUnit4/runtest.cmd -a res://tests --continue # Linux/macOS: runtest.sh
godot --headless --path . --quit-after 300
echo "ALL CHECKS PASSED"
Text Asset Safety (.tscn / .tres)
Scenes and resources are plain text — readable, diffable, mergeable. Rules:
| Do | Don't |
|---|---|
Read .tscn to learn structure before editing scripts |
Invent uid:// values |
| Edit node properties, positions, exported values in place | Delete or rename .uid sidecar files |
Add [node] blocks mirroring existing syntax exactly |
Reference resources by UID you haven't seen |
| Prefer generating scenes via a tool script (below) | Hand-write complex nested scenes from scratch |
Let Godot regenerate paths after moving files (--import) |
Edit anything under .godot/ |
Since Godot 4.4, scripts carry .uid sidecars and ext_resource entries may point at
uid://…. If both uid and path exist, either resolving is fine — keep the path
attribute when hand-editing and never fabricate UIDs.
When structural changes get risky (new node trees, wiring signals between nodes), generate the scene programmatically instead:
# tools/build_scene.gd — run: godot --headless --path . -s res://tools/build_scene.gd
extends SceneTree
func _init() -> void:
call_deferred("_build")
func _build() -> void:
var root_node := Node2D.new()
root_node.name = "Level1"
var sprite := Sprite2D.new()
sprite.name = "Player"
sprite.position = Vector2(640, 360)
root_node.add_child(sprite)
var packed := PackedScene.new()
packed.pack(root_node)
ResourceSaver.save(packed, "res://scenes/levels/level_1.tscn")
print("SCENE SAVED")
quit(0)
This is deterministic, reviewable, and can't corrupt unrelated scene sections.
CLI Export
One-time setup: create presets (editor UI or by writing export_presets.cfg — see
references/exporting.md for working Android/iOS preset skeletons).
# Windows/Linux desktop
godot --headless --path . --export-release "Windows Desktop" builds/win/game.exe
# Android (AAB for Play Store; APK for sideload/Galaxy Store)
godot --headless --path . --export-release "Android" builds/android/game.aab
# iOS — produces an Xcode project FOLDER, archived later on macOS
godot --headless --path . --export-release "iOS" builds/ios/
Release Android exports read signing config from env vars:
export GODOT_ANDROID_KEYSTORE_PATH=/path/to/release.keystore
export GODOT_ANDROID_KEYSTORE_USER=alias
export GODOT_ANDROID_KEYSTORE_PASSWORD=secret
Details, debugging exports (--export-debug), and the store-submission checklist live in
references/exporting.md.
Mobile Quick Checklist
- Stretch mode
canvas_items+ portrait/landscape locked inproject.godot - Touch input: enable "Emulate Mouse From Touch"; consider Godot 4.7's
VirtualJoystick - Respect notches/safe areas (get visible rect from viewport, anchor UI inside it)
- Budget: mid-range phone should hold 60 fps — profile with
godot-performanceskill before shipping - Min versions when using the RevenueCat plugin below: iOS 15+, Android SDK 24+
In-App Purchases (RevenueCat)
Use the community Godotx RevenueCat plugin (MIT; wraps the official RC iOS/Android SDKs;
built against Godot 4.7-stable). Condensed flow — full playbook in references/revenuecat.md:
- Install via AssetLib ("Godotx RevenueCat") → gives
addons/,ios/plugins/,android/plugins/ - Enable the plugin per export preset; Android additionally needs Gradle Build ON
- Create the app + IAP products in Play Console / App Store Connect first, mirror them in the RevenueCat dashboard, note the platform-specific public API keys
- Init at startup with the platform key → fetch offerings → paywall → purchase → entitlement check
- Test a real sandbox purchase on device before building game systems on top of it
⚠️ Verify exact method names against the addon's own README/API reference at integration time — don't guess API surface (see failure modes below).
Agent Failure Modes (catch these in review)
| Failure mode | Symptom | Gate |
|---|---|---|
| Godot 3 API bleed | onready var, yield(, .instance(), tool alone, export var, string-form connect("x", self, "y") |
`grep -rnE "onready |yield( |
| Unity API bleed (C# projects) | UnityEngine, MonoBehaviour, GetComponent |
Only relevant in .NET projects; prefer GDScript to avoid entirely |
| Invented UIDs / broken refs | Load errors at import | Run --import gate after every scene edit |
| Autoload order bugs | Singleton accessed before init in _init() |
Move access to _ready(); smoke run catches it |
| Wrong callback | Game logic in _process() that needs physics ticks |
Use _physics_process() for movement/forces (godot-physics skill) |
| Signal leaks | Connections to freed nodes; crashes on scene change | Connect in _ready(), disconnect or use CONNECT_ONE_SHOT; review with godot-code-review skill |
| Guessed external APIs | Hallucinated plugin/engine methods | Only cite APIs found in the project's addons/docs or verified via --check-only passing |
Editor vs Headless — When To Use Which
| Task | Tool |
|---|---|
| Script edits, tests, exports, CI, refactors | Headless CLI (this skill) |
| Visual layout polish, animation curves, theme tweaking | Editor GUI (human) or the package's mcp-bridge/ MCP server for agent-driven editor control |
| Import cache issues | Always --import headless, never hand-fix .godot/ |
Related Skills
| Need | Skill |
|---|---|
| Scaffold the project | godot-project-setup |
| GDScript style & architecture decisions | godot-gdscript-patterns, godot-brainstorming |
| Review before merging features | godot-code-review |
| Diagnose failing smoke runs | godot-debugging |
| Ship fast on phones | godot-performance |
| Input handling, save systems, audio, shaders | godot-input, godot-save-systems, godot-audio, godot-shaders-vfx |
References
references/testing.md— gdUnit4 deep dive: suites, assertions, async, CI integrationreferences/exporting.md— export presets, Android/iOS pipelines, store submission checklistreferences/revenuecat.md— RevenueCat plugin integration playbook for mobile games