Automate Building and Publishing a Game on itch.io
Tags: gamedev, githubaction, github
Published: 2022-04-10 16:21:33

---
title: Automate Building and Publishing a Game on itch.io
published: true
date: 2022-04-10 16:00:00 UTC
tags: gamedev,githubaction,github
canonical_url: https://www.jdno.dev/automate-building-and-publishing-a-game-on-itch-io/
---

![Automate Building and Publishing a Game on itch.io](https://www.jdno.dev/content/images/2022/04/github-to-itch.png)

I really like automation in software development (and elsewhere). And since my personal projects are hosted on [GitHub](https://github.com/), I have fully adopted [GitHub Actions](https://github.com/features/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](https://dev.to/jdno/auto-traffic-control-a-video-game-for-programmers-f9k), 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 [itch.io](https://itch.io).

## 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:

```yml
---
name: itch.io

env:
  itch_project: jdno/auto-traffic-control
```

Change the `itch_project` string to match your username on [itch.io](https://itch.io) 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 itch.io](https://www.jdno.dev/content/images/2022/04/Screenshot-2022-04-10-at-17.16.40.png)

When I want to create a new release, I actually use the [release feature](https://docs.github.com/en/repositories/releasing-projects-on-github/about-releases) 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 [itch.io](https://itch.io).

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:

```yml
"on":
  release:
    types:
      - prereleased
      - released
```

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.

```yml
"on":
  push:
```

## 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 [itch.io](https://itch.io). 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.

```yml
jobs:
  linux:
    name: Publish Linux build
    runs-on: ubuntu-latest

    steps:
      - name: Install system dependencies
        run: |
          sudo apt-get update && sudo apt-get install -y \
          libx11-dev \
          libasound2-dev \
          libudev-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
        with:
          command: build
          args: --release
```

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 itch.io

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

```shell
butler push directory user/game:channel --userversion version
```

The command takes a `directory` as its argument, and uploads the contents of this directory to [itch.io](https://itch.io). 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](https://itch.io/docs/butler/pushing.html#channel-names) are an important concept on [itch.io](https://itch.io), 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 itch.io.

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](https://docs.github.com/en/actions/learn-github-actions/contexts). This makes it possible, for example, to access the title of the release with `github.event.release.name` (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.

```yml
      - name: Set version
        run: echo "version=${{ github.event.release.name }}" >> $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
```

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 [itch.io](https://itch.io) requires an API key, which you can [find here](https://itch.io/docs/butler/login.html#running-butler-from-ci-builds-travis-ci-gitlab-ci-etc).

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

      - name: Publish game to itch.io
        run: |
          butler push dist ${{ env.itch_project }}:${{ env.itch_channel }} --userversion ${{ env.version }}
        env:
          BUTLER_API_KEY: ${{ secrets.ITCHIO_API_KEY }}
```

A new version of your game is now available on [itch.io](https://itch.io).

## Profit

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](https://dev.to/jdno/auto-traffic-control-a-video-game-for-programmers-f9k) 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.

[jdno.dev](https://jdno.dev)

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

[jdno_dev - Twitch](https://www.twitch.tv/jdno_dev)