Faster GitHub Actions with Build and Tag Action

08 Jan 2021 in Tech

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:

"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:

- uses: fictional/releaser@v1 # Not a real action!
id: releaser
- uses: JasonEtco/build-and-tag-action@v1
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 the release event, so we do not need to pass any inputs to the workflow. If the tag_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!