
6 minutes read
GitHub Actions enables GitHub users to define powerful workflows that run on Github. I saw potential to model a CI/CD workflow with GitHub actions. This blog post is about a general CI/CD setup with GitHub Actions journey.
Github Actions in a nutshell
Actions are currently in beta phase. Actions are basically a composition of docker container runs. Containers in Actions are referenced as:
- Dockerfiles in GitHub repositories
- Docker images in public Docker registries.
Please refer to Using a Docker image in an GitHub Action for more details.
To use an action, refer to the action in your .workflow file using a path relative to the repository directory e.g.
action "my action" {
uses = "[using a path relative to the repository directory]"
}
This a specific example (based on shaking-finger-action):
action "post gif on fail" {
uses = "jessfraz/shaking-finger-action@master"
secrets = ["GITHUB_TOKEN"]
}
You can choose from a wide range of existing GitHub Actions or other listings like awesome actions. There are also icons that may help you to make your actions look pretty. A UI editor allows you to arrange actions as you can do with code as well.
My goal was to use Open Source actions as well as to define my own action. I used a golang hello world for my CI/CD github actions because I did not find too many Github Actions with golang examples.
A collection of actions is a workflow. The GitHub platform runs your workflows and actions. Workflows and subsequently actions are cron like scheduled or triggered by events. ForkEvent and PullRequestEvent are just two examples of a longer list of available events.
Actions have access to the payload of the event and a copy of the source code.
This is the Dockerfile I used for my CI/CD setup:
FROM golang:1.12.4
LABEL "inspiredby"="https://github.com/actions/bin/blob/master/sh/Dockerfile @ 2019 04 24"
LABEL "maintainer"="Lothar Schulz <http://bit.ly/2zVLbWh>"
LABEL "version"="0.0.2"
LABEL "com.github.actions.name"="golang / make / bash for GitHub Actions"
LABEL "com.github.actions.description"="Runs one or more golang / make / bash commands in an Action"
LABEL "com.github.actions.icon"="terminal"
LABEL "com.github.actions.color"="blue"
COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
I followed GitHubs recommendation to create a shell script called entrypoint.sh that is called from the ENTRYPOINT instruction. Please make sure the entrypoint.sh is executable e.g. with chmod +x entrypoint.sh.
A shell script like entrypoint.sh may look like this:
#!/bin/bash
# inspired by https://github.com/actions/bin/blob/master/sh/entrypoint.sh @ 2019 04 24
# http://redsymbol.net/articles/unofficial-bash-strict-mode/
set -euo pipefail
IFS=$'\n\t'
for cmd in "$@"; do
echo "Running '$cmd'..."
if sh -c "$cmd"; then
# no op
echo "Successfully ran '$cmd'"
else
exit_code=$?
echo "Failure running '$cmd', exited with $exit_code"
exit $exit_code
fi
done
Continuous Integration and Continuous Delivery with Github Actions
I experienced recurring patterns of Continuous Integration and Continuous Delivery in the past. From those I wanted to model the following with GitHub actions:
Continuous Integration
- build source code
- run various kinds of tests
- build deployment artifact
Continuous Delivery
- deploy either on
- all code changes
- on specific ones e.g. on pushed to default branch – often master
Workflow
Based on the considerations above I created this workflow:
This workflow gets triggered on pull request events so that every push to a branch belonging to a pull request triggers a workflow run.
Continuous Integration stage
There are three parallel actions triggered in the first Continuous Integration stage:
- Unit test the golang source code
- Benchmark the golang source code
- Post a gif on fail
Unit and benchmark action are the only golang specific actions.
Post a gif on fail is calling the shaking-finger-action. This Github Action displays a gif of Conan O’Brien shaking his finger to a pull request on fail.
Even if the subsequent action is failing, shaking-finger still posts a gif to the pull request.
Continuous Delivery stage
In this showcase setup, I consider passing tests good enough to build a Docker image in the next action.
In case the Docker image is created successfully, a subsequent action performs a log-in to DockerHub. Pushing the created image is the last step in this workflow. This completes the delivery in this showcase setup.
I use $GITHUB_SHA environment variable to tag the docker image. GitHub sets some environment variables by default.
Github Actions offer an option to store secrets. The easiest way is to add secrets is using the visual workflow editor. I used secrets to talk to the GitHub API as well as to push to DockerHub.
Wishes & Learnings
I really enjoyed playing around with GitHub Actions and I would consider GitHub Actions in production once it is general available. I would love to experience the following details about actions and workflows differently in the future.
Workflow level environment-variables
Unfortunately, environment-variables can only be set currently on action level. I’d love to define environment-variables on workflow level as well. Environment-variables defined on workflow level shall be effective for all actions referenced by the workflow. In case the same environment-variable is defined on workflow and action level (and the action is part of the workflow), the environment-variable on action level would take preference.
My use case regarding workflow level environment-variables is:
- there is one action to build a docker image
- there is a subsequent action to login to docker hub
- there is a subsequent action to push to docker hub
The docker image tag in step 1 and 3 is currently repeated because I can’t define a workflow level environment-variable like
DOCKER_IMAGE=”lotharschulz/hello-github-actions:$GITHUB_SHA”
Store environment
Store-env is a Github Action listed in the Marketplace that stores its environment in ~/.profile file. Unfortunately this is not a workaround for the use case described above.
Whenever I used store-env to store environment variables the subsequent step build “docker.build” failed with
/entrypoint.sh: export: line 7: test,: bad variable name e.g.
- https://github.com/lotharschulz/hello-github-actions/runs/119337611
- https://github.com/lotharschulz/hello-github-actions/runs/118746104
Whenever the step using store-env wasn’t present the exact same step “docker.build” was successful.
Parallel steps Cancellation
I tested a workflow that includes three parallel actions.
Actions benchmark, test, post gif on fail are defined to run in parallel; there is a subsequent step that needs actions test. If one of the parallel steps fails, I’d expected the remaining parallels step to finish. However, these steps get cancelled as in the screenshot above.
Exit code “neutral” 78
Github Actions can be configured to return the exit code 78 “neutral” in case an action fails. This exit code “indicates that the action terminated but did not fail”.
NEUTRALCODE=78
for cmd in "$@"; do
echo "Running '$cmd'..."
if sh -c "$cmd"; then
# no op
echo "Successfully ran '$cmd'"
else
exit_code=$?
echo "Failure running '$cmd', exited with $exit_code. Action will return $NEUTRALCODE 'neutral'."
exit $NEUTRALCODE
fi
done
In the screenshot below you see the GitHub Action benchmark “fail” with exit code 78 “neutral”.
That causes the cancellation of the other actions: test & post git on fail.
The respective log contains:
- Github Action benchmark:
- Github Action test:
Exit code 78 does not offer a workaround for the Canceling parallel steps situation.
Start up delays
I also noticed some times a couple of seconds delay between git push to origin and the first action in the workflow being started. This is just a feeling because I did not measure the delay.
Conclusion
GitHub actions let you automate almost everything on GitHub. I dove into a CI/CD use case to showcase that. The ability to run actions defined on your own or use open source actions that exist already offers a lot of options. Being able to define native global workflow level environment-variables would be an important step to use GitHub Actions in production environment once it is generally available.
Further reading
- GitHub Actions
- Creating a workflow with GitHub Actions
- GitHub Actions organisation
- GitHub Actions Marketplace
This post is inspired by Bas Peters’ talk GitHub Actions: Open Source Workflow Automation by Bas Peters in Vilnius 2019.
Thanks for reviewing this article: Bas Peters and Johannes Nicolai
comments on linkedin: