GitHub Actions: Testing, Building and Notifying
Tags: docker, kubernetes, devops, github
Published: 2020-06-30 15:17:47

In a previous article we discussed [**How to improve your CI/CD**](https://appfleet.com/blog/github-actions-how-to-improve-your-ci-cd-workflow/) workflow using GitHub Actions. Today we are going a step further. We will work with some additional actions to test our application, before submit the Docker image, and sending a notification to a Slack channel.

First at all, we need to create our GitHub action using the path **"our_repository_name"/[.github](https://github.com/pablosponce/docker-githubactions/tree/master/.github)/[workflows](https://github.com/pablosponce/docker-githubactions/tree/master/.github/workflows)/** and create our workflow file. For this tutorial we are going to deploy a Node.js app, so we will start by selecting the Node.js template to work as shown below.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/5w7u24cky2gys3ife2ks.png)

After clicking on *Set up this workflow* a new file **nodejs.yml** is created in our repository with the following content:

```
 name: Node.js CI

    on: [push]

    jobs:
      build:

        runs-on: ubuntu-latest

        strategy:
          matrix:
            node-version: [8.x, 10.x, 12.x]

        steps:
        - uses: actions/checkout@v2
        - name: Use Node.js ${{ matrix.node-version }}
          uses: actions/setup-node@v1
          with:
            node-version: ${{ matrix.node-version }}
        - run: npm install
        - run: npm run build --if-present
        - run: npm test
          env:
            CI: true

```

By default it is going to build three different versions using 8.x, 10.x and 12.x running on Ubuntu, and looking for our **package.json** on the root directory.  

We are going to keep the default path and will build the application using version 13.x along with installing Jest for testing purposes. Other lines will remain unchanged except for the name of the action `Node.js Test, Build and Notify`:

```
 name: Node.js Test, Build and Notify

    on: [push]

    jobs:
      build:

        runs-on: ubuntu-latest

        strategy:
          matrix:
            node-version: [13.x]

        steps:
        - uses: actions/checkout@v2
        - name: Use Node.js ${{ matrix.node-version }}
          uses: actions/setup-node@v1
          with:
            node-version: ${{ matrix.node-version }}
        - run: npm install jest --save-dev
        - run: npm run build --if-present
        - run: npm test
          env:
            CI: true
```

If you have your own Node.js app code, you will have to change the test line into scripts section for `"test": "jest"` on your **package.json**

In our package.json example, this is the final code:

```
{
      "name": "docker-githubactions",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "jest"
      },
      "author": "Pablo Ponce",
      "license": "ISC",
      "dependencies": {
        "express": "^4.17.1"
      }
    }
```

Next, we will have to create our test files. As per Jest requirements, we are going to create the test files using *filename.spec.js* and store them in **[src](https://github.com/pablosponce/docker-githubactions/tree/master/src)/[__test__](https://github.com/pablosponce/docker-githubactions/tree/master/src/__test__)/**

We are going to set a *silly check* for demo purposes evaluating if two numbers are equals. We named the file *test.spec.js* but the relevant point is the path (all tests files should be included on [src](https://github.com/pablosponce/docker-githubactions/tree/master/src)/[__test__/](https://github.com/pablosponce/docker-githubactions/tree/master/src/__test__))

```
  describe("Testing our nodeJS app", () => {
      it("Testing using Github Actions", () => {
        expect(2).toBe(2);
      });
    });
```

If the test is successful, we will add a new step into our action to build our own Docker image. We will need a Dockerfile defined in our root directory to build our application using Docker based on Node.js 13:

```
 FROM node:13

    EXPOSE 3000

    WORKDIR /usr/src/app

    COPY package.json package.json

    RUN npm install

    COPY --chown=node:node . .

    CMD [ "node", "index.js" ]
```

Although our Node.js app is an index.js file, you should adapt the Dockerfile with your own requirements:

```
 //Load express module with `require` directive
    var express = require('express')
    var app = express()

    //Define request response in root URL (/)
    app.get('/', function (req, res) {
      res.send('It works!')
    })

    //Launch listening server on port 3000
    app.listen(3000, function () {
      console.log('Keep working!')
    })
```

After that we can edit our **nodejs.yml** and add the Docker action made by [elgohr](https://github.com/elgohr/Publish-Docker-Github-Action). An important point is to set the conditional 'if' to guarantee the execution only if the previous step has been successful:

```
    - name: Publish Docker
          if: success()
          uses: elgohr/Publish-Docker-Github-Action@2.11
          with:
            # The name of the image you would like to push
            name: cloudblog/nodejs_githubactions
            # The login username for the registry
            username: ${{ secrets.DOCKERHUB_USER }}
            # The login password for the registry
            password: ${{ secrets.DOCKERHUB_PASS }}

```

> Note: You need to rename the image using the format **yourdockeruser/image_name_of_your_app**

If everything is working fine we should have a new repository on our Docker Hub with our container image:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/782h9vdcg4nivowba8wx.png)

Additionally we would need to send a notification to a Slack channel when the build is complete and has finished correctly. Same procedure as before, we need to include a new step to be executed only if the previous step has been completed without errors. 

For the Slack notification, we need to use incoming webhooks for the Slack app on our workspace. We also can override the default configuration from the webhook like the associated channel. 

Here we are using the default settings when we created the webhook but writing a specific message to be sent to our #general channel with the text *"A new version of your Node.js app has been built"*

```
- name: GitHub Action for Slack
          if: success()
          env:
           SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
          uses: Ilshidur/action-slack@1.6.2
          with:
           args: 'A new version of your Node.js app has been built'

```

After an execution this output is sent to our Slack channel #general:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/sb0mfijpcwz17izh817b.png)

This message will  be sent only when all previous steps are completed without errors. When the tests in our code are successfully passed we get something like:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/o7mblb2spemuvbmkpagq.png)

Including all configured tests passed as expected:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/zhklbkss0ckk3ks49r2t.png)

However please note that even if one test fails, the workflow stops and further actions will not been executed. If we modify our sample test to evaluate 3 vs 2, the test is going to fail and hence the following steps - Docker Build, Upload to Docker Hub and Notifying Slack channel - will not be executed.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/81u1wk6a64cuvl9ktw6v.png)

Here we can see how further steps are not executed after failing the test:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/pwlvzm8e16ym6opz0hyn.png)

The reason of the failed test is clear, we expected '2' but we are receiving '3' from our function:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/coiph0sosvdgcmjk8mk4.png)

