A guide to GitHub Actions using Node.js for Git workflow automation
Tags: github, actions, devops, gitops
Published: 2019-03-31 18:09:26

---
title: A guide to GitHub Actions using Node.js for Git workflow automation
published: true
description: Read how to write Github Actions using Node.js instead of shell scripts, including tools used and tips on Github Docker container and runtime environment.
tags: #github #actions #devops #gitops
canonical_url: https://datree.io/blog/git-workflow-automation-github-actions-node-js/
cover_image: https://datree.io/wp-content/uploads/2019/03/post10-1648x824.jpg
---

## What is Github Actions?

GitHub Actions, a feature announced in October 2018 during GitHub Universe, generated immense hype under the apt positioning as the “swiss army knife” of git workflow automation.

Github Actions allows developers to perform tasks automatically in a Github workflow, such as pushing commits to a repository, deploying a release to staging, running tests, removing feature flags, and so on, by way of a simple text file.

[Chewy.com](http://chewy.com/), for example, demoed an action that checks if a [Jira ticket number is included](https://vimeo.com/295656803) in every pull request name among other things, to ensure the code being deployed to production is compliant with their policies and best practices.

### “With GitHub Actions, you can automate your workflow from idea to production.”
– [GitHub actions page](https://github.com/features/actions)

## Write Github Actions in Node.js

I started tinkering with the feature as soon as I could get access to the private beta. I noticed that most Actions are written in shell script. GitHub itself promoted writing actions in [shell script for simple actions](https://developer.github.com/actions/creating-github-actions/creating-a-new-action/#using-shell-scripts-to-create-actions). While I understand the motivation (so you can quickly and easily start writing Actions), I feel that shell scripts are limited in terms of writing full-fledged software.

Since I generally work in Node.js, I decided to ‘accept the challenge’ and write my first Action in Node. But, after struggling with the specifics of running Node.js actions and understanding the differences between a simple container and the GitHub execution environment, I decided to write this tutorial in the hope that it will help others who prefer to write an action in JavaScript.

## Environment setup

The basic setup is straightforward – start by following [GitHub’s tutorial](https://developer.github.com/actions/creating-github-actions/creating-a-new-action/#creating-a-new-github-action) all the way to the _entrypoint.sh_ section. The main difference up to this point is in the chosen docker image. I suggest using the [alpine image](https://github.com/nodejs/docker-node/tree/90043cdde5057865b94fec447ce193fb46b69e18#nodealpine), it’s very lightweight compared to the regular [node image](https://github.com/nodejs/docker-node/tree/90043cdde5057865b94fec447ce193fb46b69e18#nodealpine). In any case, I suggest using an LTS variant, currently being node 10.

A very important tool, one that helped reduce the development cycle from 5 minutes per iteration to mere seconds is [Act](https://github.com/nektos/act), a zero-config, easy to use tool to run actions locally. It doesn’t fully replicate the environment (for obvious reasons, it doesn’t provide a GitHub token, more on that later) but it’s close enough to speed up the development process and test your action locally.

At the end of this step, you should have a Dockerfile that looks like this:
{% gist https://gist.github.com/romanlab/04311e00211941323ba3a58c87bb56cc %}

## Node-specific setup

This is the basic _entrypoint.sh_ file that will work for a Node.js action:
{% gist https://gist.github.com/romanlab/180777f37429d9f9f5e467db56505539 %}

I chose _npm ci_ because it’s the easiest way to make sure you always get the same versions of the packages you want to install. It requires you to have _package.json_ and _package-lock.json_ in your project – but that’s a best practice anyway.

The installation script is in the entry point file and NOT in the Dockerfile (as is usual in classic container use cases) because it makes it much easier to use an npm token and install private packages. All you need to do is add an NPM_TOKEN secret and use it in the entry point file (above _npm ci_) by adding:

_npm config set //registry.npmjs.org/:_authToken=$NPM_TOKEN_
_node script.js $*_ runs the script and passes the action args as arguments to the script.

## Node script tips and basic structure

Over the years, I’ve developed a preferred structure for a node executable (CLI) script. I will share it here but for the purpose of this tutorial, this part is completely optional and at this stage, you’re more than ready to develop your own action in Node.js.

The script looks like this:
{% gist https://gist.github.com/romanlab/162435796e72983021bc4cb78bd58a77 %}

The important section is at the bottom: _if (require.main === module)_

It checks if the file was imported/required or if it’s the entrypoint into the program. This allows reusing the same module both programmatically and as a CLI tool.

If this is the entrypoint, I would then parse the command line arguments (using [commander](https://github.com/tj/commander.js/)) passed in from _entrypoint.sh_. The arguments were injected into _entrypoint.sh_ by GitHub from the workflow file through the container (more on that later).

I then invoke the main function. Since it’s an async function, I handle its return value with a then clause and handle failure with a catch clause.

It’s also useful to read the event, provided by GitHub, and use it in the script:

_const event = JSON.parse(fs.readFileSync('/github/workflow/event.json', 'utf8'))_

## Secrets, Args, and the execution environment

The Actions environment takes some getting used to. Although GitHub provides great tutorials on all things workflow related, I wanted to mention:

* GitHub provides many [environment variables](https://developer.github.com/actions/creating-github-actions/accessing-the-runtime-environment/#environment-variables) inside the container running the Action, but most of the information can also be retrieved from the event file (see section above).
* Others are defined in the workflow file, while some are provided as part of the secrets mechanism in GitHub.

[Secrets](https://developer.github.com/actions/creating-workflows/storing-secrets/#github-token-secret) are pretty straightforward, you define them in the repo settings tab and then they’re exposed as environment variables inside the container.

Defining the secret in the repo settings tab:
![secrets tab](http://datree.staging.wpengine.com/wp-content/uploads/2019/02/p1.png)

Using the secret in your workflow file:
![secrets code](http://datree.staging.wpengine.com/wp-content/uploads/2019/02/p2.png)

The only exception is the GitHub token, which you don’t need to define in the settings. The token is only exposed in the workflow file and GitHub will provide the token itself with these [permissions](https://developer.github.com/actions/creating-workflows/storing-secrets/#github-token-secret).

Another important item to note is the mounted folder GitHub provides. It’s mounted under /github and provides a couple of useful things:
- the event under _/github/workflow/event.json_ and
- the repo where the action runs under _/github/workspace/REPO_NAME_

More information on the mechanics of the mount can be found [here](https://developer.github.com/actions/creating-github-actions/creating-a-docker-container/#workdir).

This tutorial provides a good starting point for anyone who wants to create their first Node.js action. My action can be found [here](https://github.com/marketplace/actions/validate-license-action) and the code for it [here](https://github.com/datreeio/validate-license-action).

If you’re interested in learning more about how you can use Github Actions to automate git workflows, check out this [webinar](https://pages.datree.io/building-a-dev-pipeline-using-github-actions-node.js-and-aws-ecs-fargate) to watch how Shimon Tolts, Datree.io Co-founder, **"built a CI/CD dev pipeline with Github Actions, Node.js, Docker, and AWS Fargate"**.

If you have any questions, corrections, or suggestions please comment below or contact me directly.