Table of Contents
GitHub Actions is a Continuous Integration, Delivery, and Deployment workflow manager made by the code repository hosting service GitHub. GitHub was a little late in the CI/CD pipeline management compared to its competitor GitLab and the good old Jenkins. After a beta testing period, the service opened in 2018.
GitHub Actions is fully integrated with the service, accessible via the “Actions” tab on a repository (if the owner enabled it). The workflows are written in YAML with a declarative language describing the event triggering the pipeline, the runner kind, some environment-related informations, and the jobs definitions. The workflows are declared as YAML files placed in a
.github/workflows/ folder at the root of the repository. A workflow can be modified in a branch and ran from it in the Actions tab, you don’t have to stick to your main branch.
An Action workflow is ran by a “runner”. The runner is an execution environment containing several runtimes, build, test and deployment tools. GitHub provides hosted runners that are virtual environments in the Microsoft Azure infrastructure. Three kind of runners are available : Linux-based (Ubuntu), macOS, and Windows Server. Each of them have two or three versions available. The GitHub hosted runners are based on Microsoft Azure Standard_DS2_v2 virtual machines providing 2 vCPU and 7GB RAM. Exception for the macOS runners that are based on GitHub’s own macOS Cloud. The runner can also be self-hosted as the software is released under the MIT license.
As a SaaS provider, GitHub Action has subscriptions plans granting more or less capabilities of Action usages. In this case, the limitation is the runner usage in minutes. At the time this article is written, the subscriptions plan are the following ones :
- Free : available with all public repositories and free accounts, granting 2 000 minutes / month
- Pro : 3 000 minutes / month
- Team : 3 000 minutes / month
- Enterprise : 50 000 minutes / month
The additional hosted runner minutes plan are :
- 0.008$ / minute for a Linux
- 0.016$ / minute for a Windows
- 0.08$ / minute for a macOS
Self-hosted runners are completely free of usage charges, excepted your hosting costs of course. They can be hosted on a server or, preferably, in a Kubernetes cluster that can scale the number of workers according to the load.
As said above, a GitHub Action workflow is a YAML file containing at least the following parts :
- An event that triggers the workflow (manual, scheduled, on pull request, on merge…)
- One or more Jobs : a group of workflow actions
- A runner to run on
- A step representing an action to execute in the job
Example of a Node.JS application build workflow :
name: Build my Node.JS application on: push: branches: [ main ] pull_request: branches: [ main ] jobs: build: runs-on: ubunt-latest steps: - uses: actions/checkout@v3 - name: Use node.js 16.x uses: actions/setup-node@v3 with: node-version: '16.x' - name: Install dependencies run: npm ci - name: Build run : npm run build - name: Test run: npm run test
Let’s explain this :
name: Optional, will display the value instead of the workflow filename in the GitHub Action list
on: This is the Trigger event. In this case, we have two triggers : on push on the main branch, and on pull request on the main branch.
jobs: The job list of the workflow
build: the “build” job, it will be displayed with this name on the Action viewer
runs-on: the runner that will be used. In this case, the latest Ubuntu available
steps: each step of the job, that’s the smallest action unit available
name: a display name for the step, it’s optional
uses: this keyword tells to use a marketplace Action (we’ll see that after). In this case :
actions/setup-nodewith version 3.x
run: another action keyword. This one executes shell commands
Each jobs and steps have input and output contexts, which means a step can use the output of a previous one as an input for itself. For example, a first step uses the REST API Action to query some information, the second parse and process the result. Each step can also use conditional or parsing expressions. Basically, at least on Linux runners as I haven’t tested the others, each step is transformed into a sub-shell script executed at once.
The Actions Marketplace remind me in some ways the same problem as Jenkins. It relies a lot on its plugins library which is enormous, but it’s sometimes a gamble. You will find something interesting, but the Action is not maintained anymore or poorly documented, just like Jenkins’ plugins main problem in my experience. And sometimes, even official GitHub Actions are not anymore maintained, routing the users to a community provided Action. I’m not a big fan of this way, mainly because of this idea :
xkcd Dependency - License CC-BY-SA-NC 2.0
I’m using GitHub Action since almost one month now, as one of my clients choose it for its CI/CD usage. In my career, I’ve mainly used Jenkins and I think that GitHub Actions marketplace has the same issue as Jenkins’ plugins library. Sometimes you’ll find something that is exactly what you need, but it’s not maintained anymore (and possibly broken or breached). Sometimes, the documentation will be inexistant or reduced to the most minimalist way forcing you to try things and read the code to understand how does it works. Another moment, you’ll find a plugin that covers half of your needs and you’ll have to compensate. And sometimes everything’s good until it’s broken and you pass a day to understand why. With Jenkins, I’ve learned to avoid using too much third-party utilities because of the randomness of their evolution. For example, most Microsoft Azure plugins are now deprecated and recommend to directly use the
az command-line tool. Fortunately, I’ve did it since the beginning.
Despite these apprehensions, GitHub Action is working well and the various triggers are nicely integrated with the rest of the ecosystem. The marketplace is pretty-well furnished and the possibility to make your own Actions is not that complicated (maybe I’ll write something about this if I have to opportunity to do some).
However, I’ve encountered some weird behavior in the shell tasks where the commands are not processed in the same way as on my own laptop (running on Fedora, so why an Ubuntu-based runner would have difficulties to run basic commands ?), maybe because of the pre-processing of the step by the runtine, and that’s very annoying. Another annoying thing is as the workflow is stored on GitHub, you need to commit / push every modification to test them. When you are learned to use them and, like me, you work in a yaks and away model, that could be very boring. Fortunately, I’ve found this project that allows to execute locally a workflow based on a container image, very helpful ! I’ll write about this on a next article.