GitHub Action - Level 1

Github Actions is a CI/CD tool built into GitHub.
Terminologies
- Workflow
- Event
- Job
- Runner
- Step
GitHub Actions or workflows should be at
/.github/workflows. It is compulsory.
Example Workflow
name: Example CI
on:
pull_request: "main"
jobs:
test_runner:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 18
- name: Install dependencies
run: npm install
- name: Run tests
run: npm test
Explaination:
The above workflow runs on pull request to main branch. The name for this workflow is Example CI . It only runs one job viz. test_runner . In the job all the mentioned steps execute one after another.
In Step 1: The repo is clone to the VM on github server.
In Step 2: Node is installed on that VM.
In Step 3: All dependencies are installed.
In Step 4: tests are run.
Common Questions:
test_runneris just a job name. We can name it anything. But some conventional names are build, test, deploy, lint, ci and cd.We can create multiple workflows in
.github/workflowsand can name the files anything. The visible name in GitHub UI is thename: Example CIfor above example. Also, GitHub automatically detects all.ymlfiles in that folder and treats each as separate workflow and run them independently. Each workflow has its own trigger.What is
action/setup-node@v4?action/setup-node@v4,actions/checkout#v4are prebuilt actions from github.Some common prebuilt actions and there use cases are
action use case actions/checkout@v4 Clones your repo into to runner actions/setup-node@v4 Install Node.js in the runner actions/setup-python@v4 install python in the runner appleboy/ssh-action Connect to remote server via SSH appleboy/scp-action used to copy folder to remote server actions/upload-artifact@v4 Upload build artifact actions/download-artifact@v4 download build artifacts Some most common triggers are push, pull_request, schedule.
Some More Examples
1. Basic Node.js application CD
name: Deploy Node App
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install dependencies
run: npm install
- name: Build project
run: npm run build
- name: Deploy to EC2
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.EC2_HOST }}
username: ubuntu
key: ${{ secrets.EC2_SSH_KEY }}
script: |
cd /var/www/my-app
git pull origin main
npm install
npm run build
pm2 restart my-app
EC2_HOST,EC2_SSH_KEYare need to specify in GitHub Secrets. Repo → Settings → Secrets → Actions → Add Secrets
2. Node.js Optimized CD to use built or dist
name: Deploy to EC2
on:
push:
branches:
- main
jobs:
deploy:
name: Deploy Backend
runs-on: ubuntu-latest
steps:
# Checkout Code
- name: Checkout code
uses: actions/checkout@v4
# Setup Node
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
# Install Dependencies
- name: Install deps
run: npm install
# Build project
- name: Build project
run: npm run build
# Debugging files: (Can be removed)
- name: Debug workspace
run: |
echo "Current path:"
pwd
echo "Files in root:"
ls -la
echo "Looking for dist:"
ls -la dist || echo "DIST NOT FOUND"
# Upload compiled build to EC2
- name: Upload build to EC2
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USER }}
key: ${{ secrets.EC2_KEY }}
source: "dist/*, package.json"
target: "/home/ubuntu/server/test-repo"
# Install production deps & restart app
- name: Restart backend
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USER }}
key: ${{ secrets.EC2_KEY }}
script: |
set -e
cd /home/ubuntu/server/careConnect-server
echo "Installing production dependencies..."
npm install --omit=dev --no-audit --no-fund
echo "Restarting backend..."
pm2 delete test_server || true
pm2 start dist/main.js --name test_server
pm2 save
3. Simple CI + CD for nodejs Application
name: Node CI + CD
on:
push:
branches:
- main
- develop
pull_request:
jobs:
# BUILD & TEST JOB
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 18
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm run test
- name: Build project
run: npm run build
# Save build as artifact (optional but good practice)
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: build-files
path: |
dist
package.json
package-lock.json
# DEPLOY JOB
deploy:
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/main'
steps:
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: build-files
- name: Copy files to EC2
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.EC2_HOST }}
username: ubuntu
key: ${{ secrets.EC2_SSH_KEY }}
source: "dist,package.json,package-lock.json"
target: "/var/www/node-app"
- name: Restart app on EC2
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.EC2_HOST }}
username: ubuntu
key: ${{ secrets.EC2_SSH_KEY }}
script: |
cd /var/www/node-app
npm ci --omit=dev
pm2 reload node-app
This workflow performs:
- CI:
- Runs on push (main, develop) and pull requests
- Installs dependencies
- Runs tests
- Builds the project
- Uploads build artifacts
- CD:
- Runs only after successful build job
- Deploys only when branch is 'main’
- Downloads build artifacts
- Copies dist + package files to EC2
- Installs production dependencies
- Restarts application using PM2
4. Optimised CI + CD for Node.js Application
name: NestJS CI
on:
pull_request:
push:
branches:
- develop
- main
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 18
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run lint
run: npm run lint
- name: Run tests
run: npm run test
Runs on:
- PR
- push to develop
- push to main
Executes:
- Lint
- Unit tests
If anything fails → workflow fails.
name: NestJS Deploy
on:
workflow_run:
workflows: ["NestJS CI"]
types:
- completed
jobs:
deploy:
if: >
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.head_branch == 'main'
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 18
- name: Install dependencies
run: npm ci
- name: Build project
run: npm run build
- name: Copy build files to EC2
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.EC2_HOST }}
username: ubuntu
key: ${{ secrets.EC2_SSH_KEY }}
source: "dist,package.json,package-lock.json"
target: "/var/www/nest-app"
- name: Restart app on EC2
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.EC2_HOST }}
username: ubuntu
key: ${{ secrets.EC2_SSH_KEY }}
script: |
cd /var/www/nest-app
npm ci --omit=dev
pm2 reload nest-app