{"id":858,"date":"2023-10-05T09:03:26","date_gmt":"2023-10-05T09:03:26","guid":{"rendered":"https:\/\/tbekk.com\/devstream\/?p=858"},"modified":"2023-10-05T09:03:26","modified_gmt":"2023-10-05T09:03:26","slug":"github-actions-for-c-and-qt","status":"publish","type":"post","link":"https:\/\/tbekk.com\/devstream\/2023\/10\/05\/github-actions-for-c-and-qt\/","title":{"rendered":"GitHub Actions for C++ and Qt"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">How to use GitHub Actions for C++ and Qt projects<\/h2>\n\n\n\n<p>You may already be hosting your code on GitHub, but do you know that GitHub provides a built-in Continuous Integration solution called&nbsp;<strong>GitHub Actions<\/strong>&nbsp;that is very easy to set up and free for public repositories?<\/p>\n\n\n\n<p>In this article I\u2019m providing a crash course on what GitHub Actions are and how to use it for your C++ and Qt projects.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Tl;DR<\/h2>\n\n\n\n<p>GitHub has a built-in CI\/automation system called \u201cGitHub Actions\u201d that:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>You&nbsp;<a href=\"https:\/\/docs.github.com\/en\/actions\/learn-github-actions\/understanding-github-actions\">configure<\/a>&nbsp;through YAML files in&nbsp;<code>.github\/workflows<\/code><\/li>\n\n\n\n<li>Can run on GitHub-provided cloud runners or on-prem\n<ul class=\"wp-block-list\">\n<li>For MacOS, Windows and Linux<\/li>\n\n\n\n<li>Can run in containers too<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>Is&nbsp;<strong>free<\/strong>&nbsp;on cloud runners for public repositories (and for private you get some build minutes for free too)<\/li>\n\n\n\n<li>Allows packaging common CI routines into reusable components. There\u2019s a marketplace for them:&nbsp;<a href=\"https:\/\/github.com\/marketplace?type=actions\">https:\/\/github.com\/marketplace?type=actions<\/a><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">What are GitHub Actions?<\/h2>\n\n\n\n<p>GitHub Actions (GHActions from now on) is a continuous integration system available on GitHub.<\/p>\n\n\n\n<p>It\u2019s configured in the repository\u2019s&nbsp;<code>.github\/workflows<\/code>&nbsp;folder. The system is very flexible and configurable. See&nbsp;<a href=\"https:\/\/docs.github.com\/en\/actions\">https:\/\/docs.github.com\/en\/actions<\/a><\/p>\n\n\n\n<p>Each file there is a&nbsp;<strong>workflow<\/strong>. Workflows can have multiple&nbsp;<strong>jobs<\/strong>&nbsp;that can run in parallel or in a sequence. Each job has&nbsp;<strong>steps<\/strong>, i.e. actual commands that run for this job.<\/p>\n\n\n\n<p>Workflows can be triggered by&nbsp;<strong>events<\/strong>&nbsp;happening in the repository. Events don\u2019t necessarily have to do anything with code, there\u2019s tons of them.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><img decoding=\"async\" src=\"https:\/\/www.kdab.com\/wp-content\/uploads\/stories\/image2023-6-2_9-26-37.png\" alt=\"Conceptual diagram of GitHub Actions\" class=\"wp-image-32638\"\/><\/figure><\/div>\n\n\n<p>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:&nbsp;<a href=\"https:\/\/docs.github.com\/en\/actions\/using-workflows\/events-that-trigger-workflows\">https:\/\/docs.github.com\/en\/actions\/using-workflows\/events-that-trigger-workflows<\/a><\/p>\n\n\n\n<p>That\u2019s an important thing to take away:&nbsp;<em>GitHub Actions is for much more than code, you can automate things around all aspects of your repo.<\/em><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Where are actions executed?<\/h2>\n\n\n\n<p>Github actions can be executed on two targets:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>On GitHub-provided cloud runners (like in the example below) running on Azure.<\/li>\n\n\n\n<li>On&nbsp;<strong>self-hosted runners<\/strong>&nbsp;that you provide yourself and hook up to your GitHub account (see&nbsp;<a href=\"https:\/\/docs.github.com\/en\/actions\/hosting-your-own-runners\/managing-self-hosted-runners\/about-self-hosted-runners\">self-hosted runners documentation<\/a>)<\/li>\n<\/ul>\n\n\n\n<p>To learn more about specs and what software is available on GitHub runners, consult this documentation page:&nbsp;<a href=\"https:\/\/docs.github.com\/en\/actions\/using-github-hosted-runners\/about-github-hosted-runners\">https:\/\/docs.github.com\/en\/actions\/using-github-hosted-runners\/about-github-hosted-runners<\/a><\/p>\n\n\n\n<p>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:\u00a0<a href=\"https:\/\/docs.github.com\/en\/actions\/using-jobs\/running-jobs-in-a-container\">https:\/\/docs.github.com\/en\/actions\/using-jobs\/running-jobs-in-a-container<\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Matrices<\/h2>\n\n\n\n<p>One interesting feature similar to other CI systems is a\u00a0<code>matrix<\/code>. You can define axes of some values and have GHActions generate jobs automatically based on the combination of the values from the axes:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs:\r\n  example_matrix:\r\n    strategy:\r\n      matrix:\r\n        version: &#91;10, 12, 14]\r\n        os: &#91;ubuntu-latest, windows-latest]<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Extensibility<\/h2>\n\n\n\n<p>Steps in jobs can be packaged to reusable components called (ekhm)&nbsp;<strong>actions<\/strong>. Action can have inputs and outputs. Custom actions can be written in&nbsp;<a href=\"https:\/\/docs.github.com\/en\/actions\/creating-actions\/creating-a-javascript-action\">JavaScript<\/a><\/p>\n\n\n\n<p>You use actions created by you or other people by adding the&nbsp;<code>uses:<\/code>&nbsp;clause in your step:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>- name: Setup VS\r\n  uses: ilammy\/msvc-dev-cmd@v1 # action from some kind person on GitHub\r\n- uses: .\/.github\/actions\/setup-rust # action from your own repository<\/code><\/pre>\n\n\n\n<p>There\u2019s also a way to call workflows from other workflows:&nbsp;<a href=\"https:\/\/docs.github.com\/en\/actions\/using-workflows\/reusing-workflows\">https:\/\/docs.github.com\/en\/actions\/using-workflows\/reusing-workflows<\/a>.<\/p>\n\n\n\n<p>GitHub itself provides tons of such actions as separate small repos:&nbsp;<a href=\"https:\/\/github.com\/actions\/\">https:\/\/github.com\/actions\/<\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Example<\/h2>\n\n\n\n<p>I created a tiny QML application (basically the template from Qt Creator) and set up GHActions for it. You can see the repository at&nbsp;<a href=\"https:\/\/github.com\/MiKom\/QtQuickApp\">https:\/\/github.com\/MiKom\/QtQuickApp<\/a>.<\/p>\n\n\n\n<p>It\u2019s driven by a workflow file located at\u00a0<code>.github\/workflows\/build.yaml<\/code>. Replicated here:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>name: CI\r\n \r\non: push # when to trigger this. Here, on every push\r\njobs:\r\n  build_and_test:\r\n    name: \"Build and test\"\r\n    strategy:\r\n      matrix:\r\n        os: &#91;ubuntu-latest, windows-latest] # we build on GitHub-provided Windows and Linux images\r\n    runs-on: ${{ matrix.os }} # use value from the matrix\r\n    steps:\r\n    - name: Install dependencies (linux)\r\n      run: sudo apt install ninja-build\r\n      if: matrix.os == 'ubuntu-latest' # conditional, runs this step only on the Ubuntu runner\r\n    - name: Install Ninja (windows)    # Ninja is not available in GitHub-provided images,\r\n                                       # see https:\/\/github.com\/actions\/runner-images\/issues\/514\r\n      run: choco install ninja         # So let's install it through Chocolatey\r\n      if: matrix.os == 'windows-latest'\r\n    - name: Install Qt\r\n      uses: jurplel\/install-qt-action@v3\r\n      with:\r\n        version: '6.5.2'\r\n    - uses: ilammy\/msvc-dev-cmd@v1 # This action essentially calls vcvarsall.bat for the latest VS in the runner for x64\r\n    - uses: actions\/checkout@v3    # Actually check out the sources. GH Actions can run for events that may not require\r\n                                   # sources (e.g. when someone comments on an issue)\r\n \r\n    # Here we call CMake manually, there are solutions for that in the Marketplace: https:\/\/github.com\/marketplace\/actions\/run-cmake\r\n    - name: Build\r\n      # We don't need to set up the environment variable for CMake to see Qt because the install-qt-action\r\n      # sets up the necessary variables automatically\r\n      run: cmake -S . -B build -G \"Ninja Multi-Config\" &amp;&amp; cmake --build build --config Debug<\/code><\/pre>\n\n\n\n<p>Notice how I used the community-provided action for installing Qt. For more information, see&nbsp;<a href=\"https:\/\/github.com\/marketplace\/actions\/install-qt\">https:\/\/github.com\/marketplace\/actions\/install-qt<\/a>. To generate the configuration for your Qt needs for this action see&nbsp;<a href=\"https:\/\/ddalcino.github.io\/aqt-list-server\/\">https:\/\/ddalcino.github.io\/aqt-list-server\/<\/a><\/p>\n\n\n\n<p>You can view the results of running this workflow in the \u201cActions\u201d tab of your repository:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"https:\/\/github.com\/MiKom\/QtQuickApp\/actions\/runs\/5975745320\/job\/16212294196\"><img decoding=\"async\" src=\"https:\/\/www.kdab.com\/wp-content\/uploads\/stories\/github_action_run-1024x713.png\" alt=\"\" class=\"wp-image-32647\"\/><\/a><\/figure><\/div>\n\n\n<h2 class=\"wp-block-heading\">But GitHub is cloud\/proprietary\/closed source!<\/h2>\n\n\n\n<p>It\u2019s understandable to be reluctant to lock-in to a service provided by an SaaS company. Thankfully, there\u2019s an open-source re-implementation of GitHub Actions called&nbsp;<a href=\"https:\/\/github.com\/nektos\/act\/\">Act<\/a>. It was designed to let you run your workflows locally.<\/p>\n\n\n\n<p><a href=\"https:\/\/about.gitea.com\/\">Gitea<\/a>, an open-source git hosting solution is now integrating a fork of Act. The feature is described in Gitea\u2019s documentation:&nbsp;<a href=\"https:\/\/docs.gitea.com\/usage\/actions\/overview\">https:\/\/docs.gitea.com\/usage\/actions\/overview<\/a>.<\/p>\n\n\n\n<p>It\u2019s source-compatible with GitHub actions so if your workflows are using the subset of features supported by Gitea, the migration should be rather painless.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Further reading<\/h2>\n\n\n\n<p>There\u2019s a lot more to GHActions than is described here. Deployments, secrets management, environments, cacheing, artifacts storage and sharing, you name it. Learn more at&nbsp;<a href=\"https:\/\/docs.github.com\/en\/actions\">https:\/\/docs.github.com\/en\/actions<\/a><\/p>\n\n\n\n<p>A good way to learn is also to look at repositories that already do stuff through GHActions, like Slint:&nbsp;<a href=\"https:\/\/github.com\/slint-ui\/slint\/blob\/master\/.github\/workflows\/ci.yaml\">https:\/\/github.com\/slint-ui\/slint\/blob\/master\/.github\/workflows\/ci.yaml<\/a><\/p>\n\n\n\n<p>Also, when you try to add an action, you can choose from many templates as a starting point:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><img decoding=\"async\" src=\"https:\/\/www.kdab.com\/wp-content\/uploads\/stories\/github_actions_templates-1024x917.png\" alt=\"\" class=\"wp-image-32648\"\/><\/figure><\/div>\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>GitHub Actions is a very flexible system that you may use if you host your stuff on GitHub. It seems to implement everything you\u2019d expect from a modern CI system and more.<\/p>\n\n\n\n<p>We already use it for e.g.&nbsp;<a href=\"https:\/\/github.com\/KDAB\/hotspot\/actions\">HotSpot<\/a>.<\/p>\n\n\n\n<p>Caveat is, of course, that it\u2019s a proprietary system and a form of vendor lock-in. You\u2019re 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\u2019re out of beta.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-text-color has-light-gray-color has-alpha-channel-opacity has-light-gray-background-color has-background is-style-wide\"\/>\n\n\n\n<ul class=\"wp-block-list\">\n<li><em><strong>Link:<\/strong><\/em> <a href=\"https:\/\/www.kdab.com\/github-actions-for-cpp-and-qt\/\"><em>KDAB<\/em><\/a><\/li>\n\n\n\n<li><strong><em>Author:<\/em><\/strong> <a href=\"https:\/\/www.kdab.com\/author\/milosz-kosobucki\/\"><em>Mi\u0142osz Kosobucki<\/em><\/a><\/li>\n\n\n\n<li><em><strong>Publication date:<\/strong> Sept. 21, 2023<\/em><\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-text-color has-light-gray-color has-alpha-channel-opacity has-light-gray-background-color has-background is-style-wide\"\/>\n","protected":false},"excerpt":{"rendered":"<p>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&#8230; <a class=\"read-more-link\" href=\"https:\/\/tbekk.com\/devstream\/2023\/10\/05\/github-actions-for-c-and-qt\/\">Read more &raquo;<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[51,10,256],"tags":[314,77,28,313,281,55],"class_list":["post-858","post","type-post","status-publish","format-standard","hentry","category-article","category-development","category-devops","tag-automation","tag-c-3","tag-ci","tag-devops","tag-github-actions","tag-qt"],"_links":{"self":[{"href":"https:\/\/tbekk.com\/devstream\/wp-json\/wp\/v2\/posts\/858","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tbekk.com\/devstream\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tbekk.com\/devstream\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tbekk.com\/devstream\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tbekk.com\/devstream\/wp-json\/wp\/v2\/comments?post=858"}],"version-history":[{"count":1,"href":"https:\/\/tbekk.com\/devstream\/wp-json\/wp\/v2\/posts\/858\/revisions"}],"predecessor-version":[{"id":859,"href":"https:\/\/tbekk.com\/devstream\/wp-json\/wp\/v2\/posts\/858\/revisions\/859"}],"wp:attachment":[{"href":"https:\/\/tbekk.com\/devstream\/wp-json\/wp\/v2\/media?parent=858"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tbekk.com\/devstream\/wp-json\/wp\/v2\/categories?post=858"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tbekk.com\/devstream\/wp-json\/wp\/v2\/tags?post=858"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}