Our final *nodejs.yml* file will look something similar like this:

```
name: Node.js Test, Build and Notify

    on: [push]

    jobs:
      build:

        runs-on: ubuntu-latest

        strategy:
          matrix:
            node-version: [13.x]

        steps:
        - uses: actions/checkout@v2
        - name: Use Node.js ${{ matrix.node-version }}
          uses: actions/setup-node@v1
          with:
            node-version: ${{ matrix.node-version }}
        - run: npm install jest --save-dev
        - run: npm run build --if-present
        - run: npm test
          env:
            CI: true

        - name: Publish Docker
          if: success()
          uses: elgohr/Publish-Docker-Github-Action@2.11
          with:
            # The name of the image you would like to push
            name: cloudblog/nodejs_githubactions
            # The login username for the registry
            username: ${{ secrets.DOCKERHUB_USER }}
            # The login password for the registry
            password: ${{ secrets.DOCKERHUB_PASS }}


        - name: GitHub Action for Slack
          if: success()
          env:
           SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
          uses: Ilshidur/action-slack@1.6.2
          with:
           args: 'A new version of your Node.js app has been built'
```

Similar actions can be done using other languages, and is hence not only related to Node.js. Testing your code (syntax or real output) prior to proceed with additional actions is a really good practice to maintain health of your CI/CD pipeline. Keep digging on the GitHub Marketplace to discover more steps to be included in your workflow.