Earlier this year, I joined a team that had been tasked with working on some exciting green-field projects to support better flow of data within our organization. Our team took the opportunity to step into the present and adopt some more modern and feature-rich tools, such as GitHub for remote code hosting and GitHub Actions to support our software development workflows. (We were previously using Bitbucket and a self-managed Jenkins instance for continuous integration and delivery.)
This post shares a few things I learned along the way as we leaned into GitHub and GitHub Actions.
- More than a continuous integration tool
- Working with workflows
- Code reuse
More than a continuous integration tool
GitHub Actions has been a live product for a few years now, but this year was the first time I used it in a project. Based on many of the blog and video headlines about GitHub Actions I had seen since its launch, I thought GitHub Actions was simply a newer continuous integration and delivery (CI/CD) tool that had tight integration with the GitHub platform. While this is accurate, it isn’t the full story. GitHub Actions is not just a CI/CD tool. It can do so much more.
GitHub Actions is an event-driven workflow automation product for supporting the software development process in the GitHub environment.
The examples of products most similar to GitHub Actions that readily come to mind are Zapier and IFTTT, but with the events that trigger an automated workflow originating from the GitHub platform. GitHub events are published when we perform activities such as forking a repository, pushing code to a remote branch, or opening a pull request. GitHub Action workflows are configured to listen for specific events and do something. That something could be anything, programmer’s choice.
Working with workflows
In the context of GitHub Actions, a workflow is an automated process you configure in a GitHub repository. Workflows are configured using YAML in files stored in a
.github/workflows/ directory. A workflow is comprised of one or more “actions” that run in response to a GitHub event. Here’s an example of a workflow that builds, tests, and deploys some code when it is merged/pushed to the main branch.
This workflow file would be located at
.github/workflows/release.yaml. It starts out with a name for the workflow, “Release,” followed by the event configuration that will cause the configured actions to execute (on code push to the main branch). Below that, the actions to execute are grouped in a job called “release”. This job (the sequence of actions/steps) will execute on an Ubuntu instance. First, an action called checkout (made available by GitHub on the GitHub Actions Marketplace) is used to checkout the repository, then we build, test, and deploy the code.
It’s worth noting that in workflows with multiple jobs, jobs are executed concurrently. However, some amount of sequence can be established by configuring dependencies among jobs. You can also share information among jobs using job output variables.
As you write more workflows across your repositories, you’ll quickly notice that there are common tasks you want to perform in your workflows, or that there is some code duplication. Naturally, your engineering instinct will nudge you to investigate ways to write reusable code for your workflows. There are two main ways to reuse GitHub Actions code: composite actions, and reusable workflows.
actions/checkout@v3 as shown in the example workflow above is an example of a composite action. GitHub saw that checking out a repository was something many of us would be doing, so they created a composite action called
checkout for us to use (and reuse) for that purpose. In this case, we’re using the 3rd version of the checkout action.
A composite action is a packaged series of tasks to execute that can be reused in GitHub Action workflows.
When creating composite actions, you need to consider the security risk of script injection. And when using third-party composite actions, you need to consider the security risk that you may be using malicious code. For this reason, you should get your composite actions from trusted sources, such as verified actions on the GitHub Actions Marketplace. You also have the ability to constrain what composite actions and reusable workflows are allowable for use in your repositories through your repository’s settings for added security and stability.
Sometimes, you will write workflows that are so similar, you will begin to wonder if you could have just written it once, parametrize it, and reuse it in other GitHub Action workflows. The answer is yes! Yes, you can. For instance, you may have a release workflow that you want to run for different environments (e.g. dev, stage, prod), each of which have different secrets and input variables such as their environment names and cloud account IDs. The GitHub Actions documentation goes in depth about how to create reusable workflows and some of the limitations on them. Reusable workflows are structurally very similar to standard workflows, with a few added configuration items such as input variables and secrets.
GitHub Actions is an event-driven workflow automation product for supporting the software development process in the GitHub environment. It is more than just a tool for continuous integration and delivery. The concepts of workflows and actions are at the heart of the GitHub Actions product. Workflows are configured using YAML to perform a specific set of actions (tasks) in response to an event originating from the GitHub platform. As you write more workflows, code deduplication and reusability will become concerns, and composite actions as well as reusable workflows are excellent tools to address those concerns.