When using Github Actions, Github supports skipping workflow runs by adding
[no ci] to your Git commits.
Conversely, to help reduce your Github bill, this post shares a way to make workflow jobs opt-in by adding
[ci] to your Git commit.
Github Actions can get expensive
Github Actions are awesome and allow automating software workflows to streamline development. At Hub3, we use Github Actions to automate the below jobs for our workflow named
ci (short for continuous integration):
lint: check the app’s code for consistent style and best practices
test: run automated tests to verify that the app’s working correctly
deploy: push the app’s code to the live production server
By default (when using
on: push), the
test jobs are automatically run for each new Git commit. This is uber convenient and allows our developers to receive fast feedback about their code’s style and functionality. Here’s what that looks like at the bottom of a pull request:
Sadly, Github Actions are not free for private repositories, and the cost is based on how long your jobs take.
As your project grows, your jobs will likely take longer due to more code to lint, more tests to run, etc. This trend compounds as you add developers to the project.
Eventually you might ask: How can we use Github Actions more efficiently and reduce our Github bill? Make jobs opt-in instead?
Conditionally running Github Actions
Well, I asked that question!
When working on a large feature, it’s rare to want to run Github Actions for EVERY commit. For instance, some commits act as checkpoints, others as quick fixes, etc. In those cases, it’s often premature to run the
lint jobs because the code is in a WIP state.
To run Github Actions only when we want to, we add the
[ci] flag to our Git commits. For example, a pull request’s WIP commits might look like this:
* 2015073 - add payment migrations * a778af1 - add payment controllers * beecca1 - add payment model and controller specs [ci] * 3c46eca - add simple payment frontend * 216f3c1 - ui/ux improvements * 78165cd - add feature specs [ci] * a7a5f37 - address feedback * e0fec97 - address more feedback * 1b7442e - fix linting issues [ci]
In that example, we only run our
ci workflow 3 times instead of 9, saving 66% on Github Actions costs for that pull request.
To add support for the
[ci] flag, we added the
if: statement under each of our workflow’s jobs:
# .github/workflows/ci.yml lint: if: "contains(toJSON(github.event.commits.*.message), '[ci]') || github.ref == 'refs/heads/main'" ... test: if: "contains(toJSON(github.event.commits.*.message), '[ci]') || github.ref == 'refs/heads/main'" ...
That’s it! Feel free to rename the
[ci] flag to whatever you want. Also, notice that the jobs will always run if the branch is
main, which is usually what you want.
Alternatively, you could introduce
[ci-lint] flags for more granular control over when jobs run. At Hub3, given the speed of our
lint job and the overhead of two flags to remember, we didn’t.
If you find a simpler way to accomplish opt-in Github Actions, shoot me a note.