…building and deploying from GitLab

And so can you! Too! The Even Better Edition.

I’ve previously shared a kind of ugly but effective .gitlab-ci.yml file that I’ve been using to automatically deploy builds, but it was incomplete, and didn’t cover:

  • Incrementing the version
  • Building the thing

It was also really, super ugly.

Since moving to Godot, I’ve fallen in love with the idea of being able to build and run automated tests in Linux Docker containers. I tried this with a project I’ve been working on (and off and on again) for a while, called The Bacon Game:

I started this project in Unity, and then in Godot Mono, which is a version of Godot that supports C#. This worked great at first, but I found that the Mono version of Godot introduced tonnes of issues and hassles when trying to do automated building and testing. The containerised environment’s setup was much more fragile and fiddly. Also, C# is owned by Microsoft, and if I’m going to go all hippy crunchy granola on this, then I may as well do it properly. So I took The Bacon Game and rewrote it in GD Script, Godot’s purpose-built language, which was actually faster and more pleasant to do than I expected.

Once I had it all running as it had before, but without C#, and had fixed a couple of bugs, I decided to dive back into the automation side, and found great templates and examples created by the amazing Barichello, the GitLab user who created the container images I’m using for this.

You’ll need to get a few things together to use this, namely setting these up as environment variables in your GitLab’s CI/CD settings under Variables:

  • Itch username as ITCHIO_USERNAME (This can be set on the Group level, rather than Repo level, if you’re working on more than one game)
  • Itch.io Butler API key as BUTLER_API_KEY (ditto, this can be set on the Group level, but recommend only doing that if your group is private)
  • Itch game’s slug as ITCHIO_GAME (only really makes sense for this to be done on the repo itself)
image: barichello/godot-ci:4.2.2

variables:
  EXPORT_NAME: the-bacon-game #my game's name – plug in your own, no spaces

stages:
  - test
  - release
  - import-assets
  - export-debug
  - export-release
  - deploy

analyse:
  # used to analyze git repository before the release
  stage: test
  image: registry.gitlab.com/juhani/go-semrel-gitlab:v0.21.1
  script:
    - release test-git
  allow_failure: false
  only:
    - merge_requests
    - main

release-version:
  # update changelog and create a tag
  stage: release
  image: registry.gitlab.com/juhani/go-semrel-gitlab:v0.21.1
  script:
    - release changelog
    - release next-version --allow-current > version.txt
    # - sh update_package.sh
    - release commit-and-tag --create-tag-pipeline CHANGELOG.md version.txt
  allow_failure: false
  only:
    - main


# Cache imported assets between runs
cache:
  key: import-assets
  paths:
    - .godot/imported/

import-assets:
  stage: import-assets
  script:
    - godot --headless --verbose --editor --quit

mac-debug:
  stage: export-debug
  script:
    - mkdir -v -p build/MacOS/debug
    - godot -v --headless --export-debug "macOS" ./build/MacOS/debug/$EXPORT_NAME.zip
  artifacts:
    name: $EXPORT_NAME-$CI_JOB_NAME
    paths:
      - build/MacOS/debug

windows-debug:
  stage: export-debug
  script:
    - mkdir -v -p build/Windows/debug
    - godot -v --headless --export-debug "Windows Desktop" ./build/Windows/debug/$EXPORT_NAME.exe
  artifacts:
    name: $EXPORT_NAME-$CI_JOB_NAME
    paths:
      - build/Windows/debug

linux-debug:
  stage: export-debug
  script:
    - mkdir -v -p build/Linux/debug
    - godot -v --headless --export-debug "Linux" ./build/Linux/debug/$EXPORT_NAME.x86_64
  artifacts:
    name: $EXPORT_NAME-$CI_JOB_NAME
    paths:
      - build/Linux/debug

mac-release:
  stage: export-release
  script:
    - mkdir -v -p build/MacOS/release
    - godot -v --headless --export-release "macOS" ./build/MacOS/release/$EXPORT_NAME.zip
  when: manual
  artifacts:
    name: $EXPORT_NAME-$CI_JOB_NAME
    paths:
      - build/MacOS/release
  dependencies:
    - mac-debug

windows-release:
  stage: export-release
  script:
    - mkdir -v -p build/Windows/release
    - godot -v --headless --export-release "Windows Desktop" ./build/Windows/release/$EXPORT_NAME.exe
  when: manual
  artifacts:
    name: $EXPORT_NAME-$CI_JOB_NAME
    paths:
      - build/Windows/release
  dependencies:
    - windows-debug

linux-release:
  stage: export-release
  script:
    - mkdir -v -p build/Linux/release
    - godot -v --headless --export-release "Linux" ./build/Linux/release/$EXPORT_NAME.x86_64
  when: manual
  artifacts:
    name: $EXPORT_NAME-$CI_JOB_NAME
    paths:
      - build/Linux/release
  dependencies:
    - linux-debug

# Itch.io Deploy
itchio:linux:
  stage: deploy
  when: manual
  script:
    - butler push ./build/Linux/release $ITCHIO_USERNAME/$ITCHIO_GAME:linux --userversion-file version.txt 
  dependencies:
    - linux-release

itchio:windows:
  stage: deploy
  when: manual
  script:
    - butler push ./build/Windows/release $ITCHIO_USERNAME/$ITCHIO_GAME:windows --userversion-file version.txt
  dependencies:
    - windows-release

itchio:mac:
  stage: deploy
  when: manual
  script:
    - butler push ./build/MacOS/release $ITCHIO_USERNAME/$ITCHIO_GAME:mac --userversion-file version.txt
  dependencies:
    - mac-release

A couple of other requirements, you’ll need to add a version.txt that has your version in it.

0.2.3

Worth noting that this version will not be reflected in your game’s binary, this is only a repo/itch concept. I’d like to evolve this script to increment/inject the version to the project’s settings in time. You’ll also need to add a CHANGELOG.md file, that starts like this:

# CHANGELOG

<!--- next entry here -->

Last thing to note is that your commits will need to follow a specific format. In time I’ve adjusted to following this approach on all my projects since it makes the commit history more useful, but some people may find it annoying.

Leave a Reply

Your email address will not be published. Required fields are marked *