Skip to content

Commit 5344dec

Browse files
committed
GitHub Actions: Test project exporting on CI
This allows finding issues in headless project export early on, including when exporting for a dedicated server. We also use this opportunity to check whether the audiovisual output between the project being run from its files and the exported PCK matches (it should always be a perfect match, assuming the same GPU is used for both runs). This can be used to catch audiovisual discrepancies, which could indicate a bug in the export process.
1 parent eb3d6d8 commit 5344dec

File tree

112 files changed

+2915
-1
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

112 files changed

+2915
-1
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
name: Export Godot project
2+
description: Export a test Godot project.
3+
4+
inputs:
5+
bin:
6+
description: The path to the Godot executable
7+
required: true
8+
9+
runs:
10+
using: composite
11+
steps:
12+
- name: Build test GDExtension
13+
shell: sh
14+
run: |
15+
cd misc/test_project/addons/test_extension/
16+
git clone --depth=1 https://github.com/godotengine/godot-cpp.git src/godot-cpp/
17+
scons target=template_debug
18+
scons target=template_release
19+
cd ../../../../
20+
21+
- name: Import resources and export project
22+
shell: sh
23+
run: |
24+
echo "Importing resources"
25+
${{ inputs.bin }} --headless --path misc/test_project/ --import 2>&1 | tee log.txt || true
26+
27+
echo "Exporting project for Linux (PCK)"
28+
${{ inputs.bin }} --headless --path misc/test_project/ --export-pack "Linux" /tmp/test_project.pck 2>&1 | tee log.txt || true
29+
GODOT_CHECK_CI_LOG_ALL_ERRORS=1 misc/scripts/check_ci_log.py log.txt
30+
31+
echo "Exporting project for Linux (ZIP)"
32+
${{ inputs.bin }} --headless --path misc/test_project/ --export-pack "Linux" /tmp/test_project.zip 2>&1 | tee log.txt || true
33+
GODOT_CHECK_CI_LOG_ALL_ERRORS=1 misc/scripts/check_ci_log.py log.txt
34+
35+
echo "Exporting project for Linux as dedicated server (PCK)"
36+
${{ inputs.bin }} --headless --path misc/test_project/ --export-pack "Linux Server" /tmp/test_project_server.pck 2>&1 | tee log.txt || true
37+
GODOT_CHECK_CI_LOG_ALL_ERRORS=1 misc/scripts/check_ci_log.py log.txt
38+
39+
- name: Run project files from folder
40+
shell: sh
41+
run: |
42+
# For all project runs, wait several frames before quitting to ensure we don't quit while audio is still playing,
43+
# as this would cause an ObjectDB leaked instances warning, causing CI to fail (see GH-76745).
44+
xvfb-run ${{ inputs.bin }} --path misc/test_project/ --language fr --audio-driver Dummy --resolution 64x64 --write-movie /tmp/test_project_folder.png --quit-after 2 2>&1 | tee log.txt || true
45+
GODOT_CHECK_CI_LOG_ALL_ERRORS=1 misc/scripts/check_ci_log.py log.txt
46+
47+
${{ inputs.bin }} --headless --path misc/test_project/ --quit-after 2 2>&1 | tee log.txt || true
48+
GODOT_CHECK_CI_LOG_ALL_ERRORS=1 misc/scripts/check_ci_log.py log.txt
49+
50+
- name: Run exported project PCK/ZIP
51+
shell: sh
52+
run: |
53+
xvfb-run ${{ inputs.bin }} --main-pack /tmp/test_project.pck --language fr --audio-driver Dummy --resolution 64x64 --write-movie /tmp/test_project_pck.png --quit-after 2 2>&1 | tee log.txt || true
54+
GODOT_CHECK_CI_LOG_ALL_ERRORS=1 misc/scripts/check_ci_log.py log.txt
55+
56+
xvfb-run ${{ inputs.bin }} --main-pack /tmp/test_project.zip --language fr --audio-driver Dummy --resolution 64x64 --write-movie /tmp/test_project_zip.png --quit-after 2 2>&1 | tee log.txt || true
57+
GODOT_CHECK_CI_LOG_ALL_ERRORS=1 misc/scripts/check_ci_log.py log.txt
58+
59+
# Headless mode is implied for dedicated server PCKs.
60+
${{ inputs.bin }} --main-pack /tmp/test_project_server.pck --quit-after 2 2>&1 | tee log.txt || true
61+
GODOT_CHECK_CI_LOG_ALL_ERRORS=1 misc/scripts/check_ci_log.py log.txt
62+
63+
echo "Checking whether video output from project folder and exported project match..."
64+
md5sum /tmp/test_project*.png | md5sum --check
65+
66+
echo "Checking whether audio output from project folder and exported project match..."
67+
md5sum /tmp/test_project*.wav | md5sum --check

