Răsfoiți Sursa

CI: Partially sync workflows and actions with 3.x branch

Stick to `ubuntu:20.04` runners for now, as porting to newer ones implies
fixing a number of compilation warnings which may not be worth the trouble
for an EOL branch.
Rémi Verschelde 8 luni în urmă
părinte
comite
aa89ab0517

+ 38 - 0
.github/actions/godot-build/action.yml

@@ -0,0 +1,38 @@
+name: Build Godot
+description: Build Godot with the provided options.
+inputs:
+  target:
+    description: The scons target (debug/release_debug/release).
+    default: "debug"
+  tools:
+    description: If tools are to be built.
+    default: false
+  tests:
+    description: If tests are to be built.
+    default: false
+  platform:
+    description: The Godot platform to build.
+    required: false
+  sconsflags:
+    default: ""
+  scons-cache:
+    description: The scons cache path.
+    default: "${{ github.workspace }}/.scons-cache/"
+  scons-cache-limit:
+    description: The scons cache size limit.
+    # actions/cache has 10 GiB limit, and GitHub runners have a 14 GiB disk.
+    # Limit to 7 GiB to avoid having the extracted cache fill the disk.
+    default: 7168
+runs:
+  using: "composite"
+  steps:
+    - name: Scons Build
+      shell: sh
+      env:
+          SCONSFLAGS: ${{ inputs.sconsflags }}
+          SCONS_CACHE: ${{ inputs.scons-cache }}
+          SCONS_CACHE_LIMIT: ${{ inputs.scons-cache-limit }}
+      run: |
+        echo "Building with flags:" ${{ env.SCONSFLAGS }}
+        scons p=${{ inputs.platform }} target=${{ inputs.target }} tools=${{ inputs.tools }} tests=${{ inputs.tests }} --jobs=2 ${{ env.SCONSFLAGS }}
+        ls -l bin/

+ 21 - 0
.github/actions/godot-cache-restore/action.yml

@@ -0,0 +1,21 @@
+name: Restore Godot build cache
+description: Restore Godot build cache.
+inputs:
+  cache-name:
+    description: The cache base name (job name by default).
+    default: "${{github.job}}"
+  scons-cache:
+    description: The scons cache path.
+    default: "${{github.workspace}}/.scons-cache/"
+runs:
+  using: "composite"
+  steps:
+    - name: Restore .scons_cache directory
+      uses: actions/cache/restore@v4
+      with:
+        path: ${{inputs.scons-cache}}
+        key: ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
+        restore-keys: |
+          ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
+          ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}
+          ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}

+ 17 - 0
.github/actions/godot-cache-save/action.yml

@@ -0,0 +1,17 @@
+name: Save Godot build cache
+description: Save Godot build cache.
+inputs:
+  cache-name:
+    description: The cache base name (job name by default).
+    default: "${{github.job}}"
+  scons-cache:
+    description: The scons cache path.
+    default: "${{github.workspace}}/.scons-cache/"
+runs:
+  using: "composite"
+  steps:
+    - name: Save .scons_cache directory
+      uses: actions/cache/save@v4
+      with:
+        path: ${{inputs.scons-cache}}
+        key: ${{inputs.cache-name}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}

+ 27 - 0
.github/actions/godot-deps/action.yml

@@ -0,0 +1,27 @@
+name: Setup python and scons
+description: Setup python, install the pip version of scons.
+inputs:
+  python-version:
+    description: The python version to use.
+    default: "3.x"
+  python-arch:
+    description: The python architecture.
+    default: "x64"
+runs:
+  using: "composite"
+  steps:
+    # Use python 3.x release (works cross platform)
+    - name: Set up Python 3.x
+      uses: actions/setup-python@v5
+      with:
+        # Semantic version range syntax or exact version of a Python version
+        python-version: ${{ inputs.python-version }}
+        # Optional - x64 or x86 architecture, defaults to x64
+        architecture: ${{ inputs.python-arch }}
+
+    - name: Setup scons
+      shell: bash
+      run: |
+        python -c "import sys; print(sys.version)"
+        python -m pip install scons==4.7.0
+        scons --version

+ 19 - 0
.github/actions/upload-artifact/action.yml

@@ -0,0 +1,19 @@
+name: Upload Godot artifact
+description: Upload the Godot artifact.
+inputs:
+  name:
+    description: The artifact name.
+    default: "${{ github.job }}"
+  path:
+    description: The path to upload.
+    required: true
+    default: "bin/*"
+runs:
+  using: "composite"
+  steps:
+    - name: Upload Godot Artifact
+      uses: actions/upload-artifact@v4
+      with:
+        name: ${{ inputs.name }}
+        path: ${{ inputs.path }}
+        retention-days: 14

+ 33 - 45
.github/workflows/android_builds.yml

@@ -1,76 +1,64 @@
 name: 🤖 Android Builds
-on: [push, pull_request]
+on:
+  workflow_call:
 
 # Global Settings
 env:
+  # Only used for the cache key. Increment version to force clean build.
   GODOT_BASE_BRANCH: 3.3
-  SCONSFLAGS: platform=android verbose=yes warnings=all werror=yes debug_symbols=no --jobs=2
-  SCONS_CACHE_LIMIT: 4096
+  SCONSFLAGS: verbose=yes warnings=all werror=yes debug_symbols=no
+
+concurrency:
+  group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-android
+  cancel-in-progress: true
 
 jobs:
   android-template:
     runs-on: "ubuntu-20.04"
-
     name: Template (target=release, tools=no)
 
     steps:
       - uses: actions/checkout@v4
 
