Faster GitHub Actions with Build and Tag Action
It’s weird saying that I have a favourite GitHub Action, but I do, and that action is JasonEtco’s build-and-tag-action.
Why is it important?
One of the biggest complaints about using the Docker runtime for Javascript based GitHub Actions is that they’re slow. Real slow. You can convert them to Node based actions fairly easily, but that adds another barrier - you need to start committing your node_modules
folder as Actions doesn’t run npm ci
automatically for you.
Trying to get the best of both worlds (no committed node_modules
folder, but fast execution) I built a project named auto-compile-node which uses @vercel/ncc to build a single JS file containing an action and all of it’s dependencies whenever a release is tagged. This allowed me to use Docker for development, but still get the speed benefits whenever a release is tagged.
Jason took this one step further, generalising the build process to run npm run build
and integrating automatic retagging of major versions (similar to actions-tagger).
How does it work?
build-and-tag-action
is designed to run when a release
is published
or edited
. Your workflow should use actions/checkout
to clone the code for that tag before build-and-tag-action
runs your setup script. If you don't specify a setup
script build-and-tag-action
will use a default of npm ci && npm run build --if-present
. This installs all of your project’s dependencies (including development dependencies) and runs your npm build
script if you have one defined.
If you want to use build-and-tag-action
for building a JavaScript GitHub Action, your build
script will likely contain npx @vercel/ncc build
. This compiles your action and all it’s dependencies into a single file (the value of main
in your package.json
).
Once npm run build
has finished executing, build-and-tag-action
will create a new commit containing only action.yml
and your main
file and update the tag that triggered this workflow to point at that new commit. This means that the tag now points at a fully compiled JavaScript Action without any additional files (which reduces the time it takes to clone the action).
Finally, build-and-tag-action
will extract the major version from your tag - if you just tagged v1.8.2
it will extract v1
- and update the SHA that the v1
tag points to. This means that your consumers can depend on v1
and still get updates to the action.
build-and-tag-action
expects that your action.yml
contains runs.using: node12
by default. This prevents you using Docker for branch based development. If you’d like to use Docker for branches and Node for tags, set your build
script to npx @vercel/ncc build && npx convert-action
. convert-action
will automatically rewrite your action.yml
file to use the Node runtime.
Here’s an example package.json
that compiles your action and updates action.yml
in a single command:
json
{"name": "your-action-name","main": "dist/index.js","scripts": {"build": "npx @vercel/ncc build && npx convert-action"}}
All of the above assumes that you are creating a release manually or using the API with a Personal Access Token (PAT). If you’re using a workflow with actions that generate a release and want to use this action you can specify the tag_name
input when calling build-and-tag-action
:
yaml
- uses: fictional/releaser@v1 # Not a real action!id: releaser- uses: JasonEtco/build-and-tag-action@v1with:tag_name: $
This is required as releases created using the GITHUB_TOKEN
secret do not trigger workflow runs, so you need to use build-and-tag-action
as an additional step and pass tag_name
as an input. In this example the releaser
step returns a tag_name
output that we can use.
tag_name
is automatically populated for therelease
event, so we do not need to pass any inputs to the workflow. If thetag_name
input is provided, it will override the auto-detection
You can iterate on your actions and test them out using the Docker runtime on your main
branch and benefit from the speed increase when using the Node runtime for any tagged releases using build-and-tag-action
to run @vercel/ncc
and convert-action
.
In addition, build-and-tag-action
keeps your major version branch (e.g. v1
, v2
etc) up to date with the latest minor and patch versions of your actions, reducing the effort it takes for your consumers to keep up to date. I use it on all of my actions and recommend that you do too!