.github/workflows/linux_builds.yml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ jobs:
3434
build-mono: true
3535
doc-test: true
3636
proj-conv: true
37+
proj-export: true
3738
api-compat: true
3839
artifact: true
3940
# Validate godot-cpp compatibility on one arbitrary editor build.
@@ -117,7 +118,7 @@ jobs:
117118
run: |
118119
sudo apt-get update
119120
sudo apt-get install libwayland-bin # TODO: Figure out somehow how to embed this one.
120-
if [ "${{ matrix.proj-test }}" == "true" ]; then
121+
if [ "${{ matrix.proj-test }}" == "true" -o "${{ matrix.proj-export }}" == "true" ]; then
121122
sudo apt-get install mesa-vulkan-drivers
122123
fi
123124
@@ -249,6 +250,13 @@ jobs:
249250
with:
250251
bin: ${{ matrix.bin }}
251252

253+
# Test project export
254+
- name: Test project export
255+
uses: ./.github/actions/godot-project-export
256+
if: matrix.proj-export
257+
with:
258+
bin: ${{ matrix.bin }}
259+
252260
# Test the project converter
253261
- name: Test project converter
254262
uses: ./.github/actions/godot-converter-test

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ xcuserdata/
232232

233233
# Actual VS project files we don't use
234234
*.sln
235+
!misc/test_project/TestProject.sln
235236
*.vcxproj*
236237

237238
# User-specific files

misc/scripts/check_ci_log.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/usr/bin/env python
22
# -*- coding: utf-8 -*-
33

4+
import os
45
import sys
56

67
if len(sys.argv) < 2:
@@ -58,6 +59,12 @@
5859
print("ERROR: Assertion failed in project, check execution log for more info")
5960
sys.exit(55)
6061

62+
if os.environ.get("GODOT_CHECK_CI_LOG_ALL_ERRORS"):
63+
# If any occurrence of "ERROR:" is found in the log, we consider it a failure.
64+
if file_contents.find("ERROR:") != -1:
65+
print("ERROR: 'ERROR:' found in log and GODOT_CHECK_CI_LOG_ALL_ERRORS is set.")
66+
sys.exit(56)
67+
6168
# For now Godot leaks a lot of rendering stuff so for now we just show info
6269
# about it and this needs to be re-enabled after fixing this memory leaks.
6370

misc/test_project/.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
*.dblite
2+
3+
# Ignore the submodule so we don't add it to version control.
4+
# We clone it on CI when needed instead.
5+
addons/test_extension/src/godot-cpp

misc/test_project/LICENSE.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# License for third-party files
2+
3+
## `polyhaven/*`
4+
5+
Copyright (c) Poly Haven
6+
7+
- Upstream: https://polyhaven.com
8+
- License: CC0-1.0
9+
10+
## `fonts/librequake_conchars.webp`
11+
12+
- Upstream: https://github.com/lavenderdotpet/LibreQuake
13+
- License: BSD-3-Clause

