CI/CD in Plesk: Deploy Node.js, React, Angular, Vue & Co. automatically

With Plesk CI/CD, you can automatically deploy Node.js, React, Angular, Vue and other technologies to the server. In this article, I’ll show you how Plesk CI/CD can be used to automate deployment processes and reduce development time.

When developing fullstack applications with Node.js, Deno and frontend frameworks like Vue, React or Angular, the deyploment process, i.e. the step in which the developed application is put live, is always similar. Doing this manually takes a lot of time and is also very error prone when done manually. And this is exactly where CI/CD comes into play.

What to expect in this tutorial:
We build a complete Plesk CI/CD pipeline. You commit local changes to your develop branch in the remote repository and create a pull request (PR) to your main branch. Your app is automatically tested and your source code is formatted (linting) and if successful everything is pushed to your main branch. The frontend is then automatically built. Plesk detects these changes and automatically deploys all changes to your users. You can also use this workflow for static websites.

Plesk CI/CD pipeline visualized
Plesk CI/CD pipeline visualized

This diagram is meant to visualize how the complete Plesk CI/CD pipeline looks like including all steps. Take a moment and have a close look at the individual steps.

What is CI/CD?

CI/CD stands for Continuous Integration/Continous Delivery and describes a method of continuous integration and deployment of changes to a software. This enables developers to iterate quickly and efficiently without relying on manual processes. As part of a CI/CD pipeline, various processes are automated, such as merging code changes, compiling and testing the application, and automatically deploying to servers.

Plesk CI/CD is an integrated solution on the Plesk server management platform that enables developers to seamlessly deploy their applications to the server. It can use different technologies such as Node.js, React, Angular, Vue and more. Plesk CI/CD supports popular versioning tools such as Git, Subversion and Bitbucket. It also provides a user-friendly interface for developers to set up and configure their CI/CD pipeline.

With Plesk CI/CD, developers can save time and effort by automating and accelerating their deployment processes. As a result, development time is reduced and software can be deployed faster and more efficiently.

Prerequisites/Assumptions

At the beginning, I make a few assumptions about the app that the pipeline will be developed according to. Of course, it doesn’t matter if you have other assumptions, you will have to adjust your Plesk CI/CD pipeline in a few places.

  • Backend and frontend are in separate repositories (preferably GitHub, as I use GitHub Actions here).
  • The frontend is created with a JS framework (e.g. Vue, React or Angular). Accordingly, npm or yarn is used.
  • The backend is a Node.js application
  • The Plesk Git extension must be installed in your Plesk (by clicking on Extensions > “Git”).

GitHub Actions: Create CI/CD Workflows

With GitHub Actions, we can create different workflows, each of which takes care of a specific task in our CI/CD pipeline. You create a new workflow as follows.

GitHub Actions: Create CI/CD Workflow
GitHub Actions: Create CI/CD Workflow

You get this editor that provides a file in YAML format.

GitHub Actions: Node.js workflow editor
GitHub Actions: Node.js workflow editor

Now here we create different YAML files for each step in our pipeline.

Run unit tests

If you have created unit tests for your code, e.g. with Mocha or supertest, you can use the following YAML file. If your code does not contain any tests, I can only strongly recommend you to create some. In another article I already showed how you can use ChatGPT to generate unit tests or Postman to test your Rest API.

name: Execute Unit Tests

on:
  push:
    branches: [ "develop" ]
  pull_request:
    branches: [ "main" ]

jobs:
  test:

    runs-on: ubuntu-latest

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

    steps:
    - uses: actions/checkout@v3
    
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v3
      with:
        node-version: ${{ matrix.node-version }}
        cache: 'npm'
        
    - name: Install dependencies and run tests
      run: |
        npm install
        npm run test
      env:
        CI: true # set environment variable to run tests in CI mode

I have named this file testing.yml. It is executed when a commit arrives on the develop branch or a pull request is created on the main branch. It first loads the current code from the repo, then installs all dependencies and then runs npm run test. Of course, your package.json must also have the appropriate script in it. This is what the section in package.json looks like if you use Mocha as your test framework and your tests are in the test/ folder:

{
  [...]
  "scripts": {
    [...]
    "test": "mocha test/**/*.test.js",
    [...]
  },
  [...]
}

Run Linting/Formatting

I can also only strongly advise you to set up a linter or formatter for your project. This automatically checks and corrects the code formatting. Especially when working with multiple developers, this is a great advantage to ensure code quality. The YAML file for GitHub Actions can look like this:

name: Execute Linting
on:
  push:
    branches: [ "develop" ]
  pull_request:
    branches: [ "main" ]

jobs:
  test:

    runs-on: ubuntu-latest

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

    steps:
    - uses: actions/checkout@v3
    
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v3
      with:
        node-version: ${{ matrix.node-version }}
        cache: 'npm'
        
    - name: Install dependencies and lint code
      run: |
        npm install
        npm run lint
      env:
        CI: true # set environment variable to run tests in CI mode

And in the package.json again the appropriate script must be deposited. Here as an example when using eslint.

{
  [...]
  "scripts": {
    [...]
    "lint": "./node_modules/.bin/eslint .",
    [...]
  },
  [...]
}

