DEV Community

Jan David
Jan David

Posted on • Originally published at on


Automate Building and Publishing a Game on

Automate Building and Publishing a Game on

I really like automation in software development (and elsewhere). And since my personal projects are hosted on GitHub, I have fully adopted GitHub Actions as my continuous integration platform. When I push code, a handful of actions is started that lint the code, check its style, and run automated tests. When they all pass, it gives me confidence in the quality of my work.

For my upcoming game Auto Traffic Control, I wanted to use GitHub Actions again to create reproducible builds of the game with a predictable and consistent quality. The goal was to avoid human error by fully automating the process, from build to release on

Create a New Action

I recommend creating a new action for this workflow. Especially if you want to build your game for multiple operating systems, the file will get pretty long as is. Go ahead and create a new YAML file in .github/workflows/itch.yml, and add the following content:


  itch_project: jdno/auto-traffic-control
Enter fullscreen mode Exit fullscreen mode

Change the itch_project string to match your username on and the name of your project. If you like, you can also change the name of the action.

Trigger a Release

The setup that I have developed over the years for my GitHub Actions has one important feature: It has different actions for different workflows.

When I push code to GitHub, actions are started that check the quality of the code. These checks have to pass before I can merge the code into the main branch. Their purpose is to ensure a consistent code quality, not to create a release.

Automate Building and Publishing a Game on

When I want to create a new release, I actually use the release feature on GitHub. I like writing a short summary of what's changed, and include a changelog in the release notes. It makes the release feel more "official", and like an actual achievement. When I create a release, another action is kicked off that builds the game in release mode, and then pushes the game to

The following snippet is what I use to trigger my release workflow. It runs the action whenever a pre-release or release is published on GitHub:

      - prereleased
      - released
Enter fullscreen mode Exit fullscreen mode

You can also build and publish the game every time code is merged into the default branch with a snippet like below. The choice is ultimately yours, and depends on what makes sense for your development workflow.

Enter fullscreen mode Exit fullscreen mode

Build the Game

The build step depends on your game, and I cannot give you advice on it. The steps will look different based on the programming language or game engine that you use. On a high level, though, you want to do the following:

  1. Check out the code
  2. Set up a build environment
  3. Compile the game

The following examples assume that you also copy the binary and any resources/assets that the game needs into a dist folder. This is the folder that will be pushed to What needs to go into this folder again depends on the game engine that you are using.

I create builds of my game for Linux, macOS, and Windows. Each operating system requires slightly different dependencies, so I decided to split the build into three different jobs: one for each operating system. Below is the Linux build as an example. The job installs the dependencies, checks out the code, sets up the Rust toolchain, and then builds the game.

    name: Publish Linux build
    runs-on: ubuntu-latest

      - name: Install system dependencies
        run: |
          sudo apt-get update && sudo apt-get install -y \
          libx11-dev \
          libasound2-dev \

      - name: Checkout code
        uses: actions/checkout@v3

      - name: Set up Rust toolchain
        uses: actions-rs/toolchain@v1

      - name: Build release artifact
        uses: actions-rs/cargo@v1
          command: build
          args: --release
Enter fullscreen mode Exit fullscreen mode

After these steps, the release artifact will be copied into the dist folder along with the assets that my game use. Theoretically, I can now run my game from this folder and it would work.

Publish to

The final step is publishing the game to We will be using itch's butler, a tool that makes it easy to upload games to itch. This is the command to publish a new version of a game:

butler push directory user/game:channel --userversion version
Enter fullscreen mode Exit fullscreen mode

The command takes a directory as its argument, and uploads the contents of this directory to This will be our dist directory. The next argument is the project, which we've set at the beginning in the itch_project variable, followed by the channel.

Channels are an important concept on, and the channel name can be used to automatically tag the game on the platform. For example, pushing to the linux channel will tag the game as a Linux executable.

Lastly, we pass the optional userversion argument. The goal is to use the same version number on GitHub and on

I am getting these variables from the release that I created on GitHub. When a release is published and the action is started, GitHub will add the release to the action's context. This makes it possible, for example, to access the title of the release with (which is 0.1.0 for this release). If you want to run the action on every commit (i.e. on: [push] above), you need to either hard-code them or find another way to provide them dynamically.

      - name: Set version
        run: echo "version=${{ }}" >> $GITHUB_ENV

      - name: Set beta channel
        if: github.event.action == 'prereleased'
        run: echo "itch_channel=linux-beta" >> $GITHUB_ENV

      - name: Set release channel
        if: github.event.action == 'released'
        run: echo "itch_channel=linux-stable" >> $GITHUB_ENV
Enter fullscreen mode Exit fullscreen mode

I am particularly proud of finding a way to support pre-releases. When a release is tagged as a pre-release on GitHub, the game will be pushed to the beta channel. This makes it easy for me to test a new version of the game, before making it generally available.

Now that we have set the variables that are required to run butler, we can first add the tool to our workflow and then run it. Pushing a build to requires an API key, which you can find here.

      - name: Set up butler
        uses: jdno/setup-butler@v1

      - name: Publish game to
        run: |
          butler push dist ${{ env.itch_project }}:${{ env.itch_channel }} --userversion ${{ env.version }}
          BUTLER_API_KEY: ${{ secrets.ITCHIO_API_KEY }}
Enter fullscreen mode Exit fullscreen mode

A new version of your game is now available on


This is admittedly an advanced action with some pretty complex steps. Getting it to work correctly for your project might require a few iterations. It took me many builds to get this right. But the results are great, and totally worth the effort!

Most importantly, you now have a reproducible build. Getting your game ready for release is not a dark art anymore, nor does it require finding the note with the instructions somewhere on your desk. You can just look at the action to see which steps are required to build and then push the game.

Because the build is automated, it is also guaranteed to produce consistent results. Your local development environment cannot break the build or the game in mysterious ways, because the build environment is always clean.

And you know that everything that is required to build the game is backed up on GitHub. All code and all assets are safely stored in a second place, with a version history.

Follow me

If you're interesting in my game Auto Traffic Control or programming games in general, make sure to follow along. I am not sure where the road will take us, but I am very excited for the journey!

Subscribe to my blog to receive weekly updates about the progress on the project.

I also stream some if not all of the development of this game on Twitch, so follow me there as well.

jdno_dev - Twitch

Latest comments (0)

Advice For Junior Developers

Advice from a career of 15+ years for new and beginner developers just getting started on their journey.