Diagram:
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.
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.
Create A Workflow
First, you need to set up the workflow configuration file within your project:
.github/workflows/
.your-service-name.cicd.yml
. This file will define all the jobs, steps, and actions that your CI/CD process requires.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:
pull_request
: The workflow triggers on pull requests to the main
branch that modify files in the specified paths
. This helps ensure that all proposed changes are built and tested before they are merged.push
: Similar to pull requests, any direct pushes to the main
branch that affect files within the specified paths will also trigger the workflow. This ensures that changes pushed directly to the branch are also subjected to your automated processes.branches
: Adjust the branch names according to your branching strategy. For example, if you use develop
for development and main
for production, you might want to trigger the workflow for both branches.paths
: Specify the directory paths that contain the source code for the service this workflow covers. Adjust this to point to different services or components depending on your project's structure.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:
node-version
field in the "Set up Node.js" step according to the Node.js version required by your project.working-directory
to point to the directory containing your project's package.json
.env
mapping under "Run tests" to include any environment-specific variables necessary for your tests.path
under "Archive build artifacts" accurately points to the directory containing the build output you want to archive and use later in the deployment or additional testing.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:
.env
creation step to include all necessary environment variables specific to your application.