Getting Started with GitHub Actions
2023-01-16 | By ShawnHymel
License: Attribution
GitHub Actions is a platform that allows you to build continuous integration and continuous delivery workflows inside GitHub. The idea is to build automated processes that execute whenever a specific action occurs inside one of your repositories. This is especially important in large-scale software projects, but it can also be useful for smaller projects and embedded development.
This tutorial will walk you through the basics of creating a simple GitHub Actions workflow to automatically compile and test a simple C program. You can watch this tutorial in video form here:
GitHub Actions Workflow
A workflow is a collection of actions that trigger whenever some event occurs. For example, you might push a new commit to your repository or someone else opens a pull request. These are examples of possible “triggers.” These triggers cause one or more workflows to execute automatically. A workflow runs in a virtual machine or Docker container, depending on your particular needs and configuration.
Workflows may or may not execute in parallel, so you should assume that they do execute in parallel unless you set up dependences from one workflow to the next. Setting dependencies will force the workflows to execute in sequential order.
A workflow consists of one or more jobs. A “job” is a sequence of events or functions that execute based on your instructions. Jobs within a workflow execute in parallel unless you specify dependencies among jobs.
In the example image above, we show the “Build and Test” workflow that is triggered on some event (e.g. push to the repository). The Build and Test workflow contains one job, which might build the project (to look for compilation errors), perform any unit tests, and/or perform any integration tests. Such a process is an example of a continuous integration (CI) workflow.
Once the Build and Test workflow is complete, the “Delivery” workflow would execute. This workflow contains a single job as well. In this case, the Delivery workflow might build the project to perform any final quality assurance (QA) checks before automatically deploying a new version of the software. This process is an example of a continuous delivery (CD) workflow.
Create a Workflow
Head to github.com and create a new repository named c-action-test. You will need to sign up for a GitHub account if you have not already done so. Clone the repository to your local computer:
git clone https://github.com/<YOUR_USERNAME>/c-action-test
Navigate into the repository’s directory and create a YAML file in the “.github/workflows” directory. We’ll call it my-action.yml.
cd c-action-test/
mkdir -p .github/workflows/
cd .github/workflows/
nano my-action.yml
YAML stands for “yet another markup language” or “yaml ain’t markup language,” depending on your particular take on the history of the language. It is a superset of JSON, which means that YAML interpreters will understand JSON formatting along with unique YAML markup. We will use it to define our workflow, including naming, parameters, triggers, and jobs.
In the my-action.yml file that you just created, enter the following:
# Name of your workflow
name: My Test Workflow
# Define the trigger event(s)
on: [push]
# Jobs run in parallel by default, each runs steps in sequence
jobs:
# Job to print something out
say-hello:
runs-on: ubuntu-latest
steps:
- name: Say hello
run: echo "Hello, GitHub Actions!"
# Job to build and test our C code
build-and-test:
needs: [say-hello]
runs-on: ubuntu-latest
steps:
# Use github.com/actions/checkout to check out this repo
- name: Check out this repo
uses: actions/checkout@v3
# Install necessary packages
- name: Install dependencies
run: sudo apt install -y build-essential python3
# See where we are in the VM
- run: pwd
# Run our test
- name: Run test
run: python3 test.py
At the top, we name our workflow “My Test Workflow.” This name will appear in GitHub when the workflow runs. Next, we define the set of triggers that will cause this workflow to run. We only use push (i.e. when a new commit is pushed to the repository), but this could be a list of events (e.g. [push, pull_request]). You can see the available events here.
From there, we define our jobs. By default, jobs run in parallel. We have two jobs: say-hello and build-and-test. Notice that build-and-test specifies “needs: [say-hello]”. This is a dependency. The build-and-test job will only run when say-hello is finished. As a result, these particular jobs run in sequence.
Under say-hello, we specify that this workflow should run in a virtual machine with the latest Ubuntu image. Available GitHub-hosted images are detailed here. You can also use Docker images, such as those available on Docker Hub.
Under steps, we list the actions that the job should take. In say-hello, we just print “Hello, GitHub Actions!” to the console (using the Linux “echo” command). Anything printed to the terminal like this will show up in our GitHub Actions dashboard. Steps should be organized as a list in YAML, where each item in the list is denoted with a dash (‘-’) character. You can see an example of how YAML lists and objects can be converted to JSON syntax here.
In this job, our only step is named “Say hello,” and the action is given by the “run” command. The run command executes a command on the console in the virtual machine (or Docker container). You can see other available step commands here.
In the second job, we again run on an Ubuntu virtual machine. This time, we use the Action checkout. This is a workflow that has been modularized into an “Action” application that can be called inside other workflows. You can learn more about the “checkout” Action here. The command “uses actions/checkout@v3” executes the checkout Action (version 3) to clone our repository into the Ubuntu virtual machine.
After cloning our repository, we install any necessary dependencies, which include the build-essential and python3 packages. The workflow will print the current working directory (by running pwd) so you can see where this script is executing in the virtual machine.
Finally, we run our test.py Python script that compiles and runs our C program (main.c). This test.py script should exit with 0 if your code successfully compiles and all of your tests passed. This tells GitHub Actions that everything ran successfully. You can return other codes (e.g. 1) to denote errors. See this article to learn more about Linux return codes.
You will need to copy the test.py and main.c files from the previous tutorial to the root folder of the repository. Note that you should change the TEST_DIR variable from “/tests” to “.” as there is no /tests directory in the GitHub Actions Ubuntu image. You can also copy the code from here: test.py and main.c.
Your repository should have the following structure:
c-action-test/
|-- .git/
|-- .github/
|-- workflows/
|-- my-action.yml
|-- main.c
|-- test.py
Test Your Workflow
With everything set up, it is time to test the workflow. Commit and push your changes:
git add --all
git commit -m “Testing my workflow”
git push
Navigate to your project on GitHub. Click Actions, and you should see your workflows.
If you see a red ‘X’, then there were errors in the workflow. A green checkmark indicates that the workflow ran successfully. Click on the individual commit to view the log for that workflow run.
The log will give you information about the workflow and display anything you printed to the console in your tests. With any luck, you should see a green checkmark to let you know that your tests have passed!
Recommended Reading
Here is some good reading material if you would like to learn more about GitHub Actions:
- Official GitHub Actions documentation
- Great overview of GitHub Actions
- Security best practices for GitHub Actions
Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.
Visit TechForum