A quick guide to understanding how GitHub Actions works. We will write a workflow file to automate the build process of a C++ sample project which is configured using CMake. This is not a step by step guide to follow, however, a good kick start for the ones who are starting to use GitHub Actions for building their C++ projects.
About GitHub Actions
GitHub Actions (not quite an older name as the release date is 13th Nov 2019) has gotten huge community attention due to its ease of use, flexible workflow customization, and reach to a countless number of programming languages supporting all the operating systems. The brand line, 'from idea to production', is quite explanatory. To simply understand it, take GitHub Actions as an integrated API built and maintained by GitHub to come along with your GitHub repository. It allows you to automate your workflow directly from your repository based on different events, such as on release, on push, on the pull, etc. GitHub manages the execution of your workflow files (I will explain workflow files momentarily), in such maintainable fashion; descriptive and colorful errors and warning provides logs for rich feedback on every step, save history with the related workflow file, etc.
GitHub Actions have proved to be a considerable candidate if you are to write CI/CD pipelines, as the base idea is that you can build, test and deploy your code directly from your git folder using your workflow file/s. A workflow file is a logical/physical representation of your CI/CD pipeline, which is a core block of GitHub Actions. To see the GitHub Actions in action, you need to have at least one workflow file in .github/workflows directory of your base repository. This workflow requires to be written in YAML format and saved with .yml and .yaml file extension. Workflow files contain the custom automated processes depending upon your project type and dependencies.
As mentioned, the processes written in the workflow files trigger once the desired event occurs. For example, on a pull request, it is required first to install the dependencies, build the project, run a few tests and only after successful execution of all these checks, the pull request will be marked as successful.
About the Article
Enough about GitHub Actions. Now that if you are convinced to use GitHub Actions to automate your build pipelines, follow along with the article as I will build a sample project written in C++ to demonstrate how to automate your build pipeline using GitHub actions. This demonstration can show you happening steps; however, you will not be comfortable with GitHub actions unless you will play with them yourself by automating different workflows on the tech stack of your choice. To know more about how your workflows work and help you to build your CI/CD pipeline, refer to this official guideline – "About GitHub Actions".
Project Structure
The project used for the demonstration is a simple 'Hello World' C++ program configured using CMake. I kept the project simple as the gist of the article is to write the workflow file instead of getting the head around the build process of a complex C++ project using CMake. For the purpose, I found the following repository on GitHub. Let's clone it first, Hello World.
Hold on, if you are not really familiar with the CMake tool for C++, yet you are to use it for building your C++ project, it is better to give it a thorough read from its official documentation [reference].
You will use Git version control to download and set up the source code:
$ git clone https://github.com/Iiqra/HelloWorldCmakeSample.git
$ cd cmake-hello-world/
$ mkdir build
$ cd build
$ cmake ..
It should run successfully like shown in the screenshot below:
Figure 1: Configuring CMake files
After cloning the repository (simple Hello World
program), I have created a dedicated folder for all the built files (in our case, this step is not that necessary. However, for big projects, this is always preferable). In the build folder, I have executed the CMake
command to generate the build file using CMake
as you can see the last three lines in the above image, indicating that configuration, generation, and the building of the project files have been successfully completed.
Thus, our build files contain a solution file (.sln), which will be used to build the project and generate the project binaries. I will use MSBuild to build the solution as I'm using on my Windows VM (you can see this build command in the below two images). You can use any alternative of your choice depending upon your preferences and operating system.
$ ls
$ msbuild.exe CMakeHelloWorld.sln
$ cd debug
$ ./CMakeHelloWorld.exe
Now that the solution is built, we can have the binaries either in the debug folder or in the release folder depending upon our build configuration. As you can see in the image below, I have executed the CMakeHelloWorld.exe file, which prints the highlighted message as a result. However, the disclaimer is not functional yet, as the mentioned dependency is commented out for the manual execution of the execution file [Figure 5].
Figure 2: Running Executable File
cmake_minimum_required (VERSION 2.8)
project (CMakeHelloWorld)
#version number
set (CMakeHelloWorld_VERSION_MAJOR 1)
set (CMakeHelloWorld_VERSION_MINOR 0)
#find dependencies
#find_package(cppzmq REQUIRED)
#include the subdirectory containing our libs
add_subdirectory (Hello)
include_directories(Hello)
#indicate the entry point for the executable
add_executable (CMakeHelloWorld Hello HelloWorld.cpp)
# Indicate which libraries to include during the link process.
target_link_libraries (CMakeHelloWorld Hello)
install (TARGETS CMakeHelloWorld DESTINATION bin)
The reason for commenting out this dependency is that the cppzmq
will be installed using vcpkg
and will take some time to be installed. That is why it is better to make this step part of the CI pipeline instead.
Writing Workflow File
Thus far, you are aware of the project structure and have also seen that how we can build it manually (excluding one step of installing vcpkg
dependency, which is, of course, better to be directly installed from the commands written in workflow file). Therefore, you are good to grasp the concepts written in the following workflow file.
name: CI
# Workflow file for windows
on:
push:
branches:
- master
pull_request: {}
jobs:
Windows:
name: build-windows
runs-on: [self-hosted, windows]
steps:
- name: Checkout
uses: actions/checkout@v1
with:
submodules: recursive
- name: Installing vcpkg (windows)
run: |
cd ..
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
git checkout --force 2020.01
.\bootstrap-vcpkg.bat
.\vcpkg.exe install cppzmq:x64-windows
- name: Running cmake (windows)
run: |
cd ${{ github.workspace }}
mkdir build
cd build
cmake .. -DCMAKE_TOOLCHAIN_FILE=${{ github.workspace }}
/../vcpkg/scripts/buildsystems/vcpkg.cmake
- name: Build Solution (windows)
run: |
cd build
MSBuild.exe CMakeHelloWorld.sln
As mentioned initially, explaining the above workflow file is not meant to be covered in this article. I recommend going through the official documentation for this sake. However, for the sake of completion, I better describe just the main steps:
Starting off with the name of my workflow, which is CI in this case. Then the event comes, I have set 'push (on the master branch)' as the main event when this workflow will be triggered, there can be multiple events as well, i.e., [push
, pull_request
]. That way, whenever something will be pushed from this repository on the master branch, the following build-windows
job will start its execution.
Next, we require the type of machine on which the job will run. Your job will run on a fresh instance of the virtual machine every time if you have chosen to go with GitHub-hosted runners, otherwise, you have an option to run your job on the self-hosted runner as well. The provided labels for GitHub runners are windows -latest
or ubuntu-latest
, etc. For a self-hosted runner, we use 'self-hosted
' as the label. Moreover, we can also use a label array, as in my case – self-hosted, windows.
Then comes the integral component – steps. Steps are the sequential tasks inside a job, which are so like different commands performing different actions. The step has some properties, names, uses clause, and run command. Please read about steps in detail from Job Steps.
All the steps are explanatory; I'm installing vcpkg (after cloning it from its git repo) to install the dependencies, which is cppzmq in the provided CMake
file. Then, I'm performing all the steps which been performed manually in the first half of the article, configuring CMake
, building solution files, and then running the execution file.
Here is the time to trigger this workflow by triggering the event on which we set the job to be executed, that is push
event.
Figure 3: Pushing changes on GitHub Repo
Modification is required to push events to occur, and for that, I have added comments (refer to figure 7). Now switch to your repository's action tab and check the action with the recent commit you made.
Figure 4: Workflow file view
This is how the action window looks like; the first commit got failed since there was no runner running at the time of the first commit. However, there was the .yaml file present in the .github/ workflow folder with the push event which got triggered eventually on our first commit. However, the second commit 'Added comment in the CI file' got executed and passed successfully. On the action's console window, you can see the colorful logging of all the steps and underline details of each command which you wrote.
The below screenshot showing the workflow console window is the well explanatory; every step is successfully executed with its underline build output details. Moreover, you can access this console file from the action tab from the in-used repository here for further details and looking deep into the steps.
Figure 5: Expanded vcpkg logs on the console window
Concluding Thoughts
Before summing up, there are two must-knows which I want you to notice:
- You require to be the owner of the repository if you want to run a runner from your machine and get the workflow files running in your repository. Therefore, make sure after cloning my repository for a kick start, you can
init-add-commit-push
again in your personal repository, then launch the runner on your own machine [Setup Runner]. - There are a few already written and tested actions available in the GitHub marketplace for most of the tasks you can get help from while automating your workflows such as run-cmake action and run-vcpkg action. However, to get started with the overall functionality of GitHub actions, I have preferred to write the raw commands instead of using these packages.
All in all, we have gone through the fundamentals of GitHub Actions. We have written one workflow file to automate our pipeline to build a simple C++ program that is configured to be built with CMake settings. Also, we have seen the execution of the workflow file on the repository's actions tab to observe how to build logs and steps are represented.
History
- 20th April, 2020: Initial version