Customer: Use Case (5: Implementing Simple Jenkins Continuous Integration)
Tags: jenkins, ci, node
Published: 2023-11-30 03:48:24

## Prerequisites
1. Installed Node version of: v20.10.0.
2. Installed and configured Jenkins. If you want to know how, you can check my post on how to do it in [How to: SonarQube Code Coverage in Jenkins with Maven Project](https://dev.to/amalja0/how-to-sonarqube-code-coverage-in-jenkins-with-maven-project-3bb6), you can leave the steps related to the SonarQube.
3. Empty `Jenkinsfile` (assuming you make the project skeleton as I did).

## Before doing this tutorial
1. Make sure you don't run any services (ExpressJS service, NestJS service, and Front-end service) on your local.

## Configuring Jenkins for Node project
1. To configure Jenkins for Node, go to your Jenkins UI. Mine is at `http://localhost:8085`.
2. From `Dashboard`, go to `Manage Jenkins` which you can find on left navigation bar.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1sea27bfurinx8pj6kmo.png)
3. Choose `Plugins`.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yh55pgbcior14kpqiimj.png)
4. Go to `Available plugins` section and type `NodeJS Plugin`. I've already installed the plugin, so I cannot show you the step, but this is the plugin:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/truyv6l893f46fn7kepd.png)
5. Back to `Manage Jenkins`.
6. Choose `Tools`.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fz6gvsj8g0uzm9gudszv.png)
7. Scroll until you see `NodeJS installations`. Don't mind the `edited`, because I already have configured mine.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/crgof5bfkcmot8i72awx.png)
8. Choose `Add NojeJS`. You can fill the field like mine. I preferred to use my locally installed Node instead of download it from Jenkins environment:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h03jswakgaozjeifu38l.png)
9. Click `Save`.
10. (OPTOPNAL) Check if your NodeJS is correctly configured at Jenkins. Choose `New Item` located above `Manage Jenkins`.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/abemzmiwul0s1tvm5lhn.png)
11. Enter a name something like `test-node-configuration`. Choose `Pipeline project`. Then click `OK`.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3hskydemkn2n646bsfiz.png)
12. In `Pipeline` section, add below script and click `Save`.
    ```
    pipeline {
        agent any
        tools {
            nodejs 'NodeJS'
        }
        stages {
            stage('Check NPM Version') {
                steps {
                    bat 'npm version'
                }
            }
        }
    }
    ```
    ![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1tuc0pbtfjojpvuypfyt.png)
13. You will get redirected into you newly created project. Click `Build Now`. Then you will see you project being built.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jim8nqhyqf29e1q4pbiv.png)
15. When it's done, click on `#1`.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p9gxy9zs55kzrh787v1a.png)
16. Choose `Console Output`. And you will see some logs. If your Node configured correctly, you will see this on your console output:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wf71qgeekjl9x2pvo0ti.png)

## Upload .env files from every service to Jenkins
Since we are depending on `.env` file for every service, we need to upload it into Jenkins server.
1. Go to `Manage Jenkins`.
2. In `Security` section, choose `Credentials`.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hkq3ibh5va4uqa4mr5cp.png)
3. Click `System` on `Stores scoped to Jenkins` section.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x0lzm3b1du0pwy8hlg5b.png)
4. Choose `Global credentials (unrestricted)`.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ixebrk8n2qouqwjzmiyv.png)
5. Click on `Add Credentials`.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cu8mgtqzey4zv8i75hjw.png)

6. Click dropdown in `Kind` section. Choose `Secret File`.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t6f57mqtnmq7oyc9zkma.png)
7. Click at `Choose File` (mine is Pilih File).
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/be52c42vtd8ichuwywp2.png)
8. For first `.env`, choose from `express-service`.
9. Give it some ID so you won't forget what this is `.env` for. Mine is: `customer-express-env`. Don't mind the warning, because I already have it configured.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rz74j3kb5tdbt3iv4g94.png)
10. Click `Create`.
12. Do the same for `.env` in `nest-service` service and `front-end-service`. I ID'd the `nest-service` with `customer-nest-env` and for `front-end-service` I set the ID as `customer-react-env`.

