GitHub Actions for C++ and Qt

      No Comments on GitHub Actions for C++ and Qt

How to use GitHub Actions for C++ and Qt projects

You may already be hosting your code on GitHub, but do you know that GitHub provides a built-in Continuous Integration solution called GitHub Actions that is very easy to set up and free for public repositories?

In this article I’m providing a crash course on what GitHub Actions are and how to use it for your C++ and Qt projects.

Tl;DR

GitHub has a built-in CI/automation system called “GitHub Actions” that:

  • You configure through YAML files in .github/workflows
  • Can run on GitHub-provided cloud runners or on-prem
    • For MacOS, Windows and Linux
    • Can run in containers too
  • Is free on cloud runners for public repositories (and for private you get some build minutes for free too)
  • Allows packaging common CI routines into reusable components. There’s a marketplace for them: https://github.com/marketplace?type=actions

What are GitHub Actions?

GitHub Actions (GHActions from now on) is a continuous integration system available on GitHub.

It’s configured in the repository’s .github/workflows folder. The system is very flexible and configurable. See https://docs.github.com/en/actions

Each file there is a workflow. Workflows can have multiple jobs that can run in parallel or in a sequence. Each job has steps, i.e. actual commands that run for this job.

Workflows can be triggered by events happening in the repository. Events don’t necessarily have to do anything with code, there’s tons of them.

Conceptual diagram of GitHub Actions

For example, creating a new issue in your repository is an event. You can then have an action that will look into the created issue and e.g. welcome the first-time contributor or check if the issue description contains appropriate information. You can also trigger the workflow through API from outside, manually, or make it run on a cron schedule: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows

That’s an important thing to take away: GitHub Actions is for much more than code, you can automate things around all aspects of your repo.

Where are actions executed?

Github actions can be executed on two targets:

  • On GitHub-provided cloud runners (like in the example below) running on Azure.
  • On self-hosted runners that you provide yourself and hook up to your GitHub account (see self-hosted runners documentation)

To learn more about specs and what software is available on GitHub runners, consult this documentation page: https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners

With both of these options, you can run your workflow directly on the operating system of the runner, or you can run your workflow in a Docker container: https://docs.github.com/en/actions/using-jobs/running-jobs-in-a-container

Matrices

One interesting feature similar to other CI systems is a matrix. You can define axes of some values and have GHActions generate jobs automatically based on the combination of the values from the axes:

jobs:
  example_matrix:
    strategy:
      matrix:
        version: [10, 12, 14]
        os: [ubuntu-latest, windows-latest]

Extensibility

Steps in jobs can be packaged to reusable components called (ekhm) actions. Action can have inputs and outputs. Custom actions can be written in JavaScript

You use actions created by you or other people by adding the uses: clause in your step:

- name: Setup VS
  uses: ilammy/msvc-dev-cmd@v1 # action from some kind person on GitHub
- uses: ./.github/actions/setup-rust # action from your own repository

There’s also a way to call workflows from other workflows: https://docs.github.com/en/actions/using-workflows/reusing-workflows.

GitHub itself provides tons of such actions as separate small repos: https://github.com/actions/

Example

I created a tiny QML application (basically the template from Qt Creator) and set up GHActions for it. You can see the repository at https://github.com/MiKom/QtQuickApp.

It’s driven by a workflow file located at .github/workflows/build.yaml. Replicated here:

name: CI
 
on: push # when to trigger this. Here, on every push
jobs:
  build_and_test:
    name: "Build and test"
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest] # we build on GitHub-provided Windows and Linux images
    runs-on: ${{ matrix.os }} # use value from the matrix
    steps:
    - name: Install dependencies (linux)
      run: sudo apt install ninja-build
      if: matrix.os == 'ubuntu-latest' # conditional, runs this step only on the Ubuntu runner
    - name: Install Ninja (windows)    # Ninja is not available in GitHub-provided images,
                                       # see https://github.com/actions/runner-images/issues/514
      run: choco install ninja         # So let's install it through Chocolatey
      if: matrix.os == 'windows-latest'
    - name: Install Qt
      uses: jurplel/install-qt-action@v3
      with:
        version: '6.5.2'
    - uses: ilammy/msvc-dev-cmd@v1 # This action essentially calls vcvarsall.bat for the latest VS in the runner for x64
    - uses: actions/checkout@v3    # Actually check out the sources. GH Actions can run for events that may not require
                                   # sources (e.g. when someone comments on an issue)
 
    # Here we call CMake manually, there are solutions for that in the Marketplace: https://github.com/marketplace/actions/run-cmake
    - name: Build
      # We don't need to set up the environment variable for CMake to see Qt because the install-qt-action
      # sets up the necessary variables automatically
      run: cmake -S . -B build -G "Ninja Multi-Config" && cmake --build build --config Debug

Notice how I used the community-provided action for installing Qt. For more information, see https://github.com/marketplace/actions/install-qt. To generate the configuration for your Qt needs for this action see https://ddalcino.github.io/aqt-list-server/

You can view the results of running this workflow in the “Actions” tab of your repository:

But GitHub is cloud/proprietary/closed source!

It’s understandable to be reluctant to lock-in to a service provided by an SaaS company. Thankfully, there’s an open-source re-implementation of GitHub Actions called Act. It was designed to let you run your workflows locally.

Gitea, an open-source git hosting solution is now integrating a fork of Act. The feature is described in Gitea’s documentation: https://docs.gitea.com/usage/actions/overview.

It’s source-compatible with GitHub actions so if your workflows are using the subset of features supported by Gitea, the migration should be rather painless.

Further reading

There’s a lot more to GHActions than is described here. Deployments, secrets management, environments, cacheing, artifacts storage and sharing, you name it. Learn more at https://docs.github.com/en/actions

A good way to learn is also to look at repositories that already do stuff through GHActions, like Slint: https://github.com/slint-ui/slint/blob/master/.github/workflows/ci.yaml

Also, when you try to add an action, you can choose from many templates as a starting point:

Conclusion

GitHub Actions is a very flexible system that you may use if you host your stuff on GitHub. It seems to implement everything you’d expect from a modern CI system and more.

We already use it for e.g. HotSpot.

Caveat is, of course, that it’s a proprietary system and a form of vendor lock-in. You’re buying into the GitHub ecosystem with a tool that is not as easily transferable as git repository itself. Gitea Actions may offer relief here, once they’re out of beta.



Leave a Reply

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