-      # Azure repositories are not reliable, we need to prevent azure giving us packages.
-      - name: Make apt sources.list use the default Ubuntu repositories
-        run: |
-          sudo rm -f /etc/apt/sources.list.d/*
-          sudo cp -f misc/ci/sources.list /etc/apt/sources.list
-          sudo apt-get update
-
       - name: Set up Java 11
         uses: actions/setup-java@v4
         with:
           distribution: temurin
           java-version: 11
 
-      # Upload cache on completion and check it out now
-      - name: Load .scons_cache directory
-        id: android-template-cache
-        uses: actions/cache@v4
-        with:
-          path: ${{github.workspace}}/.scons_cache/
-          key: ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
-          restore-keys: |
-            ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
-            ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}
-            ${{github.job}}-${{env.GODOT_BASE_BRANCH}}
+      - name: Restore Godot build cache
+        uses: ./.github/actions/godot-cache-restore
         continue-on-error: true
 
-      # Use python 3.x release (works cross platform)
-      - name: Set up Python 3.x
-        uses: actions/setup-python@v5
+      - name: Setup python and scons
+        uses: ./.github/actions/godot-deps
+
+      - name: Compilation (armv7)
+        uses: ./.github/actions/godot-build
         with:
-          # Semantic version range syntax or exact version of a Python version
-          python-version: '3.x'
-          # Optional - x64 or x86 architecture, defaults to x64
-          architecture: 'x64'
+          sconsflags: ${{ env.SCONSFLAGS }} android_arch=armv7
+          platform: android
+          target: release
+          tools: false
 
-      - name: Configuring Python packages
-        run: |
-          python -c "import sys; print(sys.version)"
-          python -m pip install scons
-          python --version
-          scons --version
+      - name: Compilation (arm64v8)
+        uses: ./.github/actions/godot-build
+        with:
+          sconsflags: ${{ env.SCONSFLAGS }} android_arch=arm64v8
+          platform: android
+          target: release
+          tools: false
 
-      - name: Compilation
-        env:
-          SCONS_CACHE: ${{github.workspace}}/.scons_cache/
+      - name: Save Godot build cache
+        uses: ./.github/actions/godot-cache-save
+        continue-on-error: true
+
+      - name: Generate Godot templates
         run: |
-          scons target=release tools=no android_arch=armv7
-          scons target=release tools=no android_arch=arm64v8
           cd platform/android/java
           ./gradlew generateGodotTemplates
           cd ../../..
           ls -l bin/
 
-      - uses: actions/upload-artifact@v4
-        with:
-          name: ${{ github.job }}
-          path: bin/*
-          retention-days: 14
+      - name: Upload artifact
+        uses: ./.github/actions/upload-artifact

+ 26 - 42
.github/workflows/ios_builds.yml

@@ -1,11 +1,16 @@
 name: 🍏 iOS Builds
-on: [push, pull_request]
+on:
+  workflow_call:
 
 # Global Settings
 env:
+  # Only used for the cache key. Increment version to force clean build.
   GODOT_BASE_BRANCH: 3.3
-  SCONSFLAGS: platform=iphone verbose=yes warnings=all werror=yes debug_symbols=no --jobs=2
-  SCONS_CACHE_LIMIT: 4096
+  SCONSFLAGS: verbose=yes warnings=all werror=yes debug_symbols=no
+
+concurrency:
+  group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-ios
+  cancel-in-progress: true
 
 jobs:
   ios-template:
@@ -15,45 +20,24 @@ jobs:
     steps:
       - uses: actions/checkout@v4
 
-      # Upload cache on completion and check it out now
-      - name: Load .scons_cache directory
-        id: ios-template-cache
-        uses: actions/cache@v4
-        with:
-          path: ${{github.workspace}}/.scons_cache/
-          key: ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
-          restore-keys: |
-            ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
-            ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}
-            ${{github.job}}-${{env.GODOT_BASE_BRANCH}}
+      - name: Restore Godot build cache
+        uses: ./.github/actions/godot-cache-restore
         continue-on-error: true
 
-      # Use python 3.x release (works cross platform)
-      - name: Set up Python 3.x
-        uses: actions/setup-python@v5
-        with:
-          # Semantic version range syntax or exact version of a Python version
-          python-version: '3.x'
-          # Optional - x64 or x86 architecture, defaults to x64
-          architecture: 'x64'
-
-      # You can test your matrix by printing the current Python version
-      - name: Configuring Python packages
-        run: |
-          python -c "import sys; print(sys.version)"
-          python -m pip install scons
-          python --version
-          scons --version
-
-      - name: Compilation
-        env:
-          SCONS_CACHE: ${{github.workspace}}/.scons_cache/
-        run: |
-          scons target=release tools=no
-          ls -l bin/
-
-      - uses: actions/upload-artifact@v4
+      - name: Setup python and scons
+        uses: ./.github/actions/godot-deps
+
+      - name: Compilation (arm64v8)
+        uses: ./.github/actions/godot-build
         with:
-          name: ${{ github.job }}
-          path: bin/*
-          retention-days: 14
+          sconsflags: ${{ env.SCONSFLAGS }}
+          platform: iphone
+          target: release
+          tools: false
+
+      - name: Save Godot build cache
+        uses: ./.github/actions/godot-cache-save
+        continue-on-error: true
+
+      - name: Upload artifact
+        uses: ./.github/actions/upload-artifact

+ 29 - 59
.github/workflows/javascript_builds.yml

@@ -1,13 +1,18 @@
 name: 🌐 JavaScript Builds
-on: [push, pull_request]
+on:
+  workflow_call:
 
 # Global Settings
 env:
+  # Only used for the cache key. Increment version to force clean build.
   GODOT_BASE_BRANCH: 3.3
-  SCONSFLAGS: platform=javascript verbose=yes warnings=all werror=yes debug_symbols=no --jobs=2
-  SCONS_CACHE_LIMIT: 4096
+  SCONSFLAGS: verbose=yes warnings=all werror=yes debug_symbols=no
   EM_VERSION: 2.0.15
-  EM_CACHE_FOLDER: 'emsdk-cache'
+  EM_CACHE_FOLDER: "emsdk-cache"
+
+concurrency:
+  group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-javascript
+  cancel-in-progress: true
 
 jobs:
   javascript-template:
@@ -17,70 +22,35 @@ jobs:
     steps:
       - uses: actions/checkout@v4
 
-      # Azure repositories are not reliable, we need to prevent azure giving us packages.
-      - name: Make apt sources.list use the default Ubuntu repositories
-        run: |
-          sudo rm -f /etc/apt/sources.list.d/*
-          sudo cp -f misc/ci/sources.list /etc/apt/sources.list
-          sudo apt-get update
-
-      # Upload cache on completion and check it out now
-      - name: Load .scons_cache directory
-        id: javascript-template-cache
-        uses: actions/cache@v4
-        with:
-          path: ${{github.workspace}}/.scons_cache/
-          key: ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
-          restore-keys: |
-            ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
-            ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}
-            ${{github.job}}-${{env.GODOT_BASE_BRANCH}}
-        continue-on-error: true
-
-      # Additional cache for Emscripten generated system libraries
-      - name: Load Emscripten cache
-        id: javascript-template-emscripten-cache
-        uses: actions/cache@v4
-        with:
-          path: ${{env.EM_CACHE_FOLDER}}
-          key: ${{env.EM_VERSION}}-${{github.job}}
-
-      # Use python 3.x release (works cross platform)
-      - name: Set up Python 3.x
-        uses: actions/setup-python@v5
-        with:
-          # Semantic version range syntax or exact version of a Python version
-          python-version: '3.x'
-          # Optional - x64 or x86 architecture, defaults to x64
-          architecture: 'x64'
-
-      # You can test your matrix by printing the current Python version
-      - name: Configuring Python packages
-        run: |
-          python -c "import sys; print(sys.version)"
-          python -m pip install scons
-          python --version
-          scons --version
-
       - name: Set up Emscripten latest
         uses: mymindstorm/setup-emsdk@v14
         with:
           version: ${{env.EM_VERSION}}
           actions-cache-folder: ${{env.EM_CACHE_FOLDER}}
+          cache-key: emsdk-${{ matrix.cache-name }}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
 
       - name: Verify Emscripten setup
         run: |
           emcc -v
 
-      - name: Compilation
-        env:
-          SCONS_CACHE: ${{github.workspace}}/.scons_cache/
-        run: |
-          scons target=release tools=no use_closure_compiler=yes
-          ls -l bin/
+      - name: Restore Godot build cache
+        uses: ./.github/actions/godot-cache-restore
+        continue-on-error: true
 
-      - uses: actions/upload-artifact@v4
+      - name: Setup python and scons
+        uses: ./.github/actions/godot-deps
+
+      - name: Compilation
+        uses: ./.github/actions/godot-build
         with:
-          name: ${{ github.job }}
-          path: bin/*
-          retention-days: 14
+          sconsflags: ${{ env.SCONSFLAGS }}
+          platform: javascript
+          target: release
+          tools: false
+
+      - name: Save Godot build cache
+        uses: ./.github/actions/godot-cache-save
+        continue-on-error: true
+
+      - name: Upload artifact
+        uses: ./.github/actions/upload-artifact

+ 102 - 173
.github/workflows/linux_builds.yml

@@ -1,204 +1,118 @@
 name: 🐧 Linux Builds
-on: [push, pull_request]
+on:
+  workflow_call:
 
 # Global Settings
 env:
+  # Only used for the cache key. Increment version to force clean build.
   GODOT_BASE_BRANCH: 3.3
-  SCONSFLAGS: platform=linuxbsd verbose=yes warnings=all werror=yes debug_symbols=no --jobs=2
-  SCONS_CACHE_LIMIT: 4096
+  SCONSFLAGS: verbose=yes warnings=all werror=yes
+
+concurrency:
+  group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-linux
+  cancel-in-progress: true
 
 jobs:
-  linux-editor-mono:
+  build-linux:
     runs-on: "ubuntu-20.04"
-    name: Editor w/ Mono (target=release_debug, tools=yes)
+    name: ${{ matrix.name }}
+    strategy:
+      fail-fast: false
+      matrix:
+        include:
+          - name: Editor w/ Mono (target=release_debug, tools=yes)
+            cache-name: linux-editor-mono
+            target: release_debug
+            tools: true
+            sconsflags: module_mono_enabled=yes mono_static=yes mono_glue=no
+            bin: "./bin/godot.x11.opt.tools.64.mono"
+            build-mono: true
+            artifact: true
+
+          - name: Editor and sanitizers (target=debug, tools=yes, use_asan=yes, use_ubsan=yes, linker=gold)
+            cache-name: linux-editor-sanitizers
+            target: debug
+            tools: true
+            sconsflags: use_asan=yes use_ubsan=yes linker=gold
+            test: true
+            bin: "./bin/godot.x11.tools.64s"
+            build-mono: false
+            # Skip 2GiB artifact speeding up action.
+            artifact: false
+
+          - name: Template w/ Mono (target=release, tools=no)
+            cache-name: linux-template-mono
+            target: release
+            tools: false
+            sconsflags: module_mono_enabled=yes mono_static=yes mono_glue=no debug_symbols=no
+            build-mono: false
+            artifact: true
 
     steps:
       - uses: actions/checkout@v4
 
-      # Azure repositories are not reliable, we need to prevent azure giving us packages.
-      - name: Make apt sources.list use the default Ubuntu repositories
+      - name: Linux dependencies
+        shell: bash
         run: |
-          sudo rm -f /etc/apt/sources.list.d/*
-          sudo cp -f misc/ci/sources.list /etc/apt/sources.list
+          # Azure repositories are flaky, remove them.
+          sudo rm -f /etc/apt/sources.list.d/{azure,microsoft}*
           sudo apt-get update
+          # The actual dependencies.
+          sudo apt-get install --no-install-recommends build-essential pkg-config libx11-dev \
+              libxcursor-dev libxinerama-dev libgl1-mesa-dev libglu-dev libasound2-dev \
+              libpulse-dev libdbus-1-dev libudev-dev libxi-dev libxrandr-dev yasm xvfb wget unzip
 
-      # Install all packages (except scons)
-      - name: Configure dependencies
+      - name: Free disk space on runner
         run: |
-          sudo apt-get install build-essential pkg-config libx11-dev libxcursor-dev xvfb \
-            libxinerama-dev libgl1-mesa-dev libglu-dev libasound2-dev libpulse-dev libudev-dev libxi-dev libxrandr-dev yasm
+          echo "Disk usage before:" && df -h
+          sudo rm -rf /usr/local/lib/android
+          echo "Disk usage after:" && df -h
 
-      # Upload cache on completion and check it out now
-      - name: Load .scons_cache directory
-        id: linux-editor-cache
-        uses: actions/cache@v4
+      - name: Restore Godot build cache
+        uses: ./.github/actions/godot-cache-restore
         with:
-          path: ${{github.workspace}}/.scons_cache/
-          key: ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
-          restore-keys: |
-            ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
-            ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}
-            ${{github.job}}-${{env.GODOT_BASE_BRANCH}}
+          cache-name: ${{ matrix.cache-name }}
         continue-on-error: true
 
-      # Use python 3.x release (works cross platform; best to keep self contained in it's own step)
-      - name: Set up Python 3.x
-        uses: actions/setup-python@v5
-        with:
-          # Semantic version range syntax or exact version of a Python version
-          python-version: '3.x'
-          # Optional - x64 or x86 architecture, defaults to x64
-          architecture: 'x64'
+      - name: Setup python and scons
+        uses: ./.github/actions/godot-deps
 
-      # Setup scons, print python version and scons version info, so if anything is broken it won't run the build.
-      - name: Configuring Python packages
-        run: |
-          python -c "import sys; print(sys.version)"
-          python -m pip install scons
-          python --version
-          scons --version
+      - name: Setup GCC problem matcher
+        uses: ammaraskar/gcc-problem-matcher@master
 
-      # We should always be explicit with our flags usage here since it's gonna be sure to always set those flags
       - name: Compilation
-        env:
-          SCONS_CACHE: ${{github.workspace}}/.scons_cache/
-        run: |
-          scons tools=yes target=release_debug module_mono_enabled=yes mono_glue=no
-          xvfb-run ./bin/godot.x11.opt.tools.64.mono --generate-mono-glue modules/mono/glue || true
-          scons tools=yes target=release_debug module_mono_enabled=yes mono_glue=yes
-          ls -l bin/
-
-      - uses: actions/upload-artifact@v4
+        uses: ./.github/actions/godot-build
         with:
-          name: ${{ github.job }}
-          path: bin/*
-          retention-days: 14
-
-  linux-template-mono:
-    runs-on: "ubuntu-20.04"
-    name: Template w/ Mono (target=release, tools=no)
-
-    steps:
-      - uses: actions/checkout@v4
-
-      # Azure repositories are not reliable, we need to prevent azure giving us packages.
-      - name: Make apt sources.list use the default Ubuntu repositories
-        run: |
-          sudo rm -f /etc/apt/sources.list.d/*
-          sudo cp -f misc/ci/sources.list /etc/apt/sources.list
-          sudo apt-get update
-
-      # Install all packages (except scons)
-      - name: Configure dependencies
-        run: |
-          sudo apt-get install build-essential pkg-config libx11-dev libxcursor-dev \
-            libxinerama-dev libgl1-mesa-dev libglu-dev libasound2-dev libpulse-dev libudev-dev libxi-dev libxrandr-dev yasm
+          sconsflags: ${{ env.SCONSFLAGS }} ${{ matrix.sconsflags }}
+          platform: linuxbsd
+          target: ${{ matrix.target }}
+          tools: ${{ matrix.tools }}
 
-      # Upload cache on completion and check it out now
-      - name: Load .scons_cache directory
-        id: linux-template-cache
-        uses: actions/cache@v4
+      - name: Save Godot build cache
+        uses: ./.github/actions/godot-cache-save
         with:
-          path: ${{github.workspace}}/.scons_cache/
-          key: ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
-          restore-keys: |
-            ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
-            ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}
-            ${{github.job}}-${{env.GODOT_BASE_BRANCH}}
+          cache-name: ${{ matrix.cache-name }}
         continue-on-error: true
 
-      # Use python 3.x release (works cross platform)
-      - name: Set up Python 3.x
-        uses: actions/setup-python@v5
-        with:
-          # Semantic version range syntax or exact version of a Python version
-          python-version: '3.x'
-          # Optional - x64 or x86 architecture, defaults to x64
-          architecture: 'x64'
-
-      # You can test your matrix by printing the current Python version
-      - name: Configuring Python packages
+      # Generate mono glue
+      - name: Generate Mono glue code
+        if: ${{ matrix.build-mono }}
         run: |
-          python -c "import sys; print(sys.version)"
-          python -m pip install scons
-          python --version
-          scons --version
+          DRI_PRIME=0 xvfb-run ${{ matrix.bin }} --generate-mono-glue modules/mono/glue || true
 
-      - name: Compilation
-        env:
-          SCONS_CACHE: ${{github.workspace}}/.scons_cache/
-        run: |
-          scons target=release tools=no module_mono_enabled=yes mono_glue=no
-          ls -l bin/
-
-      - uses: actions/upload-artifact@v4
+      # Rebuild with mono
+      - name: Compilation (mono_glue=yes)
+        uses: ./.github/actions/godot-build
+        if: ${{ matrix.build-mono }}
         with:
-          name: ${{ github.job }}
-          path: bin/*
-          retention-days: 14
-
-  linux-editor-sanitizers:
-    runs-on: "ubuntu-20.04"
-    name: Editor and exported project with sanitizers (target=debug/release, tools=yes/no, use_ubsan=yes, use_asan=yes)
+          sconsflags: ${{ env.SCONSFLAGS }} ${{ matrix.sconsflags }} mono_glue=yes
+          platform: linuxbsd
+          target: ${{ matrix.target }}
+          tools: ${{ matrix.tools }}
 
-    steps:
-      - uses: actions/checkout@v4
-
-      # Azure repositories are not reliable, we need to prevent azure giving us packages.
-      - name: Make apt sources.list use the default Ubuntu repositories
-        run: |
-          sudo rm -f /etc/apt/sources.list.d/*
-          sudo cp -f misc/ci/sources.list /etc/apt/sources.list
-          sudo apt-get update
-
-      # Install all packages (except scons)
-      - name: Configure dependencies
-        run: |
-          sudo apt-get install build-essential pkg-config libx11-dev libxcursor-dev \
-            libxinerama-dev libgl1-mesa-dev libglu-dev libasound2-dev libpulse-dev libudev-dev libxi-dev libxrandr-dev yasm \
-            xvfb wget2 unzip
-
-      # Upload cache on completion and check it out now
-      - name: Load .scons_cache directory
-        id: linux-sanitizer-cache
-        uses: actions/cache@v4
-        with:
-          path: ${{github.workspace}}/.scons_cache/
-          key: ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
-          restore-keys: |
-            ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
-            ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}
-            ${{github.job}}-${{env.GODOT_BASE_BRANCH}}
-        continue-on-error: true
-
-      # Use python 3.x release (works cross platform; best to keep self contained in it's own step)
-      - name: Set up Python 3.x
-        uses: actions/setup-python@v5
-        with:
-          # Semantic version range syntax or exact version of a Python version
-          python-version: '3.x'
-          # Optional - x64 or x86 architecture, defaults to x64
-          architecture: 'x64'
-
-      # Setup scons, print python version and scons version info, so if anything is broken it won't run the build.
-      - name: Configuring Python packages
-        run: |
-          python -c "import sys; print(sys.version)"
-          python -m pip install scons
-          python --version
-          scons --version
-
-      # We should always be explicit with our flags usage here since it's gonna be sure to always set those flags
-      - name: Compilation
-        env:
-          SCONS_CACHE: ${{github.workspace}}/.scons_cache/
-        run: |
-          scons tools=yes target=debug use_asan=yes use_ubsan=yes
-          ls -l bin/
-
-      # Download test project
-      # CI has no audio device, so use the Dummy audio driver to avoid spurious error messages.
-      - name: Download project
+      # Download and extract zip archive with project, folder is renamed to be able to easy change used project
+      - name: Download test project
+        if: ${{ matrix.test }}
         run: |
           wget https://github.com/godotengine/regression-test-project/archive/3.3.zip
           unzip 3.3.zip
@@ -206,20 +120,35 @@ jobs:
 
       # Editor is quite complicated piece of software, so it is easy to introduce bug here
       - name: Open and close editor
+        if: ${{ matrix.test }}
         run: |
-          DRI_PRIME=0 xvfb-run bin/godot.x11.tools.64s --audio-driver Dummy -e -q --path test_project 2>&1 | tee sanitizers_log.txt || true
+          DRI_PRIME=0 xvfb-run ${{ matrix.bin }} --audio-driver Dummy -e -q --path test_project 2>&1 | tee sanitizers_log.txt || true
           misc/scripts/check_ci_log.py sanitizers_log.txt
 
       # Run test project
       - name: Run project
+        if: ${{ matrix.test }}
         run: |
-          DRI_PRIME=0 xvfb-run bin/godot.x11.tools.64s 30 --video-driver GLES3 --audio-driver Dummy --path test_project 2>&1 | tee sanitizers_log.txt || true
+          DRI_PRIME=0 xvfb-run ${{ matrix.bin }} 30 --video-driver GLES3 --audio-driver Dummy --path test_project 2>&1 | tee sanitizers_log.txt || true
           misc/scripts/check_ci_log.py sanitizers_log.txt
 
       # Check class reference
       - name: Check for class reference updates
+        if: ${{ matrix.test }}
         run: |
           echo "Running --doctool to see if this changes the public API without updating the documentation."
           echo -e "If a diff is shown, it means that your code/doc changes are incomplete and you should update the class reference with --doctool.\n\n"
-          DRI_PRIME=0 xvfb-run bin/godot.x11.tools.64s --doctool . 2>&1 > /dev/null || true
-          git diff --color --exit-code
+          DRI_PRIME=0 xvfb-run ${{ matrix.bin }} --doctool . 2>&1 > /dev/null || true
+          git diff --color --exit-code && ! git ls-files --others --exclude-standard | sed -e 's/^/New doc file missing in PR: /' | grep 'xml$'
+
+      - name: Prepare artifact
+        if: ${{ matrix.artifact }}
+        run: |
+          strip bin/godot.*
+          chmod +x bin/godot.*
+
+      - name: Upload artifact
+        uses: ./.github/actions/upload-artifact
+        if: ${{ matrix.artifact }}
+        with:
+          name: ${{ matrix.cache-name }}

+ 43 - 86
.github/workflows/macos_builds.yml

@@ -1,111 +1,68 @@
 name: 🍎 macOS Builds
-on: [push, pull_request]
+on:
+  workflow_call:
 
 # Global Settings
 env:
+  # Only used for the cache key. Increment version to force clean build.
   GODOT_BASE_BRANCH: 3.3
-  SCONSFLAGS: platform=osx verbose=yes warnings=all werror=yes debug_symbols=no --jobs=2
-  SCONS_CACHE_LIMIT: 4096
+  SCONSFLAGS: verbose=yes warnings=all werror=yes debug_symbols=no
+
+concurrency:
+  group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-macos
+  cancel-in-progress: true
 
 jobs:
-  macos-editor:
+  build-macos:
     runs-on: "macos-latest"
-
-    name: Editor (target=release_debug, tools=yes)
+    name: ${{ matrix.name }}
+    strategy:
+      fail-fast: false
+      matrix:
+        include:
+          - name: Editor (target=release_debug, tools=yes)
+            cache-name: macos-editor
+            target: release_debug
+            tools: true
+            bin: "./bin/godot.osx.opt.tools.64"
+
+          - name: Template (target=release, tools=no)
+            cache-name: macos-template
+            target: release
+            tools: false
 
     steps:
       - uses: actions/checkout@v4
 
-      # Upload cache on completion and check it out now
-      - name: Load .scons_cache directory
-        id: macos-editor-cache
-        uses: actions/cache@v4
+      - name: Restore Godot build cache
+        uses: ./.github/actions/godot-cache-restore
         with:
-          path: ${{github.workspace}}/.scons_cache/
-          key: ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
-          restore-keys: |
-            ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
-            ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}
-            ${{github.job}}-${{env.GODOT_BASE_BRANCH}}
+          cache-name: ${{ matrix.cache-name }}
         continue-on-error: true
 
-      # Use python 3.x release (works cross platform; best to keep self contained in it's own step)
-      - name: Set up Python 3.x
-        uses: actions/setup-python@v5
-        with:
-          # Semantic version range syntax or exact version of a Python version
-          python-version: '3.x'
-          # Optional - x64 or x86 architecture, defaults to x64
-          architecture: 'x64'
-
-      # Setup scons, print python version and scons version info, so if anything is broken it won't run the build.
-      - name: Configuring Python packages
-        run: |
-          python -c "import sys; print(sys.version)"
-          python -m pip install scons
-          python --version
-          scons --version
+      - name: Setup python and scons
+        uses: ./.github/actions/godot-deps
 
-      # We should always be explicit with our flags usage here since it's gonna be sure to always set those flags
       - name: Compilation
-        env:
-          SCONS_CACHE: ${{github.workspace}}/.scons_cache/
-        run: |
-          scons tools=yes target=release_debug
-          ls -l bin/
-
-      - uses: actions/upload-artifact@v4
+        uses: ./.github/actions/godot-build
         with:
-          name: ${{ github.job }}
-          path: bin/*
-          retention-days: 14
-
-  macos-template:
-    runs-on: "macos-latest"
-    name: Template (target=release, tools=no)
+          sconsflags: ${{ env.SCONSFLAGS }}
+          platform: osx
+          target: ${{ matrix.target }}
+          tools: ${{ matrix.tools }}
 
-    steps:
-      - uses: actions/checkout@v4
-
-      # Upload cache on completion and check it out now
-      - name: Load .scons_cache directory
-        id: macos-template-cache
-        uses: actions/cache@v4
+      - name: Save Godot build cache
+        uses: ./.github/actions/godot-cache-save
         with:
-          path: ${{github.workspace}}/.scons_cache/
-          key: ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
-          restore-keys: |
-            ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
-            ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}
-            ${{github.job}}-${{env.GODOT_BASE_BRANCH}}
+          cache-name: ${{ matrix.cache-name }}
         continue-on-error: true
 
-      # Use python 3.x release (works cross platform)
-      - name: Set up Python 3.x
-        uses: actions/setup-python@v5
-        with:
-          # Semantic version range syntax or exact version of a Python version
-          python-version: '3.x'
-          # Optional - x64 or x86 architecture, defaults to x64
-          architecture: 'x64'
-
-      # You can test your matrix by printing the current Python version
-      - name: Configuring Python packages
-        run: |
-          python -c "import sys; print(sys.version)"
-          python -m pip install scons
-          python --version
-          scons --version
-
-      - name: Compilation
-        env:
-          SCONS_CACHE: ${{github.workspace}}/.scons_cache/
+      - name: Prepare artifact
         run: |
-          scons target=release tools=no
-          ls -l bin/
+          strip bin/godot.*
+          chmod +x bin/godot.*
 
-      - uses: actions/upload-artifact@v4
+      - name: Upload artifact
+        uses: ./.github/actions/upload-artifact
         with:
-          name: ${{ github.job }}
-          path: bin/*
-          retention-days: 14
+          name: ${{ matrix.cache-name }}

+ 46 - 0
.github/workflows/runner.yml

@@ -0,0 +1,46 @@
+name: 🔗 GHA
+on: [push, pull_request]
+
+concurrency:
+  group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-runner
+  cancel-in-progress: true
+
+jobs:
+  static-checks:
+    name: 📊 Static
+    uses: ./.github/workflows/static_checks.yml
+
+  android-build:
+    name: 🤖 Android
+    needs: static-checks
+    uses: ./.github/workflows/android_builds.yml
+
+  ios-build:
+    name: 🍏 iOS
+    needs: static-checks
+    uses: ./.github/workflows/ios_builds.yml
+
+  javascript-build:
+    name: 🌐 JavaScript
+    needs: static-checks
+    uses: ./.github/workflows/javascript_builds.yml
+
+  linux-build:
+    name: 🐧 Linux
+    needs: static-checks
+    uses: ./.github/workflows/linux_builds.yml
+
+  macos-build:
+    name: 🍎 macOS
+    needs: static-checks
+    uses: ./.github/workflows/macos_builds.yml
+
+  server-build:
+    name: ☁ Server
+    needs: static-checks
+    uses: ./.github/workflows/server_builds.yml
+
+  windows-build:
+    name: 🏁 Windows
+    needs: static-checks
+    uses: ./.github/workflows/windows_builds.yml

+ 46 - 98
.github/workflows/server_builds.yml

@@ -1,120 +1,68 @@
 name: ☁ Server Builds
-on: [push, pull_request]
+on:
+  workflow_call:
 
-# Global Cache Settings
+# Global Settings
 env:
+  # Only used for the cache key. Increment version to force clean build.
   GODOT_BASE_BRANCH: 3.3
-  SCONSFLAGS: platform=server verbose=yes warnings=all werror=yes debug_symbols=no --jobs=2
-  SCONS_CACHE_LIMIT: 4096
+  SCONSFLAGS: verbose=yes warnings=all werror=yes debug_symbols=no module_mono_enabled=yes mono_static=yes mono_glue=no
+
+concurrency:
+  group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-server
+  cancel-in-progress: true
 
 jobs:
-  linux-editor:
+  build-server:
     runs-on: "ubuntu-20.04"
-    name: Linux Headless w/ Mono (target=release_debug, tools=yes)
+    name: ${{ matrix.name }}
+    strategy:
+      fail-fast: false
+      matrix:
+        include:
+          - name: Linux Headless w/ Mono (target=release_debug, tools=yes)
+            cache-name: server-editor-mono
+            target: release_debug
+            tools: true
+
+          - name: Linux Server w/ Mono (target=release, tools=no)
+            cache-name: server-template-mono
+            target: release
+            tools: false
 
     steps:
       - uses: actions/checkout@v4
 
-      # Azure repositories are not reliable, we need to prevent azure giving us packages.
-      - name: Make apt sources.list use the default Ubuntu repositories
+      - name: Linux dependencies
+        shell: bash
         run: |
-          sudo cp -f misc/ci/sources.list /etc/apt/sources.list
+          # Azure repositories are flaky, remove them.
+          sudo rm -f /etc/apt/sources.list.d/{azure,microsoft}*
           sudo apt-get update
+          # The actual dependencies.
+          sudo apt-get install --no-install-recommends build-essential pkg-config libx11-dev \
+              libxcursor-dev libxinerama-dev libgl1-mesa-dev libglu-dev libasound2-dev \
+              libpulse-dev libdbus-1-dev libudev-dev libxi-dev libxrandr-dev yasm xvfb wget unzip
 
-      # Install all packages (except scons)
-      - name: Configure dependencies
-        run: |
-          sudo apt-get install build-essential pkg-config libx11-dev libxcursor-dev \
-            libxinerama-dev libgl1-mesa-dev libglu-dev libasound2-dev libpulse-dev libudev-dev libxi-dev libxrandr-dev yasm
-
-      # Upload cache on completion and check it out now
-      - name: Load .scons_cache directory
-        id: linux-headless-cache
-        uses: actions/cache@v4
+      - name: Restore Godot build cache
+        uses: ./.github/actions/godot-cache-restore
         with:
-          path: ${{github.workspace}}/.scons_cache/
-          key: ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
-          restore-keys: |
-            ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
-            ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}
-            ${{github.job}}-${{env.GODOT_BASE_BRANCH}}
+          cache-name: ${{ matrix.cache-name }}
         continue-on-error: true
 
-      # Use python 3.x release (works cross platform; best to keep self contained in it's own step)
-      - name: Set up Python 3.x
-        uses: actions/setup-python@v5
-        with:
-          # Semantic version range syntax or exact version of a Python version
-          python-version: '3.x'
-          # Optional - x64 or x86 architecture, defaults to x64
-          architecture: 'x64'
+      - name: Setup python and scons
+        uses: ./.github/actions/godot-deps
 
-      # Setup scons, print python version and scons version info, so if anything is broken it won't run the build.
-      - name: Configuring Python packages
-        run: |
-          python -c "import sys; print(sys.version)"
-          python -m pip install scons
-          python --version
-          scons --version
-
-      # We should always be explicit with our flags usage here since it's gonna be sure to always set those flags
       - name: Compilation
-        env:
-          SCONS_CACHE: ${{github.workspace}}/.scons_cache/
-        run: |
-          scons -j2 verbose=yes warnings=all werror=yes platform=server tools=yes target=release_debug module_mono_enabled=yes mono_glue=no
-
-  linux-server:
-    runs-on: "ubuntu-20.04"
-    name: Linux Server w/ Mono (target=release, tools=no)
-
-    steps:
-      - uses: actions/checkout@v4
-
-      # Azure repositories are not reliable, we need to prevent azure giving us packages.
-      - name: Make apt sources.list use the default Ubuntu repositories
-        run: |
-          sudo cp -f misc/ci/sources.list /etc/apt/sources.list
-          sudo apt-get update
-
-      # Install all packages (except scons)
-      - name: Configure dependencies
-        run: |
-          sudo apt-get install build-essential pkg-config libx11-dev libxcursor-dev \
-            libxinerama-dev libgl1-mesa-dev libglu-dev libasound2-dev libpulse-dev libudev-dev libxi-dev libxrandr-dev yasm
-
-      # Upload cache on completion and check it out now
-      - name: Load .scons_cache directory
-        id: linux-server-cache
-        uses: actions/cache@v4
+        uses: ./.github/actions/godot-build
         with:
-          path: ${{github.workspace}}/.scons_cache/
-          key: ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
-          restore-keys: |
-            ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
-            ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}
-            ${{github.job}}-${{env.GODOT_BASE_BRANCH}}
-        continue-on-error: true
+          sconsflags: ${{ env.SCONSFLAGS }} ${{ matrix.sconsflags }}
+          platform: server
+          target: ${{ matrix.target }}
+          tools: ${{ matrix.tools }}
 
-      # Use python 3.x release (works cross platform)
-      - name: Set up Python 3.x
-        uses: actions/setup-python@v5
+      - name: Save Godot build cache
+        uses: ./.github/actions/godot-cache-save
         with:
-          # Semantic version range syntax or exact version of a Python version
-          python-version: '3.x'
-          # Optional - x64 or x86 architecture, defaults to x64
-          architecture: 'x64'
-
-      # You can test your matrix by printing the current Python version
-      - name: Configuring Python packages
-        run: |
-          python -c "import sys; print(sys.version)"
-          python -m pip install scons
-          python --version
-          scons --version
-
-      - name: Compilation
-        env:
-          SCONS_CACHE: ${{github.workspace}}/.scons_cache/
-        run: |
-          scons -j2 target=release tools=no module_mono_enabled=yes mono_glue=no
+          cache-name: ${{ matrix.cache-name }}
+        continue-on-error: true

+ 17 - 16
.github/workflows/static_checks.yml

@@ -1,36 +1,33 @@
 name: 📊 Static Checks
-on: [push, pull_request]
+on:
+  workflow_call:
+
+concurrency:
+  group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-static
+  cancel-in-progress: true
 
 jobs:
   static-checks:
     name: Static Checks (clang-format, black format, file format, documentation checks)
-    runs-on: ubuntu-20.04
+    runs-on: "ubuntu-20.04"
     steps:
       - name: Checkout
         uses: actions/checkout@v4
 
-      # Azure repositories are not reliable, we need to prevent Azure giving us packages.
-      - name: Make apt sources.list use the default Ubuntu repositories
-        run: |
-          sudo rm -f /etc/apt/sources.list.d/*
-          sudo cp -f misc/ci/sources.list /etc/apt/sources.list
-          sudo apt-get update
-
       - name: Install dependencies
         run: |
-          sudo apt-get install -qq dos2unix recode clang-format-11
-          sudo update-alternatives --remove-all clang-format
+          # Azure repositories are flaky, remove them.
+          sudo rm -f /etc/apt/sources.list.d/{azure,microsoft}*
+          sudo apt-get update
+          sudo apt-get install -qq dos2unix python3-pip moreutils
+          sudo update-alternatives --remove-all clang-format || true
           sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-11 100
-          sudo pip3 install black==20.8b1 pygments
+          sudo pip3 install black==24.8.0 pygments
 
       - name: File formatting checks (file_format.sh)
         run: |
           bash ./misc/scripts/file_format.sh
 
-      - name: Style checks via clang-format (clang_format.sh)
-        run: |
-          bash ./misc/scripts/clang_format.sh
-
       - name: Python style checks via black (black_format.sh)
         run: |
           bash ./misc/scripts/black_format.sh
@@ -45,3 +42,7 @@ jobs:
       - name: Documentation checks
         run: |
           doc/tools/makerst.py --dry-run doc/classes modules
+
+      - name: Style checks via clang-format (clang_format.sh)
+        run: |
+          bash ./misc/scripts/clang_format.sh

+ 60 - 104
.github/workflows/windows_builds.yml

@@ -1,117 +1,73 @@
 name: 🏁 Windows Builds
-on: [push, pull_request]
+on:
+  workflow_call:
 
 # Global Settings
 # SCONS_CACHE for windows must be set in the build environment
 env:
+  # Only used for the cache key. Increment version to force clean build.
   GODOT_BASE_BRANCH: 3.3
-  SCONSFLAGS: platform=windows verbose=yes warnings=all werror=yes debug_symbols=no --jobs=2
+  SCONSFLAGS: verbose=yes warnings=all werror=yes debug_symbols=no
   SCONS_CACHE_MSVC_CONFIG: true
-  SCONS_CACHE_LIMIT: 4096
+
+concurrency:
+  group: ci-${{github.actor}}-${{github.head_ref || github.run_number}}-${{github.ref}}-windows
+  cancel-in-progress: true
 
 jobs:
-  windows-editor:
+  build-windows:
     # Windows 10 with latest image
     runs-on: "windows-latest"
-
-    # Windows Editor - checkout with the plugin
-    name: Editor (target=release_debug, tools=yes)
-
-    steps:
-    - uses: actions/checkout@v4
-
-      # Upload cache on completion and check it out now
-      # Editing this is pretty dangerous for Windows since it can break and needs to be properly tested with a fresh cache.
-    - name: Load .scons_cache directory
-      id: windows-editor-cache
-      uses: actions/cache@v4
-      with:
-        path: /.scons_cache/
-        key: ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
-        restore-keys: |
-          ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
-          ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}
-          ${{github.job}}-${{env.GODOT_BASE_BRANCH}}
-      continue-on-error: true
-
-    # Use python 3.x release (works cross platform; best to keep self contained in it's own step)
-    - name: Set up Python 3.x
-      uses: actions/setup-python@v5
-      with:
-        # Semantic version range syntax or exact version of a Python version
-        python-version: '3.x'
-        # Optional - x64 or x86 architecture, defaults to x64
-        architecture: 'x64'
-
-    # Setup scons, print python version and scons version info, so if anything is broken it won't run the build.
-    - name: Configuring Python packages
-      run: |
-        python -c "import sys; print(sys.version)"
-        python -m pip install scons
-        python --version
-        scons --version
-
-    # We should always be explicit with our flags usage here since it's gonna be sure to always set those flags
-    - name: Compilation
-      env:
-        SCONS_CACHE: /.scons_cache/
-      run: |
-        scons tools=yes target=release_debug
-        ls -l bin/
-
-    - uses: actions/upload-artifact@v4
-      with:
-        name: ${{ github.job }}
-        path: bin/*
-        retention-days: 14
-
-  windows-template:
-    runs-on: "windows-latest"
-    name: Template (target=release, tools=no)
+    name: ${{ matrix.name }}
+    strategy:
+      fail-fast: false
+      matrix:
+        include:
+          - name: Editor (target=release_debug, tools=yes)
+            cache-name: windows-editor
+            target: release_debug
+            tools: true
+            bin: "./bin/godot.windows.opt.tools.64.exe"
+
+          - name: Template (target=release, tools=no)
+            cache-name: windows-template
+            target: release
+            tools: false
 
     steps:
-    - uses: actions/checkout@v4
-
-    # Upload cache on completion and check it out now
-    # Editing this is pretty dangerous for Windows since it can break and needs to be properly tested with a fresh cache.
-    - name: Load .scons_cache directory
-      id: windows-template-cache
-      uses: RevoluPowered/[email protected]
-      with:
-        path: /.scons_cache/
-        key: ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
-        restore-keys: |
-          ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}-${{github.sha}}
-          ${{github.job}}-${{env.GODOT_BASE_BRANCH}}-${{github.ref}}
-          ${{github.job}}-${{env.GODOT_BASE_BRANCH}}
-      continue-on-error: true
-
-    # Use python 3.x release (works cross platform)
-    - name: Set up Python 3.x
-      uses: actions/setup-python@v5
-      with:
-        # Semantic version range syntax or exact version of a Python version
-        python-version: '3.x'
-        # Optional - x64 or x86 architecture, defaults to x64
-        architecture: 'x64'
-
-    # You can test your matrix by printing the current Python version
-    - name: Configuring Python packages
-      run: |
-        python -c "import sys; print(sys.version)"
-        python -m pip install scons
-        python --version
-        scons --version
-
-    - name: Compilation
-      env:
-        SCONS_CACHE: /.scons_cache/
-      run: |
-        scons target=release tools=no
-        ls -l bin/
-
-    - uses: actions/upload-artifact@v4
-      with:
-        name: ${{ github.job }}
-        path: bin/*
-        retention-days: 14
+      - uses: actions/checkout@v4
+
+      - name: Restore Godot build cache
+        uses: ./.github/actions/godot-cache-restore
+        with:
+          cache-name: ${{ matrix.cache-name }}
+        continue-on-error: true
+
+      - name: Setup python and scons
+        uses: ./.github/actions/godot-deps
+
+      - name: Setup MSVC problem matcher
+        uses: ammaraskar/msvc-problem-matcher@master
+
+      - name: Compilation
+        uses: ./.github/actions/godot-build
+        with:
+          sconsflags: ${{ env.SCONSFLAGS }}
+          platform: windows
+          target: ${{ matrix.target }}
+          tools: ${{ matrix.tools }}
+
+      - name: Save Godot build cache
+        uses: ./.github/actions/godot-cache-save
+        with:
+          cache-name: ${{ matrix.cache-name }}
+        continue-on-error: true
+
+      - name: Prepare artifact
+        run: |
+          Remove-Item bin/* -Include *.exp,*.lib,*.pdb -Force
+
+      - name: Upload artifact
+        uses: ./.github/actions/upload-artifact
+        with:
+          name: ${{ matrix.cache-name }}

+ 0 - 1
compat.py

@@ -43,7 +43,6 @@ if sys.version_info < (3,):
         # Not properly equivalent to __qualname__ in py3, but it doesn't matter.
         return obj.__name__
 
-
 else:
 
     def isbasestring(s):

+ 1 - 0
core/core_builders.py

@@ -3,6 +3,7 @@
 All such functions are invoked in a subprocess on Windows to prevent build flakiness.
 
 """
+
 from platform_methods import subprocess_main
 from compat import iteritems, itervalues, open_utf8, escape_string, byte_to_str
 

+ 5 - 5
doc/tools/makerst.py

@@ -597,7 +597,7 @@ def escape_rst(text, until_pos=-1):  # type: (str) -> str
         pos = text.find("*", pos, until_pos)
         if pos == -1:
             break
-        text = text[:pos] + "\*" + text[pos + 1 :]
+        text = text[:pos] + "\\*" + text[pos + 1 :]
         pos += 2
 
     # Escape _ character at the end of a word to avoid interpreting it as an inline hyperlink
@@ -607,7 +607,7 @@ def escape_rst(text, until_pos=-1):  # type: (str) -> str
         if pos == -1:
             break
         if not text[pos + 1].isalnum():  # don't escape within a snake_case word
-            text = text[:pos] + "\_" + text[pos + 1 :]
+            text = text[:pos] + "\\_" + text[pos + 1 :]
             pos += 2
         else:
             pos += 1
@@ -865,7 +865,7 @@ def rstize_text(text, state):  # type: (str, State) -> str
 
         # Properly escape things like `[Node]s`
         if escape_post and post_text and (post_text[0].isalnum() or post_text[0] == "("):  # not punctuation, escape
-            post_text = "\ " + post_text
+            post_text = "\\ " + post_text
 
         next_brac_pos = post_text.find("[", 0)
         iter_pos = 0
@@ -873,7 +873,7 @@ def rstize_text(text, state):  # type: (str, State) -> str
             iter_pos = post_text.find("*", iter_pos, next_brac_pos)
             if iter_pos == -1:
                 break
-            post_text = post_text[:iter_pos] + "\*" + post_text[iter_pos + 1 :]
+            post_text = post_text[:iter_pos] + "\\*" + post_text[iter_pos + 1 :]
             iter_pos += 2
 
         iter_pos = 0
@@ -882,7 +882,7 @@ def rstize_text(text, state):  # type: (str, State) -> str
             if iter_pos == -1:
                 break
             if not post_text[iter_pos + 1].isalnum():  # don't escape within a snake_case word
-                post_text = post_text[:iter_pos] + "\_" + post_text[iter_pos + 1 :]
+                post_text = post_text[:iter_pos] + "\\_" + post_text[iter_pos + 1 :]
                 iter_pos += 2
             else:
                 iter_pos += 1

+ 1 - 0
editor/editor_builders.py

@@ -3,6 +3,7 @@
 All such functions are invoked in a subprocess on Windows to prevent build flakiness.
 
 """
+
 import os
 import os.path
 from platform_methods import subprocess_main

+ 1 - 0
editor/icons/editor_icons_builders.py

@@ -3,6 +3,7 @@
 All such functions are invoked in a subprocess on Windows to prevent build flakiness.
 
 """
+
 import os
 from platform_methods import subprocess_main
 from compat import StringIO

+ 2 - 1
gles_builders.py

@@ -3,6 +3,7 @@
 All such functions are invoked in a subprocess on Windows to prevent build flakiness.
 
 """
+
 from platform_methods import subprocess_main
 
 
@@ -385,7 +386,7 @@ def build_legacygl_header(filename, include, class_suffix, output_attribs, gles2
             x = header_data.enums[xv]
             bits = 1
             amt = len(x)
-            while 2 ** bits < amt:
+            while 2**bits < amt:
                 bits += 1
             strs = "{"
             for i in range(amt):

+ 1 - 0
main/main_builders.py

@@ -3,6 +3,7 @@
 All such functions are invoked in a subprocess on Windows to prevent build flakiness.
 
 """
+
 from platform_methods import subprocess_main
 from compat import byte_to_str
 from collections import OrderedDict

+ 0 - 4
misc/ci/sources.list

@@ -1,4 +0,0 @@
-deb http://archive.ubuntu.com/ubuntu/ focal main restricted universe multiverse
-deb http://archive.ubuntu.com/ubuntu/ focal-updates main restricted universe multiverse
-deb http://archive.ubuntu.com/ubuntu/ focal-security main restricted universe multiverse
-deb http://archive.ubuntu.com/ubuntu/ focal-backports main restricted universe multiverse

+ 2 - 0
misc/scripts/check_ci_log.py

@@ -25,6 +25,8 @@ if (
     file_contents.find("Program crashed with signal") != -1
     or file_contents.find("Dumping the backtrace") != -1
     or file_contents.find("Segmentation fault (core dumped)") != -1
+    or file_contents.find("Aborted (core dumped)") != -1
+    or file_contents.find("terminate called without an active exception") != -1
 ):
     print("FATAL ERROR: Godot has been crashed.")
     sys.exit(1)

+ 26 - 12
misc/scripts/file_format.sh

@@ -5,8 +5,8 @@
 # run before them.
 
 # We need dos2unix and recode.
-if [ ! -x "$(command -v dos2unix)" -o ! -x "$(command -v recode)" ]; then
-    printf "Install 'dos2unix' and 'recode' to use this script.\n"
+if [ ! -x "$(command -v dos2unix)" -o ! -x "$(command -v isutf8)" ]; then
+    printf "Install 'dos2unix' and 'isutf8' (from the moreutils package) to use this script.\n"
 fi
 
 set -uo pipefail
@@ -20,13 +20,17 @@ while IFS= read -rd '' f; do
         continue
     elif [[ "$f" == *"sln" ]]; then
         continue
+    elif [[ "$f" == *".bat" ]]; then
+        continue
     elif [[ "$f" == *"patch" ]]; then
         continue
     elif [[ "$f" == *"pot" ]]; then
         continue
     elif [[ "$f" == *"po" ]]; then
         continue
-    elif [[ "$f" == "thirdparty"* ]]; then
+    elif [[ "$f" == "thirdparty/"* ]]; then
+        continue
+    elif [[ "$f" == *"/thirdparty/"* ]]; then
         continue
     elif [[ "$f" == "platform/android/java/lib/src/com/google"* ]]; then
         continue
@@ -34,7 +38,7 @@ while IFS= read -rd '' f; do
         continue
     fi
     # Ensure that files are UTF-8 formatted.
-    recode UTF-8 "$f" 2> /dev/null
+    isutf8 "$f" >> utf8-validation.txt 2>&1
     # Ensure that files have LF line endings and do not contain a BOM.
     dos2unix "$f" 2> /dev/null
     # Remove trailing space characters and ensures that files end
@@ -46,17 +50,27 @@ done
 
 git diff --color > patch.patch
 
-# If no patch has been generated all is OK, clean up, and exit.
-if [ ! -s patch.patch ] ; then
+# If no UTF-8 violations were collected and no patch has been
+# generated all is OK, clean up, and exit.
+if [ ! -s utf8-validation.txt ] && [ ! -s patch.patch ] ; then
     printf "Files in this commit comply with the formatting rules.\n"
-    rm -f patch.patch
+    rm -f patch.patch utf8-validation.txt
     exit 0
 fi
 
-# A patch has been created, notify the user, clean up, and exit.
-printf "\n*** The following differences were found between the code "
-printf "and the formatting rules:\n\n"
-cat patch.patch
+# Violations detected, notify the user, clean up, and exit.
+if [ -s utf8-validation.txt ]
+then
+    printf "\n*** The following files contain invalid UTF-8 character sequences:\n\n"
+    cat utf8-validation.txt
+fi
+
+if [ -s patch.patch ]
+then
+    printf "\n*** The following differences were found between the code "
+    printf "and the formatting rules:\n\n"
+    cat patch.patch
+fi
+rm -f utf8-validation.txt patch.patch
 printf "\n*** Aborting, please fix your commit(s) with 'git commit --amend' or 'git rebase -i <hash>'\n"
-rm -f patch.patch
 exit 1

+ 1 - 0
modules/denoise/resource_to_cpp.py

@@ -19,6 +19,7 @@
 import os
 from array import array
 
+
 # Generates a C++ file from the specified binary resource file
 def generate(in_path, out_path):
 

+ 1 - 0
modules/gdnative/gdnative_builders.py

@@ -3,6 +3,7 @@
 All such functions are invoked in a subprocess on Windows to prevent build flakiness.
 
 """
+
 import json
 from platform_methods import subprocess_main
 

+ 2 - 2
modules/mono/doc_classes/CSharpScript.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<class name="CSharpScript" inherits="Script" category="Core" version="3.2">
+<class name="CSharpScript" inherits="Script" version="3.2">
 	<brief_description>
 		A script implemented in the C# programming language (Mono-enabled builds only).
 	</brief_description>
@@ -12,7 +12,7 @@
 	</tutorials>
 	<methods>
 		<method name="new" qualifiers="vararg">
-			<return type="Object">
+			<return type="Variant">
 			</return>
 			<description>
 				Returns a new instance of the script.

+ 1 - 1
modules/mono/doc_classes/GodotSharp.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<class name="GodotSharp" inherits="Object" category="Core" version="3.2">
+<class name="GodotSharp" inherits="Object" version="3.2">
 	<brief_description>
 		Bridge between Godot and the Mono runtime (Mono-enabled builds only).
 	</brief_description>

+ 1 - 0
platform/osx/platform_osx_builders.py

@@ -3,6 +3,7 @@
 All such functions are invoked in a subprocess on Windows to prevent build flakiness.
 
 """
+
 import os
 from platform_methods import subprocess_main
 

+ 1 - 1
platform/server/detect.py

@@ -78,7 +78,7 @@ def configure(env):
 
     ## Architecture
 
-    is64 = sys.maxsize > 2 ** 32
+    is64 = sys.maxsize > 2**32
     if env["bits"] == "default":
         env["bits"] = "64" if is64 else "32"
 

+ 1 - 0
platform/windows/platform_windows_builders.py

@@ -3,6 +3,7 @@
 All such functions are invoked in a subprocess on Windows to prevent build flakiness.
 
 """
+
 import os
 from platform_methods import subprocess_main
 

+ 1 - 1
platform/x11/detect.py

@@ -115,7 +115,7 @@ def configure(env):
 
     ## Architecture
 
-    is64 = sys.maxsize > 2 ** 32
+    is64 = sys.maxsize > 2**32
     if env["bits"] == "default":
         env["bits"] = "64" if is64 else "32"
 

+ 1 - 0
platform/x11/platform_x11_builders.py

@@ -3,6 +3,7 @@
 All such functions are invoked in a subprocess on Windows to prevent build flakiness.
 
 """
+
 import os
 from platform_methods import subprocess_main