Build frontend

When we develop locally we always start a development server on our machine. For the live run (production) we have to run a command like npm run build or similar. Now we want to automate this process.

The following template is used to run multiple jobs. The dependencies are installed, the code is linked again and the frontend is built. Optionally, you can add the execution of unit tests here again.

In the last step we push the complete build to the branch build of the same repository. You will probably need to adjust the highlighted line. Here you specify the name of the folder where your frontend was built. For Vue.js this is the dist/ folder by default. For React this is usually the public/APPNAME/ folder. However, the path can also be customized via the config of the respective JS framework.

name: Build Application

on:
  push:
    branches: [ "main" ]

jobs:
  test-and-build:

    runs-on: ubuntu-latest

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

    steps:
    - uses: actions/checkout@v3
    
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v3
      with:
        node-version: ${{ matrix.node-version }}
        cache: 'npm'
        
    - name: Install dependencies
      run: |
        npm ci
        
    - name: Lint code
      run: |
        npm run lint
      env:
        CI: true # set environment variable to run tests in CI mode
        
    - name: Build Frontend
      run: npm run build
      env:
       CI: false
    
    - name: Push to build branch
      uses: s0/git-publish-subdir-action@develop
      env:
        REPO: self
        BRANCH: build # The branch name where you want to push the assets
        FOLDER: ./dist # The directory where your assets are generated
        GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} # GitHub will automatically add this - you don't need to bother getting a token
        MESSAGE: "Build: ({sha}) {msg}" # The commit message

As soon as you save the workflow file, the job is automatically executed and your frontend is built and pushed to the build branch. This can take 1-2 minutes depending on the frontend.

Run GitHub Workflow
Run GitHub Workflow

Set up Git repository in Plesk

The next step is to set up our two Git repositories in Plesk. We set up a different deployment for the frontend and the backend.

Git Repositories for Plesk CI/CD Workflow
Git Repositories for Plesk CI/CD Workflow

Backend

Select the following settings for the backend repository:

  • Use the SSH repository URL of your backend (git@github.com:USERNAME/REPO.git)
  • Branch: main
  • Provisioning mode: automatic
  • Server path: /httpdocs
  • Provisioning actions (to delete unneeded files directly):
    • touch tmp/restart.txt
    • rm -R .github/
    • rm .env.template
    • rm -R test/
    • rm README.md
    • rm .gitignore
    • rm .eslintrc.js

For everything you need to know about hosting a Node application, check out my in-depth post on Node.js app hosting with Plesk.

Frontend

The second repo instance in Plesk is for the frontend only. Select the following settings here:

  • Use the SSH repository URL of your frontend (git@github.com:USERNAME/REPO.git)
  • Branch: build (if this does not exist yet, create it, we need it later)
  • Server path: /httpdocs/public
  • Provisioning mode: automatic

We do the display of the frontend via our backend. To do this, we need to add the following lines to the index.js of the Node.js app:

// handle production
if (process.env.NODE_ENV === "production") {
  // static folder
  app.use(express.static(__dirname + "/public"));

  // handle SPA
  app.get(/.*/, (req, res) => res.sendFile(__dirname + "/public/index.html"));
}

I have a Node.js backend with express web server. If you use other technologies, you can rewrite it accordingly.

Up to this point, our files are deployed when we manually execute the pull in Plesk. So when we commit locally and push it to our remote repository we would always have to log into Plesk and click the “Pull Now” button. We configure these automations in the next step.

Plesk Git SSH Key Set Up in GitHub

But before that, we need to allow Plesk to access our private GitHub repository. To do this, we need to copy the public SSH key created by Plesk.

Copy Plesk SSH key for GitHub
Copy Plesk SSH key for GitHub

You need to add this key to your GitHub account under Settings > SSH and GPG Keys > New SSH key.

For detailed instructions on this step, see my post Cloning a Private GitHub Repository with Plesk.

Webhooks: Automatic retrieval of new commits

What we have so far: Frontend and backend are now hosted in the right directories on our Plesk server and our frontend is built automatically with GitHub Actions. The only thing missing for a complete Plesk CI/CD workflow is that new commits are automatically detected and deployed by Plesk. For this we use webhooks.

In the repository settings in Plesk you will find a webhook URL that you have to copy out.

Plesk Git Repository: Read Webhook URL
Plesk Git Repository: Read Webhook URL

Then go to the repository settings in your GitHub repo under Settings > Webooks and click Add webhook. Here you add the URL and leave all other settings at their default values.

GitHub: Add webhooks
GitHub: Add webhooks

If your workflow does not work and error messages with missing permissions appear, you may need to generate a new token under GitHub Settings > Developer seettings > Personal access tokens > Tokens (classic) and allow all permissions on “repo”.

That’s it! All components of our Plesk CI/CD pipeline are up and running.

Setting up the workflow certainly took some time, but it will save a lot of time in the future. 🙂

Related Posts
Join the Conversation

2 Comments

Your email address will not be published. Required fields are marked *

bold italic underline strikeThrough
insertOrderedList insertUnorderedList outdent indent
removeFormat
createLink unlink
code

This can also interest you