~/home/blogs/case-study-modernizing-godot-ci-tool.md

Case Study: Modernizing a Community CI/CD Tool

Modernizing a Godot testing tool from a legacy bash script to a test-covered Node.js GitHub Action, improving maintainability and community contribution.


Raul G

October 16, 2025

Challenge: A Critical Tool Falling Behind

In any software ecosystem, automated testing is the bedrock of quality. For the Godot game engine, the community often uses the Godot Unit Test (GUT) library. To integrate this unit test library into modern development workflows, the godot-tester action was created, allowing developers to automatically run their unit tests as part of a CI/CD pipeline.

In late 2022, I encountered an issue when leveraging the godot-tester action on a pet project of mine. I noticed that the github issue covering the problem had been open for 5 months. To summarize, the GUT testing library had updated its test result format to JUnit XML, but the godot-tester action hadn't been updated to parse it. At this time, I learned that the github action was implemented as a single bash script. While I submitted a patch to the bash code to fix the immediate issue, I saw the deeper challenge: the project's complexity was outgrowing its bash foundation, making it difficult to maintain and adapt.

This became critical when a request for Godot 4 support was opened in November 2022. For months, the issue saw no activity. The technical hurdles were significant enough that modifying the monolithic script was a daunting task for potential contributors.

The Evolution: A Three-Stage Modernization

Seeing the community's need, I decided to take ownership of the modernization effort. Instead of a single "big bang" rewrite, I approached it as a pragmatic, three-stage evolution.

Stage 1: Improving the Bash Foundations (May 2023)

My first step wasn't to start adding new features, but to make the existing codebase manageable. I first tried to empower the community by detailing the required changes in the open issue, providing a clear path forward. After a month passed with no activity, I took ownership of the changes. I refactored the monolithic bash script, extracting logic into named functions and creating a lib/ directory. This made the code more readable and, crucially, lowered the barrier for any future contributions.

Stage 2: Delivering Godot 4 Support with Backward Compatibility (June 2023)

With the refactored script in place, I implemented support for Godot 4. This was done carefully, creating switch branches based on the user's specified Godot version (3.x or 4.x) which adjusted the script's behavior accordingly. This ensured that we could introduce a major new feature without breaking the workflow for the existing user base. This version was merged, successfully resolving a months-old community request.

Stage 3: The Strategic Rewrite for Long-Term Health (July 2023)

Feeling like I was on a hotstreak after merging the Godot 4 support, I decided to address the project's remaining technical debt. While the bash script now worked, it was still a technical dead-end. It was difficult to test, prone to platform-specific issues, and not an ideal foundation for future features. A week after the Godot 4 support was merged, I proposed and executed a complete rewrite of the action in Node.js.

The new architecture was designed for maintainability, reliability, and extensibility:

  • Node.js Core: A more robust, platform-agnostic language for handling logic like downloading files, parsing XML, and interacting with the GitHub Actions environment.
  • Docker Container: The action now runs inside a Docker container, ensuring a consistent and predictable execution environment for every user.
  • Unit Tests: The new Node.js codebase is covered by unit tests, allowing for confident refactoring and feature development without fear of regressions.
  • Modularity: The code was split into multiple files, each with a single, isolated purpose (e.g., parsing inputs, downloading Godot, running tests, evaluating results).
graph LR
    A((Start))--> B[Read Params/Args];
    B --> DownloadGodot;
    DownloadGodot --> RunGodotImport;
    RunGodotImport --> ExecuteGUTTests;
    ExecuteGUTTests --> AnalyzeTestResults;

    subgraph DownloadGodot[Download Godot]
        C1[Generate Download URL] --> C2[Perform Download];
        C2 --> C3[Decompress ZIP];
        C3 --> C4[Find Executable];
    end

    subgraph RunGodotImport[Run Godot Asset Import]
        D1[Add Rebuilder Scene] --> D2[Run Godot in Headless Editor Mode];
        D2 --> D3[Remove Rebuilder Scene];
    end

    subgraph ExecuteGUTTests[Execute GUT Tests]
        E1[Construct GUT CLI Args] --> E2[Run Godot with GUT Script];
    end

    subgraph AnalyzeTestResults[Analyze Test Results]
        F1[Read XML File] --> F2[Parse XML Data];
        F2 --> F3[Calculate Pass Rate];
        F3 --> F4[Compare with Minimum Pass/Max Fails];
    end

    AnalyzeTestResults --> CheckPassRate{Failure
threshold
met?} CheckPassRate -->|Too Many Failures| Fail; CheckPassRate -->|All passed| Success;

The Results: A Revitalized Community Tool

The migration from a legacy bash script to a modern Node.js application transformed the godot-tester action from a maintenance challenge into a stable and extensible platform.

  • Improved Reliability: The Docker container and unit tests ensure the action behaves consistently and correctly across different environments.
  • Enhanced Maintainability: The modular, well-tested codebase is significantly easier for new contributors to understand, modify, and extend.
  • Future-Ready: The new architecture makes it easier to add features and support future versions of Godot.

This modernization effort shows how an iterative approach can breathe new life into a valuable open-source project. By breaking down the problem—first improving the existing script, then adding features, and finally rewriting for long-term health—the tool was updated to meet the community's current and future needs. The project is now in a much better position for continued community contribution and evolution.

Share this post