Introduction

In the world of DevOps, automation is everything. Continuous Integration and Continuous Deployment (CI/CD) pipelines streamline code changes, ensuring rapid delivery and consistent environments. GitHub Actions, launched by GitHub in 2019, provides a powerful and native way to automate workflows directly from your GitHub repository.

Whether you’re automating tests, building Docker images, or deploying to production, GitHub Actions can do it. In this guide, we’ll build a fully functional pipeline from scratch, using GitHub Actions to handle CI/CD for a sample application.

⚙️ What is GitHub Actions?

GitHub Actions is a CI/CD and automation tool embedded into GitHub. It allows you to:

  • Run scripts or jobs automatically based on events (like pushing code or creating a pull request)
  • Build, test, and deploy applications in any language
  • Leverage GitHub-hosted runners or self-hosted ones
  • Use pre-built Actions from the GitHub Marketplace

You define your workflows as YAML files in your repository.

🔧 Setting Up Your First Workflow

Let’s get hands-on.

Step 1: Project Setup

We’ll use a simple Node.js app as an example, but the same logic applies to any language.

Directory structure:

/my-app
  ├── .github/workflows/ci.yml
  ├── package.json
  └── index.js

Step 2: Create Your Workflow File

Inside .github/workflows/, create a file named ci.yml.

name: CI Pipeline

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

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Set up Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'

      - name: Install dependencies
        run: npm install

      - name: Run tests
        run: npm test

What this pipeline does:

  • Triggers on pushes and pull requests to main
  • Runs on an Ubuntu runner
  • Checks out code, installs Node.js and dependencies
  • Executes tests

🧠 Understanding Key Concepts

1. Triggers (on:)

These define when the workflow runs:

  • push and pull_request are most common
  • You can also use workflow_dispatch for manual runs

2. Jobs

Each workflow contains one or more jobs, which run independently by default.

jobs:
  build:
    runs-on: ubuntu-latest

You can define job dependencies with needs: if they need to run in sequence.

3. Steps

Steps are the individual commands or actions inside a job.

steps:
  - name: Install dependencies
    run: npm install

4. Actions

You can use pre-built or custom actions via:

uses: actions/setup-node@v3

🧪 Adding Test Reporting

Let’s add more depth by saving test reports for later analysis.

Update the test step:

- name: Run tests with coverage
  run: |
    npm install -g jest
    jest --coverage

And optionally upload the report:

- name: Upload coverage report
  uses: actions/upload-artifact@v3
  with:
    name: coverage-report
    path: coverage/

🚢 Adding a Deployment Step

Suppose you want to deploy to a VPS over SSH after successful tests.

Add secrets to GitHub:

You can add this under Settings -> Secrets and variables -> Actions

  • SERVER_IP
  • SSH_USER
  • SSH_KEY (private key)

Deployment step:

- name: Deploy to server
  if: success()
  run: |
    ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=no ${{ secrets.SSH_USER }}@${{ secrets.SERVER_IP }} 'bash ~/deploy.sh'
  env:
    SSH_KEY: ${{ secrets.SSH_KEY }}

Also, add a step to write the private key:

- name: Set up SSH key
  run: |
    mkdir -p ~/.ssh
    echo "${{ secrets.SSH_KEY }}" > ~/.ssh/deploy_key
    chmod 600 ~/.ssh/deploy_key

This securely deploys code to your VPS on every push.

✅ Bonus: Manual Triggers and Environment Approval

Use workflow_dispatch for manual triggers:

on:
  workflow_dispatch:

And define environments with approvals:

jobs:
  deploy:
    environment:
      name: production
      url: https://my-app.com
    runs-on: ubuntu-latest

🧰 Useful GitHub Actions from the Marketplace

  • Upload Artifact: actions/upload-artifact
  • Slack Notifications: 8398a7/action-slack
  • Docker Build & Push: docker/build-push-action
  • Cache Dependencies: actions/cache

🧪 Common CI Patterns

1. Caching Dependencies

Speed up builds with caching:

- name: Cache node modules
  uses: actions/cache@v3
  with:
    path: ~/.npm
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}

2. Matrix Builds

Test across multiple versions:

strategy:
  matrix:
    node: [14, 16, 18]

🧱 Best Practices

  • Use secret management for sensitive data
  • Keep workflows modular using reusable actions
  • Add status checks for PRs
  • Use caching to reduce execution time
  • Limit workflow triggers to necessary branches

🚀 Wrapping Up

GitHub Actions makes CI/CD incredibly accessible, especially for projects hosted on GitHub. With just a few lines of YAML, you can automate code testing, linting, deployments, notifications, and more.

Here’s what we accomplished:

  • Built a working CI pipeline
  • Installed dependencies and ran tests
  • Uploaded test reports
  • Deployed to a remote server over SSH

This basic pipeline can scale into a powerful DevOps automation engine as your project grows.

Power Your Projects with vpszen.com VPS Solutions

Looking for reliable hosting to run your Linux servers and host your next big project? VpsZen.com has you covered with top-tier VPS options tailored to your needs.
Choose from ARM64 VPS Servers for energy-efficient performance, or Root VPS Servers for virtual servers with dedicated resources.