misc/test_project/SConstruct

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/usr/bin/env python
2+
3+
# ruff: noqa: F821
4+
5+
# This file is for building as a Godot GDExtension.
6+
SConscript("addons/test_extension/SConstruct")
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# GDExtension generated files
2+
*.gen.*
3+
.sconsign*.dblite
4+
bin/
5+
compile_commands.json
6+
src/gen/
7+
8+
# Compiled files
9+
*.bc
10+
*.creator
11+
*.creator.user
12+
*.dblite
13+
*.exp
14+
*.files
15+
*.idb
16+
*.includes
17+
*.lib
18+
*.o
19+
*.os
20+
*.pdb
21+
*.pyc
22+
*.so
23+
*.obj
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#!/usr/bin/env python
2+
3+
# ruff: noqa: F821
4+
5+
# This file is for building as a Godot GDExtension.
6+
7+
env = SConscript("src/godot-cpp/SConstruct")
8+
9+
# Add source files.
10+
env.Append(CPPPATH=["src/"])
11+
sources = Glob("src/*.cpp")
12+
13+
env.Append(CPPDEFINES=["GDEXTENSION"])
14+
15+
bin_path = "bin/"
16+
extension_name = "test_extension"
17+
debug_or_release = "release" if env["target"] == "template_release" else "debug"
18+
19+
if "arch_suffix" not in env:
20+
env["arch_suffix"] = env["arch"]
21+
22+
if env["target"] in ["editor", "template_debug"]:
23+
# GDExtension has editor classes available in debug templates, which allows editor code to compile.
24+
# If you don't want to compile editor stuff in templates, move these 2 lines to a separate "if" block.
25+
env.Append(CPPPATH=["src/editor/"])
26+
sources += Glob("src/editor/*.cpp")
27+
# Add documentation XML files as a generated cpp file. Only works in Godot 4.3+.
28+
doc_data = env.GodotCPPDocData("src/gen/doc_data.gen.cpp", source=Glob("doc_classes/*.xml"))
29+
sources.append(doc_data)
30+
31+
# Create the library target (e.g. libtest_extension.linux.debug.x86_64.so).
32+
if env["platform"] == "macos":
33+
library = env.SharedLibrary(
34+
"{0}/lib{1}.{2}.{3}.framework/{1}.{2}.{3}".format(
35+
bin_path,
36+
extension_name,
37+
env["platform"],
38+
debug_or_release,
39+
),
40+
source=sources,
41+
)
42+
else:
43+
library = env.SharedLibrary(
44+
"{}/{}{}.{}.{}.{}{}".format(
45+
bin_path,
46+
env.subst("$SHLIBPREFIX"),
47+
extension_name,
48+
env["platform"],
49+
debug_or_release,
50+
env["arch_suffix"],
51+
env["SHLIBSUFFIX"],
52+
),
53+
source=sources,
54+
)
55+
56+
Default(library)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<class name="ExampleNode" inherits="Node" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
3+
<brief_description>
4+
GDExtension example node.
5+
</brief_description>
6+
<description>
7+
An example node that demonstrates how to create a node in [GDExtension], which can be compiled and loaded by [GDExtensionManager]. This example node can also be compiled as an engine module, if that option was selected in the editor when creating the GDExtension.
8+
</description>
9+
<tutorials>
10+
<link title="GDExtension overview">$DOCS_URL/tutorials/scripting/gdextension/what_is_gdextension.html</link>
11+
<link title="GDExtension example in C++">$DOCS_URL/tutorials/scripting/gdextension/gdextension_cpp_example.html</link>
12+
</tutorials>
13+
<methods>
14+
<method name="print_hello" qualifiers="const">
15+
<return type="void" />
16+
<description>
17+
Prints either [code]"Hello, World! From GDExtension."[/code] if compiled as GDExtension or [code]"Hello, World! From a module."[/code] if compiled as a module, determined by the [code]#if[/code] directives in the C++ code.
18+
</description>
19+
</method>
20+
<method name="return_hello" qualifiers="const">
21+
<return type="String" />
22+
<description>
23+
Returns either [code]"Hello, World! From GDExtension."[/code] if compiled as GDExtension or [code]"Hello, World! From a module."[/code] if compiled as a module, determined by the [code]#if[/code] directives in the C++ code. If neither a module or extension, the code does not compile.
24+
</description>
25+
</method>
26+
</methods>
27+
</class>

0 commit comments

Comments
 (0)