## Writing Jenkinsfile
This is actually very exciting for me because it is the first time I do CI with Jenkins by writing the Jenkinsfile first. I recommend you to write it in an IDE like Visual Studio Code, and if you use VS Code, I recommend you to install an extension called [JenkinsFile Support](https://marketplace.visualstudio.com/items?itemName=ivory-lab.jenkinsfile-support).

### Defining the pipeline
First we are going to create the Jenkinsfile pipeline.
```
pipeline{
}
```
### Defining agent
Since I don't know much about Jenkins, I define the agent as `any`.
```
agent any
```
### Defining secret environment
Since we need `.env` to run all service, we have to provide the credential IDs for all `.env` that we already upload on Jenkins Server.
```
environment {
    SECRET_FILE_EXPRESS = credentials("customer-express-env")
    SECRET_FILE_NEST = credentials("customer-nest-env")
    SECRET_FILE_REACT = credentials("customer-react-env")
}
```
### Defining tool
We have to define the tools to build the project. Which is NodeJS. 
```
tools {
    nodejs "NodeJS" // change the NodeJS with the name you give when you configuring tools of NodeJS in Jenkins Server
}
```
### Defining stages
We are going to have few stages.
1. Creating `.env` file for every services.
    ```
        stage("Create ExpressJs Service ENV"){
            steps{
              dir("express-service") {
                script {
                  withCredentials([file(credentialsId: "customer-express-env", variable: "SECRET_FILE_EXPRESS")]) {
                    writeFile file: '.env', text: readFile(file: "${SECRET_FILE_EXPRESS}")
                  }
                }
              }
            }
        }
        stage("Create NestJs Service ENV"){
          steps{
            dir("nest-service") {
              script {
                withCredentials([file(credentialsId: "customer-nest-env", variable: "SECRET_FILE_NEST")]) {
                  writeFile file: '.env', text: readFile(file: "${SECRET_FILE_NEST}")
                }
              }
            }
          }
        }
        stage("Create React Service ENV"){
          steps{
            dir("front-end-service") {
              script {
                withCredentials([file(credentialsId: "customer-react-env", variable: "SECRET_FILE_REACT")]) {
                  writeFile file: '.env', text: readFile(file: "${SECRET_FILE_REACT}")
                }
              }
            }
          }
        }
    ```
2. Running all service in parallel because if we defined it in sequence, Jenkins build will stuck on what ever run first, so this is how:
    ```
        stage("Build ExpressJs and NestJs Service") {
          steps {
            parallel (
              "run express" : {
                dir("express-service") {
                  bat "npm install"
                  bat "node -r dotenv/config index.js"
                  bat "node -r dotenv/config src/configs/db.config.js"
                  bat "node index.js"
                }
              },
              "run nest": {
                dir("nest-service") {
                  bat "npm install"
                  bat "npm run start"
                }
              },
              "run react": {
                dir("front-end-service") {
                  bat "npm install"
                  bat "npm run dev"
                }
              }
            )
          }
        }
    ```

## Final look of Jenkinsfile
This is the final look of our `Jenkinsfile`.
```
pipeline {
  agent any
  environment {
    SECRET_FILE_EXPRESS = credentials("customer-express-env")
    SECRET_FILE_NEST = credentials("customer-nest-env")
    SECRET_FILE_REACT = credentials("customer-react-env")
  }
  tools {
    nodejs "NodeJS"
  }
  stages {
    stage("Create ExpressJs Service ENV"){
        steps{
          dir("express-service") {
            script {
              withCredentials([file(credentialsId: "customer-express-env", variable: "SECRET_FILE_EXPRESS")]) {
                writeFile file: '.env', text: readFile(file: "${SECRET_FILE_EXPRESS}")
              }
            }
          }
        }
    }
    stage("Create NestJs Service ENV"){
      steps{
        dir("nest-service") {
          script {
            withCredentials([file(credentialsId: "customer-nest-env", variable: "SECRET_FILE_NEST")]) {
              writeFile file: '.env', text: readFile(file: "${SECRET_FILE_NEST}")
            }
          }
        }
      }
    }
    stage("Create React Service ENV"){
      steps{
        dir("front-end-service") {
          script {
            withCredentials([file(credentialsId: "customer-react-env", variable: "SECRET_FILE_REACT")]) {
              writeFile file: '.env', text: readFile(file: "${SECRET_FILE_REACT}")
            }
          }
        }
      }
    }
    stage("Build ExpressJs and NestJs Service") {
      steps {
        parallel (
          "run express" : {
            dir("express-service") {
              bat "npm install"
              bat "node -r dotenv/config index.js"
              bat "node -r dotenv/config src/configs/db.config.js"
              bat "node index.js"
            }
          },
          "run nest": {
            dir("nest-service") {
              bat "npm install"
              bat "npm run start"
            }
          },
          "run react": {
            dir("front-end-service") {
              bat "npm install"
              bat "npm run dev"
            }
          }
        )
      }
    }
  }
}
```
## Build Jenkins Project.
1. Push the change you created in Jenkinsfile to your Git repository.
2. Choose `New Item` at Jenkins Dashboard.
3. Enter the name for your Jenkins project. choose Pipeline. Click `OK`.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ag4vnlds7loy970sqiql.png)
4. Once you got redirected to your project configuration, go to `Pipeline` section.
5. In Definition dropdown, choose `Pipeline script from SCM`.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pldk9cjvf6civ5szac02.png)
6. In SCM dropdown, choose `Git`.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iqvhn6dj6ixmzs65pq7y.png)
7. Enter your repository url. The one that you use to clone your repository. I don't set any credential since my repository is public.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7nwgmurn9gye8nyuwvhu.png)
8. Choose the branch you want to build, mine is `main`.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0zn7o7kpme5br6ldyf6h.png)
9. I don't change anything at `Script Path` section since my Jenkinsfile name is `Jenkinsfile`. Please mind that it is case sensitive.
10. Click `Save`.
11. You will be redirected to your project. Choose `Build Now`.
12. If things are fine you will mostlikely see this:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2xgunkmykhxqyu8aaj96.png)
13. Since we are running our project, Jenkins will keep on going on build stage. To see if we done it right, go to your build history. Click on the latest build, which is `#1`.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oijbjdacecr24cbhmijl.png)
14. Choose `Console Output`. Carefuly read the output to see if our services are running. If it does, try to go to front-end service address (still using the local address).
15. You will see your own interface even when you're not running it in your local because it is being run by Jenkins Server.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r4qyktq0liyogv356ba3.png)