Full Web App + CI/CD pipeline in a Hammer Way! 🔨🔥🛠
Tags: deployment, node, vue, pipeline
Published: 2019-04-19 13:18:32

---
title: Full Web App + CI/CD pipeline in a Hammer Way! 🔨🔥🛠
published: true
description: Let's build a full Web App project with pipeline in a hammer way!
tags: deployment, nodejs, vuejs, pipeline
---

VueJS app with NodeJS backend + CI/CD pipeline: Hammer way 🔨🛠

***

In this tutorial I will explain how to build a __simple__, full-scale Web App project with NodeJS backend + complete CI/CD pipeline.

I call it __"Hammer way"__ , because it doesn't use Docker or any architecture... It is not very fancy, however it works ¯\_(ツ)_/¯

In the end of this tutorial we will be able to have our application running on server, with fully automated pipeline which will build, test and deploy on every push to git repository!

Both projects are already available here:

Backend => [Here](https://github.com/NickitaX/dev_to_tutorial_backend)
Frontend => [And here](https://github.com/NickitaX/dev_to_tutorial_frontend)

(Feel free to submit PRs for improvements)

Are you ready?

Let’s go! 🔥

***
# Part One: Our NodeJS backend 💻
***

For the sake of simplicity, we will have a super minimalistic NodeJS backend which will do the only thing: serve a front end.
Project structure will look like this:

```
./backend
├── package-lock.json
├── package.json
├── circle.yml
├── scripts
│   └── deploy.sh
└── server.js
```

Let’s have a look at server.js:

```javascript
const express = require('express');

const path = require('path');

const http = require('http');

const app = express();

app.use(express.static('/home/deploy/frontend/'));

app.get('/', (req, res) =>  res.sendFile(path.join('/home/deploy/frontend/index.html')));

http.createServer(app).listen(8080);

```

Described above code uses express library which loads an __index.html__ file referenced in static folder and serves on port __8080__. Not a rocket science, but we need to start somewhere...

What about deploy.sh?

```bash
#!/usr/bin/env bash

ssh-keyscan -H "$1" >> ~/.ssh/known_hosts
ssh "deploy@$1" rm -rf ~/backend/*
scp -r ./server "deploy@$1:~/backend/"
scp -r ./package.json "deploy@$1:~/backend/"
scp -r ./package-lock.json "deploy@$1:~/backend/"
ssh $1 "cd /home/deploy/backend
                                   npm i
                                   forever stopall
                                   forever start server.js
                                   “
```

This shell script plays a role of automation deployment system.
 
Before we walk through the shell code, some server setup clarification:

On our server we will create __deploy__ user specifically for this purpose and generate a pair of SSH keys for our CI/CD pipeline (will go through it soon).

We will also install `forever js` which will run NodeJS process in the background.

There will be two folders in home directory: __frontend__ and __backend__. These will be locations where CI server will copy files to.

>For those who are not familiar with shell variables: that `$1` means we are going to pass server host name as a parameter. (It will be executed like: `bash deploy.sh my.awesome.website`, where `my.awesome.website` will be placed instead of `$1`).
In brief, this script removes old version of server files, copies new files from Git repository and restarts background run process of server.js.

But how do we integrate with CircleCI? Here is our magic circle.yml configuration file:

```yaml
version: 2
jobs:
  build:
    working_directory: ~/backend
    docker:
      - image: circleci/node:4.8.2
    steps:
      - checkout
      - restore_cache:
          key: dependency-cache-{{ checksum "package.json" }}
      - run:
          name: Install npm dependencies
          command: npm install
      - save_cache:
          key: dependency-cache-{{ checksum "package.json" }}
          paths:
            - node_modules
  test:
    docker:
      - image: circleci/node:4.8.2
    steps:
      - checkout
      - run:
          name: Test
          command: npm run test

  deploy:
    docker:
      - image: circleci/node:4.8.2
    steps:
      - checkout
      - run:
          name: Deploy
          command: bash ./scripts/deploy.sh my.awesome.website

workflows:
  version: 2
  build_and_test:
    jobs:
      - build
      - test:
          requires:
            - build
          filters:
            branches:
              only: master
      - deploy:
          requires:
            - build
            - test
          filters:
            branches:
              only: master
```

Configuration file above declares working directory where our pulled from Git repository will be located, test and deploy steps which will run test and execute shell script we discussed before to copy new files. 

And it also contains environment description such as node version installed on docker container.

Awesome! We finished with backend! Yahoo! 🎉

Now go and push your project to your Git repository!



***
# Part Two: Quick server configuration 🔑
***

As I promised, here is more on Linux server configuration:

We need to install NodeJS:

[=> Node JS](https://github.com/nodesource/distributions/blob/master/README.md)

After that we should install `forever.js` for background processing (there could be better alternatives, feel free to experiment):

[=> Forever JS](https://github.com/foreverjs/forever)

And lastly, a pair of SSH keys:

```bash
ssh-keygen -t rsa -C “amazing@dev.to"
```

You will need a private key soon to allow CircleCi perform SSH actions on the instance.


***
# Part Three: Front end! 😍
***

It is a time for a VueJS front-end!

The best way to start with VueJS is by using VueCLI. Once installed you can create a new application by running:

```bash
vue create frontend
```

(You can follow official doc: [here](https://cli.vuejs.org/guide/creating-a-project.html#vue-create))

It may take some time to generate a project, and in the end we will have similar structure:

```
./frontend
├── README.md
├── babel.config.js
├── node_modules
├── package-lock.json
├── package.json
├── public
└── src
```

>Don’t forget to run `npm install` to install all dependencies.

To test your awesome template website, run: `npm run serve`.

Can you see it? Amazing, it works! Good job!

Now, let’s add some magic… 🔮

Let’s create a `scripts` directory in `src` folder, and put `deploy.sh` file there which will look like this:

```bash
#!/usr/bin/env bash
ls -l
ssh-keyscan -H "$1" >> ~/.ssh/known_hosts
ssh "deploy@$1" "rm -rf ~/frontend/*"
scp -r ./dist/static "deploy@$1:~/frontend/"
scp ./dist/index.html "deploy@$1:~/frontend/"
scp ./dist/service-worker.js "deploy@$1:~/frontend/"
```

Looks similar, isn’t it?
This script will remove old front-end, and copy new files built by our CircleCi! 

And here our `circle.yml` file we will create in root of the project:

```yaml
version: 2
jobs:
  deploy:
    docker:
      - image: circleci/node:latest
    steps:
      - checkout
      - run:
          name: Install dependencies
          command: npm i
      - run:
          name: Package
          command: npm run build
      - run:
          name: Deploy
          command: bash ./scripts/deploy.sh my.awesome.website 

workflows:
  version: 2
  build_and_deploy:
    jobs:
      - deploy:
          filters:
            branches:
              only: master

```

As you can spot, it looks almost the same as a previous one for the server. 
However this version builds our frontend project first, and after - performs deployment.

Final project structure will look like this (with our new files):

```
./frontend
├── README.md
├── babel.config.js
├── node_modules
├── circle.yml
├── scripts
│   └── deploy.sh
├── package-lock.json
├── package.json
├── public
└── src

```

We did it! Yahoo!

Now the last piece: CI configuration (CircleCi in this case)


***
# Part Four: CircleCi 🔄
***

Once you login with BitBucket to [CircleCi](https://circleci.com), add your projects by following them:

![Follow project](https://thepracticaldev.s3.amazonaws.com/i/sgexwlzepavaaho7d282.png)

Do you still remember SSH key we generated on our server? Now we need to use it!

Go to settings of each of two projects, navigate to `SSH Permissions` tab and copy/paste generated private key there:

![SSH keys](https://thepracticaldev.s3.amazonaws.com/i/6wjr8v25u43npn0olm0d.png)

And now try to push any change to either frontend or backend project => it will trigger a build and your Web Application will be automatically updated:

![First build](https://thepracticaldev.s3.amazonaws.com/i/ytocgd2gfpwbt9x3u5hg.png)

We made it! 🎉🎉🎉

![Victory](https://media.giphy.com/media/lnlAifQdenMxW/giphy.gif)

***
# Conclusion 🍕
***

This kind of pipeline would be suitable for a small personal project, or just to get familiar with deployment idea. Sure thing: almost every part of this tutorial can be upgraded and enhanced :) If you have any ideas or spotted a bug (I'm sure there are few of them - don't hesitate to rase PRs!)