Making open source contributions friendly

A Little Game Called Mario is an open source project that accepts contributions from anyone, regardless of their git experience. The project’s core promise is that every merged pull request triggers an automatic build and release of the game on itch.io, turning even the smallest change into a published contribution. To make that workable at scale with newcomers, the maintainers leaned on GitHub Actions to automate as much of the contribution pipeline as possible.

Auto-building and deploying to itch.io

The first piece of infrastructure was a workflow that kept the itch.io version of the game in sync with the GitHub repository. The original setup used two actions: one for building the Godot project and one for uploading the resulting build to itch.io via the butler CLI. The workflow triggered whenever new code landed on the main branch, checking out the repo, building the game, and publishing it. Configuration was straightforward—the build action required only the Godot version, and the upload step needed a few config values and an itch.io API key.

Crediting contributors via the GitHub API

Crediting every contributor visibly was a priority from the start. The README shows a live contributor count via a shields.io badge, and the in-game credits screen is generated as part of the build process. Early attempts were fragmented: one contributor edited the build workflow to write contributor data from git history into a text file, but that wasn’t wired to the game engine. Another contributor built a credits screen that queried the GitHub API at runtime. Eventually, these were consolidated into one system run by GitHub Actions before each build, which writes a contributor list from the GitHub contributors API to a file the game loads when credits are shown.

Animation showing the auto-generated credits for A Little Game Called Mario

The choice to use the GitHub API rather than git history was a deliberate one for inclusivity. Git commit names can become a permanent record of a contributor’s old name—an issue for anyone who has transitioned or changed their name. The GitHub API reflects a user’s current username, so renaming is handled centrally without rewriting git history. This decision also shaped the contribution flow for non-coders. Rather than maintaining separate credit lists for art or music assets, the team encouraged all contributors to use the standard fork-and-pull-request flow, making the GitHub API the single source of credit truth.

Preview builds for every pull request

A standout feature is the preview build system. Every pull request triggers an automatic build with the proposed changes, which is uploaded to a Netlify deploy preview. A comment on the pull request includes a public link to the playable build, and that link is also shared to Twitter and the project’s Discord channel. This gives reviewers a quick way to test a change and lets the wider community see new contributions in action before merging.

Screenshot showing GitHub Actions generating a preview deployment on Netlify.

Getting this to work securely was a challenge. The obvious approach—building directly from a pull request with the pull_request trigger—looks like a vulnerability if the build step could access repository secrets. Since anyone can open a PR against a public repo, a malicious actor could write a script that reads the GITHUB_TOKEN environment variable and push code to the repository without authorization. Switching to pull_request_target grants full environment access by default, but that felt too risky for a build process that runs arbitrary contributor code.

The solution mirrors the approach recommended by GitHub’s security lab. One workflow triggered by pull_request performs the “unsafe” part—building the game with the PR’s changes—and uploads the result as an artifact. This workflow has no access to repository secrets. A second workflow starts when the first completes; this one has full secret access but only handles trusted operations: it downloads the artifact, uploads it to Netlify, and posts the link back to the pull request as a comment. The two-step process required passing metadata like the PR number and title through files in the artifact, which adds some clunkiness, but it’s a reliable way to run untrusted code in an action without opening up the repository to attack.

The preview workflow even caught the attention of a core Godot maintainer, who praised the ability to test PRs immediately and expressed hope that more open source games would adopt the same pattern.

Enforcing Lints Without the Friction

Style enforcement is a staple of open source projects, but the standard approaches didn't fit A Little Game Called Mario. Requiring contributors to set up pre-commit hooks, which would have meant installing a Python environment solely for the linter, was too much to ask of newcomers. A passive GitHub Actions check that simply flagged style violations would also have placed an unfair burden on less experienced developers, forcing them to manually fix formatting issues identified in code review.

The project faced a particular challenge with GDScript's whitespace sensitivity. The editor lets users toggle between tabs and spaces, but those settings can't be committed to the repository, so pull requests frequently introduced whole-file whitespace changes. Explaining the editor defaults and whitespace rules to contributors who just wanted their changes merged became a major time sink.

The solution was an auto-formatter that fixed code automatically rather than requesting manual changes. The project uses gdformat from the Godot GDScript Toolkit. The tool doesn't expose configuration options, meaning the style guide is whatever its authors decided, but consistency is valuable enough that specific choices matter less. An earlier attempt to run the formatter as a step in the pull request workflow failed because a bot account couldn't push commits to public forks. Instead, the formatter runs on every merge to the main branch. Skipping a test run on auto-formatted preview builds is a compromise, but one that has never introduced breaking errors.

Community-Written CI Helpers

That initial formatting workflow became a template for community contributors, who started building their own checks to improve compile-time safety. One such workflow validates the dialogue data stored in the project's JSON-based narrative system. A community member defined a JSON Schema for the format and wired up a validator action that fails the build and comments on the pull request if a file doesn't pass, catching issues early before a human reviewer has to dig through the file.

Another community-built check verifies that collision layers are configured correctly for every object in a Godot scene. The logic handles a real housekeeping task unique to game engines and is itself written entirely in GDScript.

Growing Shared Ownership

Having the community build its own workflows is even more satisfying than designing one for them to use, since it demonstrates collective ownership of the development process. GitHub Actions keeps a low barrier to experimentation because definitions are just YAML files in the repository and can reference composable actions from third parties. That accessibility has encouraged contributors to not just use the workflows but improve them. Recent contributions are visible alongside the other build steps in the A Little Game Called Mario repository.