Building and Testing C Projects with GitHub Actions

Building and Testing C Projects with GitHub Actions

Demystifying CI/CD using GitHub Actions for automating your C project builds and tests

Play this article

Continuous Integration and Deployment / Development, CI/CD, is becoming more popular recently. Developers that have little knowledge of CI/CD now stand to have an upper hand at job interviews. Because most companies now use CI/CD automation to streamline their product testing and save time.

CI/CD is automating what developers already do. For example, running regression tests, and deploying new features. It’s important because it saves time and eliminates the possibility of human errors. Have you ever experienced forgetting to add an env key when deploying that new feature that depends on a new secret key? These kinds of little mistakes might cost users' trust.

You can also use CI/CD to automate your code testing. No need to run tests manually for every single commit to the repository. You can run automation for tests using tools like GitHub Actions, and Jenkins.

This article covers how you can use GitHub action to automate tests and builds in your C projects.

You can use GitHub Actions to automate many development workflows. This article covers how to use it with C programming language by explaining the basics of GitHub Actions. If you understand the basics you will be able to use it for other projects written in different programming languages or frameworks.

Prerequisites 💻

To follow this article, you need to have;

  1. an understanding of writing C,

  2. an understanding of how to build C programs with GCC,

  3. gcc installed on your machine, and

  4. a little understanding of Git and GitHub.

Project Setup 🤓

You need to have a codebase with tests first before automating its testing. There is a simple C codebase already provided. You can clone it to get started, but to work with GitHub Actions you need to fork the repository. This is because you need permission to trigger workflow runs on a particular repository. By forking the repository you have access to trigger workflow runs on the forked repository. This is needed so you can visualize and test your workflow runs. The next section covers what is called a workflow.

Repository to fork: https://github.com/devvspaces/dsa-with-c

Description: This is a repository that implements many data structures and algorithms in C and includes tests.

This repository already has GitHub Actions workflow configured for testing. You need to delete the .github folder in the root directory to start afresh.

What Is GitHub Action? 🤔

GitHub Actions helps you to automate your development workflows.

GitHub Actions makes it easy to automate all your software workflows, now with world-class CI/CD. Build, test, and deploy your code right from GitHub. Make code reviews, branch management, and issue triaging work the way you want. - GitHub Actions documentation

GitHub actions can be used to automate many software workflows. A single automation flow from start to end is called a workflow run in GitHub Actions. Developers can configure multiple workflows, each for separate tasks like regression testing and deployment. Each workflow can have one or more jobs.

Workflows can be configured to be triggered by an event in your GitHub repository. For example, you can have one workflow to build and test when there is a new pull request. Then another workflow to deploy your application every time you push a new commit. Also, another workflow to add a label every time someone opens a new issue.

Events are specific operations that trigger a workflow run. Events can be a new pull request or a new build release. You can configure workflows to listen to only events originating from specific branches or tags.

Jobs in a workflow are a group of steps executed on the same runner. A step can be a script or an action. For example, there can be a step for building a C source code and another step for executing the build.

Actions are a complex set of steps that are often repeated. For example, checking out a repository on a runner. Actions are reusable in many workflows and repositories. A developer does not need to rewrite the steps for checking out a repository on a runner, there are already efficient actions for this.

Runners are servers that execute jobs in a workflow when it’s triggered. A runner can only run a single job at a time. GitHub offers Ubuntu Linux, Microsoft Windows, and macOS runners to run your workflows. Each workflow run executes in a fresh, newly-provisioned virtual machine.

Setting Up Your Workflow 💻

To create a new workflow in your repository you need to create a .github/workflows directory in your root directory. Then create a new file called ctest.yml. The file name is at your discretion but it must have a .yml extension. This file will contain configurations to build and test the source code in the repository.

name specifies the name of a workflow file.

# ctest.yml
name: C Build Test

The on key configures events and resources that trigger workflow runs.

The code below configures a workflow run when there is a push or pull request to the master branch.

# ctest.yml
...
on:
  push:
    branches: [ "master" ]
  pull_request:
    branches: [ "master" ]

To specify each job and its respective steps, you can use the job's key and steps key for each job. This is where most configurations exist. Steps like checking out your repository on a runner and executing scripts or commands are here.

jobs groups together all the jobs that run in a workflow.

build-and-execute defines this job's name. The child keys define the properties of this job. This job handles building and executing the code because it’s faster and each job runs on its runner. Meaning, jobs can’t access each other’s files.

runs-on configures the job to run on the latest version of an Ubuntu Linux runner.

steps groups together all the steps that run in the build-and-execute job. Each item nested under this section is a separate action or shell script.

uses specifies this step to run v3 of the actions/checkout action. This action checks out your repository onto the runner, allowing you to run scripts or other actions against your code (such as build and test tools). You should use the checkout action any time your workflow will use the repository's code.

The run keyword instructs this step to execute a command on the runner. In this case, gcc compiles and build all the c files in the project to an executable called dsa. Then the next step executes this executable. The last step deletes the build from the runner, but this step is not required. The runner deletes all the files created in the runner when each job is complete.

# ctest.yml
...
jobs:
  build-and-execute:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: Build source code
      run: gcc -Wall -Werror -Wextra -pedantic -std=gnu89 */*.c */*/*.c */*/*/*.c -o dsa
    - name: Run build
      run: ./dsa
    - name: Clean build
      run: rm -f dsa

Your complete workflow file should now look like this.

# ctest.yml
name: C Build Test

on:
  push:
    branches: [ "master" ]
  pull_request:
    branches: [ "master" ]

jobs:
  build-and-execute:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: Build source code
      run: gcc -Wall -Werror -Wextra -pedantic -std=gnu89 */*.c */*/*.c */*/*/*.c -o dsa
    - name: Run build
      run: ./dsa
    - name: Clean build
      run: rm -f dsa

Now you can commit and push.

Visualizing Your Workflow Run 📈

To visualize your new workflow run. Head over to your repository on GitHub. Under your repository name, click the actions tab. There you will see the new workflow run triggered by the push event you initiated.

Screenshot of the tabs for the github/docs repository. The "Actions" tab is highlighted with an orange outline.

From the list of workflow runs, click the name of the run to see the workflow run summary. In the left sidebar, you will the list of jobs in execution for the workflow. Click the job you want to see. In this case that will be the build-and-execute job. To view the results of a step, click the step. All standard output you will get on a normal machine is available under each step in the job.

Conclusion 👋

Hope you now understand how GitHub Actions work and how to configure a simple workflow. It’s easy to use GitHub Actions to automate workflows for many programming languages and frameworks.

You can always check the documentation for more understanding of writing complex workflows. It’s recommended to check out popular open-source frameworks to learn more about using GitHub Actions.

Resources

GitHub Actions Documentation

Microsoft Github Actions Course

Using Github Actions with Django