Getting into it
Recently I had decided I wanted to start creating a CI/CD (Continuous Integration / Continuous Delivery) pipeline for some of the code I have up on Github. I have dealt with CI/CD in the past and generally have enjoyed the consistent feedback loop on the changes I push. One thing I did want with this CI was some kind of feedback loop in Github to show the current state of the project (failing, passing, etc).
Normally in these cases, I would reach for Jenkins, but I tend to like to try something different when I am working on non-work related projects. Looking around the big competitors I noticed had been Travis and Circle. However, I had heard of Github Actions and that sounded like a perfect fit for what I wanted to do.
It just so happened that the public release of Github's actions timed very well with me wanting to create some CI/CD for my projects.
Enter Github actions
Recently Github released Github Actions to the general public. Github actions is a CI/CD system for code hosted on Github. Actions can be added to any Github project by creating a YAML file in .github/workflows.
In a workflow there are a couple of important properties to know about.
- name: The name property gives the workflow a name.
- on: This can be one of a couple of different values. This is the trigger condition, when can this action be automatically executed. For me, the most valuable actions are triggered on
push
orpull_request
but there are plenty of other triggers listed here. -
jobs: This is the key to the workflow. A workflow has at least 1 job and a job is the main body of work to be done in a workflow.
- runs-on: Controls what the job runs on. So far I have only had to use
ubuntu-latest
, but Windows and Mac OS's are supported. For more information see this page. - steps: These are the individual actions that a job will take.
- run: This is the script that will run in the step. In the case of ubuntu, this should be a bash script.
- runs-on: Controls what the job runs on. So far I have only had to use
Simple example workflow
name: My CI
on: [push, pull_request]
jobs:
greet:
name: Greet
runs-on: ubuntu-latest
steps:
- name: Greet
run: echo 'Hello world'
Putting actions to use
After a bit of research, I started getting to work on creating a CI pipeline for one of my go based projects.
In this process, I created two jobs a build job and an integration test job. In the build job, a couple of things had been done.
- Install go on the ubuntu image.
- Checkout related go code from Github.
- Install dependencies for go code.
- Build the go application.
- Run the unit tests.
In the integration test job, a couple more things would be done.
- Install go on the ubuntu image.
- Checkout related go code from Github
- Install dependencies and start the go application.
- Install NodeJS
- Build and start a simple web application. This was used to test Webhooks with my go application.
- Install python 2.
- Run integration tests that were written in python to test the go server.
The end result for all of this is the following yml file.
name: Go
on: [push, pull_request]
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.13
uses: actions/setup-go@v1
with:
go-version: 1.13
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v1
- name: Get dependencies
run: |
go get -v -d -t ./...
if [ -f Gopkg.toml ]; then
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
dep ensure
fi
- name: Build
run: go build -v .
working-directory: ./src
- name: Test
run: go test
working-directory: ./src
Test:
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.13
uses: actions/setup-go@v1
with:
go-version: 1.13
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v1
- name: Get dependencies
run: |
go get -v -d -t ./...
if [ -f Gopkg.toml ]; then
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
dep ensure
fi
- name: Start
run: |
go build -v .
./src ../test/RuntimeConfig.json &
working-directory: ./src
- name: Install node
uses: actions/setup-node@v1
with:
node-version: '10.x'
- name: Start web hook server
run: |
npm install
npm run start &
working-directory: ./test/webhooktester
- name: Install python
uses: actions/setup-python@v1
with:
python-version: '2.x'
- name: Install dependencies
run: |
pip install requests
pip install assertpy
- name: Run E2E Tests
run: python apitest.py
working-directory: ./test
UI
Run output
Commit feedback
Final thoughts
Github actions are really easy to get up and running. All you have to do is add a yml file to your source control and you are up and running. There is no concern around managing infrastructure like you would have to do with Jenkins.
One big difference that I was worried about was the loss of a scripting language in my pipeline. Again I am used to Jenkins in which I could define my workflow and have the power of groovy. At no point did I really feel limited by only being able to execute bash scripts.
It is also incredibly nice to have a decent UI to show my Job status as well as immediate feedback on my commit or PR around CI status.