Introduction

Diagram:

Untitled

Prerequisite:

Before beginning, ensure you have setup EC2 instance in AWS. (Follow this link: https://www.linkedin.com/pulse/step-by-step-guide-creating-ec2-instance-aws-kapil-pattnaik)

Note, please choose Ubuntu instance.

Setting up a workflow with GitHub Actions

Now that you have a basic understanding of how the CI/CD pipeline works, let's delve deeper into setting it up using GitHub Actions. This guide will walk you through creating and configuring a workflow that automates your Continuous Integration and Continuous Deployment processes.

  1. Create A Workflow

    First, you need to set up the workflow configuration file within your project:

  2. Define Workflow Triggers

    Specify when the workflow should be triggered. Common triggers include pushes to specific branches or when a pull request is made to those branches. For example:

    name: CI/CD Pipeline for Service
    
    on:
      pull_request:
        branches: [main]  # Specify branches as needed
        paths:
          - 'path/to/your/service/**'  # Adjust path to match the service directory
      push:
        branches: [main]  # Specify branches as needed
        paths:
          - 'path/to/your/service/**'  # Adjust path to match the service directory
    

    Explanation:

  3. Configure the Build Job

    Define a job to handle the building and testing of your application. This job will checkout the code, set up the Node.js environment, install dependencies, run the build process, and execute tests.

    jobs:
      build:
        runs-on: ubuntu-latest  # Specifies that the job should run on the latest Ubuntu virtual environment provided by GitHub
        defaults:
          run:
            working-directory: path/to/your/project  # Adjust this to the root directory of your Node.js project
    
        steps:
          - name: Checkout code
            uses: actions/checkout@v4  # This step checks out your repository under $GITHUB_WORKSPACE, so your workflow can access it
            with:
              fetch-depth: 0  # Ensures all branches and tags are fetched, useful for versioning or other git-related operations
    
          - name: Set up Node.js
            uses: actions/setup-node@v4  # This step sets up a Node.js environment with a specified version
            with:
              node-version: '20'  # Specify the Node.js version you require, e.g., '14', '16'
    
          - name: Install dependencies
            run: yarn install  # Runs yarn install to install all dependencies defined in your package.json
    
          - name: Run build
            run: yarn build  # Executes the build script defined in your package.json
    
          - name: Run tests
            env:
              CUSTOM_ENV_VARIABLE: ${{ secrets.CUSTOM_ENV_VARIABLE }}  # Replace with actual environment variables needed for testing
            run: yarn test  # Executes tests using yarn. Customize this command based on your test runner
    
          - name: Archive build artifacts
            uses: actions/upload-artifact@v4  # Archives build artifacts for use in later jobs or for deployment
            with:
              name: artifact-name  # Optionally change the name of the artifact
              path: ./path/to/artifacts  # Adjust this to where your build outputs artifacts, e.g., './build'
    

    Notes for Customization:

  4. Define the Deployment Job

    Set up another job that handles the deployment of your application once the build job has completed successfully. This job should be configured to run only if the build job succeeds and should handle tasks such as setting up the environment, transferring files, and managing application start with PM2.

    jobs:
      deploy:
        runs-on: ubuntu-latest  # Specifies that the job should run on the latest Ubuntu virtual environment provided by GitHub
        needs: build  # Specifies that this job needs the 'build' job to complete successfully before it starts
        if: github.ref == 'refs/heads/main'  # This job runs only if the push or PR merge is to the 'main' branch
    
        steps:
          - name: Download build artifacts
            uses: actions/download-artifact@v4  # Downloads artifacts from the build job
            with:
              name: build  # The name of the artifact to download
              path: ./path/to/artifacts # The path to store the downloaded artifact
    
          - name: Prepare Deployment Directories
            uses: appleboy/ssh-action@master  # SSH into the server to prepare directories
            with:
              host: ${{ secrets.SERVER_IP }}  # Server IP address from secrets
              username: ${{ secrets.SERVER_USERNAME }}  # Server username from secrets
              key: ${{ secrets.SSH_PRIVATE_KEY }}  # SSH private key from secrets
              port: 22  # SSH port, usually 22
              script: |
                mkdir -p /home/ubuntu/apps/build  # Change to match your desire directory structure
                mkdir -p /home/ubuntu/apps/build/configs  # For additional configuration files
    
          - name: Copy files to Server
            uses: appleboy/scp-action@master  # Copies files to the server using SCP
            with:
              host: ${{ secrets.SERVER_IP }}
              username: ${{ secrets.SERVER_USERNAME }}
              key: ${{ secrets.SSH_PRIVATE_KEY }}
              port: 22
              source: "./path/to/artifacts/*"
              target: "/home/ubuntu/apps/build"
              strip_components: 1  # Adjust based on the directory depth of the source
    
          - name: Create .env File
            uses: appleboy/ssh-action@master  # Creates an environment variable file on the server
            with:
              host: ${{ secrets.SERVER_IP }}
              username: ${{ secrets.SERVER_USERNAME }}
              key: ${{ secrets.SSH_PRIVATE_KEY }}
              port: 22
              script: |
                echo "NODE_ENV=production" > /path/to/deployment/directory/build/.env
                echo "PORT=your_port_number" >> /path/to/deployment/directory/build/.env
                echo "DATABASE_URL=your_database_url" >> /path/to/deployment/directory/build/.env
    
          - name: Install Dependencies and Restart Application
            uses: appleboy/ssh-action@master  # Installs dependencies and restarts the application using a process manager
            with:
              host: ${{ secrets.SERVER_IP }}
              username: ${{ secrets.SERVER_USERNAME }}
              key: ${{ secrets.SSH_PRIVATE_KEY }}
              port: 22
              script: |
                cd /path/to/deployment/directory/build
                yarn install --production
                # Assume PM2 is used, Replace with other command for other process manager
                # Assume you have script for run start in production and restart in production
                # Check if the PM2 process is running
                if pm2 show your-service-name > /dev/null; then
                  echo "Application is running. Restarting..."
                  yarn restart
                else
                  echo "Application is not running. Starting..."
                  yarn start
                fi
    

    Customization Tips: