Skip to main content

Setting up a CI/CD Pipeline with GitHub Actions #160

📖 Introduction

We've reached the final article in our "React JS: From Zero to Hero" journey! After learning about deployment platforms like Netlify and Vercel in Deploying to Vercel (Part 2), it's time to automate our deployment process. This article will introduce Continuous Integration/Continuous Deployment (CI/CD) concepts and guide you through setting up a basic CI/CD pipeline using GitHub Actions to automatically build, test, and deploy your React application.

Automating your workflow ensures consistency, reduces manual errors, and allows you to ship features faster and more reliably.


📚 Prerequisites

Before we begin, you should have:

  • A React application hosted on GitHub.
  • Your application deployable to a platform like Netlify or Vercel (ideally already set up for Git-based deploys, as this simplifies the GitHub Actions part).
  • Familiarity with basic Git commands and concepts (push, branches, pull requests).
  • Understanding of npm or yarn scripts for building and testing your app.

🎯 Article Outline: What You'll Master

In this article, you will learn:

  • What is CI/CD? Core principles and benefits.
  • Introduction to GitHub Actions: How it works, key components (workflows, events, jobs, steps, actions).
  • Creating a Basic CI Workflow: Running tests and linters on push/pull_request.
  • Creating a CD Workflow for Deployment: Building and deploying to Netlify/Vercel on pushes to the main branch.
  • Using Secrets in GitHub Actions: Securely storing API keys or deploy tokens.
  • Example Workflow for a React App.
  • Benefits of Automating Your Deployments.

🧠 Section 1: What is CI/CD?

CI/CD stands for:

  • Continuous Integration (CI): The practice of frequently merging code changes from multiple developers into a central repository (e.g., the main or develop branch). Each merge triggers an automated build and test sequence to detect integration issues early.
    • Key Goals of CI: Verify code quality, run automated tests (unit, integration), build the application, and provide rapid feedback to developers.
  • Continuous Deployment (CD): The practice of automatically deploying all code changes that pass the CI stage to a testing or production environment.
  • Continuous Delivery (also CD): Similar to Continuous Deployment, but the final deployment to production might involve a manual approval step after the CI/CD pipeline has prepared a release candidate.

Benefits of CI/CD:

  1. Faster Release Cycles: Automate build, test, and deploy processes to ship updates more frequently.
  2. Improved Code Quality: Automated tests catch bugs earlier in the development cycle.
  3. Reduced Risk: Smaller, frequent deployments are less risky than large, infrequent ones.
  4. Increased Developer Productivity: Developers can focus on writing code rather than manual deployment chores.
  5. Reliable and Consistent Deployments: Automation reduces human error.
  6. Early Bug Detection: Issues are found quickly after code is committed.

💻 Section 2: Introduction to GitHub Actions

GitHub Actions is a powerful and flexible CI/CD platform built directly into GitHub. It allows you to automate workflows based on events in your GitHub repository (e.g., pushes, pull requests, releases).

Key Components of GitHub Actions:

  • Workflow: An automated process defined by a YAML file placed in your repository under .github/workflows/. A repository can have multiple workflows.
  • Event: A specific activity in your repository that triggers a workflow run (e.g., push, pull_request, schedule, workflow_dispatch).
  • Job: A set of steps within a workflow that execute on the same "runner" (a virtual machine hosted by GitHub or self-hosted). Jobs can run in parallel or depend on other jobs.
  • Step: An individual task within a job. A step can run shell commands or use a pre-built "action."
  • Action: A reusable piece of code that performs a common task (e.g., actions/checkout@v3 to check out your repository code, actions/setup-node@v3 to set up Node.js). You can use actions from the GitHub Marketplace or create your own.
  • Runner: A server that runs your workflow jobs. GitHub provides Ubuntu Linux, Windows, and macOS runners.

🛠️ Section 3: Creating a Basic CI Workflow (Build and Test)

Let's create a workflow that runs on every push to any branch and on every pull request. This workflow will install dependencies, lint the code (optional), and run tests.

Create a file: .github/workflows/ci.yml

name: React App CI # Name of your workflow

on: # Events that trigger this workflow
push: # Runs on pushes to any branch
branches:
- main # Or your default branch
- develop
- 'feature/**' # Example: pushes to feature branches
pull_request: # Runs on pull requests targeting main or develop
branches:
- main
- develop

jobs: # A workflow run is made up of one or more jobs
build_and_test: # Name of the job
name: Build and Test
runs-on: ubuntu-latest # Specifies the runner (OS)

strategy: # Optional: run job for multiple Node versions
matrix:
node-version: [16.x, 18.x] # Test on Node 16 and 18

steps: # Sequence of tasks executed in this job
# Step 1: Check out the repository code
- name: Checkout code
uses: actions/checkout@v3

# Step 2: Set up Node.js environment
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm' # Or 'yarn' - caches dependencies

# Step 3: Install dependencies
- name: Install dependencies
run: npm ci # 'ci' is generally preferred over 'install' in CI for reproducible builds
# Or: yarn install --frozen-lockfile

# Step 4: Run linter (optional, replace with your lint command)
# - name: Lint code
# run: npm run lint

# Step 5: Run tests
- name: Run tests
run: npm test -- --coverage --watchAll=false # Add --coverage for report, --watchAll=false to exit after tests
# Or: yarn test --coverage --watchAll=false

# Step 6: Build the application (optional, but good to ensure it builds)
- name: Build application
run: npm run build
# Or: yarn build
env: # Example: Provide a dummy env var if your build needs it but it's not sensitive
REACT_APP_API_URL: "http://dummy-api.example.com"
# For Vite: VITE_API_URL: "http://dummy-api.example.com"

Explanation:

  • name: React App CI: A descriptive name for your workflow.
  • on:: Defines the events that trigger the workflow. Here, it's triggered on pushes to main, develop, or any feature/* branch, and on pull requests targeting main or develop.
  • jobs:: Contains one or more jobs.
  • build_and_test:: The ID of our job.
    • name: Build and Test: Display name for the job.
    • runs-on: ubuntu-latest: Specifies that this job will run on a GitHub-hosted Ubuntu runner.
    • strategy: matrix: node-version: [16.x, 18.x]: This is optional. It configures the job to run twice, once for Node.js 16.x and once for 18.x. Useful for ensuring compatibility.
    • steps:: The actual tasks.
      • actions/checkout@v3: An action to check out your repository's code onto the runner.
      • actions/setup-node@v3: Sets up a specific Node.js version and can cache dependencies.
      • run: npm ci: Installs dependencies. npm ci is recommended for CI as it uses the package-lock.json for a clean, reproducible install.
      • run: npm test ...: Runs your test script. The arguments --coverage --watchAll=false are common for CI: generate coverage and ensure Jest exits after running.
      • run: npm run build: Attempts to build the application. This is a good check to ensure the app is buildable, even if this CI workflow doesn't deploy. The env: block here shows how to pass non-sensitive build-time environment variables if your build process requires them.

Commit this file to your repository. GitHub Actions will automatically pick it up and start running the workflow on the specified events. You can see the progress and logs under the "Actions" tab of your GitHub repository.


🔬 Section 4: Creating a CD Workflow for Deployment

Now, let's create a separate workflow (or add a job to the existing one) to deploy our application when changes are pushed to the main branch. This assumes you've already set up your site on Netlify or Vercel for Git-based deploys.

For Netlify/Vercel Git-based Deploys (Simpler CD): If your Netlify or Vercel site is already configured to watch your main branch and auto-deploy, your CI workflow (from Section 3) is often enough!

  1. The CI workflow runs tests on push to main.
  2. If tests pass and the build step (if included in CI) succeeds, the push to main happens.
  3. Netlify/Vercel detects this push and triggers its own build and deploy process using the settings (build command, publish directory, environment variables) you configured in their UI.

In this scenario, GitHub Actions is primarily for CI (testing, linting, ensuring buildability), and Netlify/Vercel handles the CD part. This is a very common and effective setup.

For Manual Deployment via GitHub Actions (e.g., to Netlify/Vercel CLI, or other hosts): If you want GitHub Actions to perform the actual deployment step (e.g., by calling the Netlify or Vercel CLI, or deploying to AWS S3), you'd add a deployment job.

Create a file: .github/workflows/cd.yml (Example for Netlify CLI deploy)

name: Deploy to Netlify

on:
push:
branches:
- main # Only deploy when pushing to main

jobs:
deploy:
name: Deploy to Production
runs-on: ubuntu-latest
if: github.event_name == 'push' # Ensure this only runs on actual pushes, not PR merges initially if CI handles PRs

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

- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '18.x' # Use your project's Node version
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Build application
run: npm run build
env:
# These should be set as GitHub Secrets in your repository settings
REACT_APP_API_URL: ${{ secrets.PROD_API_URL }}
REACT_APP_SENTRY_DSN: ${{ secrets.PROD_SENTRY_DSN }}
# For Vite: VITE_API_URL: ${{ secrets.PROD_API_URL }}
# etc.

- name: Deploy to Netlify
uses: netlify/actions/cli@master # Official Netlify CLI action
with:
args: deploy --prod --dir=build # Or 'dist' for Vite
env:
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}

Explanation:

  • if: github.event_name == 'push': An additional condition to ensure it only runs on direct pushes to main.
  • Build Step env: Environment variables needed for the production build are sourced from GitHub Secrets.
  • netlify/actions/cli@master: Uses an official Netlify action to run Netlify CLI commands.
    • args: deploy --prod --dir=build: Deploys the contents of the build directory to production.
    • NETLIFY_SITE_ID and NETLIFY_AUTH_TOKEN are environment variables needed by the Netlify CLI. These must be stored as GitHub Secrets.

A similar approach can be used for Vercel CLI:

# ... (checkout, setup node, install, build steps are similar) ...
- name: Deploy to Vercel
run: npx vercel --prod --token ${{ secrets.VERCEL_TOKEN }} --scope <your_vercel_scope_or_team_id>
# You might need to install vercel CLI: npm install -g vercel
# Or use a dedicated Vercel GitHub Action if available and preferred.

You'd need to set VERCEL_TOKEN as a GitHub Secret and find your Vercel scope/team ID.

Choosing Between Git-based CD (Netlify/Vercel UI) vs. Actions-based CD:

  • Git-based CD (via Netlify/Vercel platform): Simpler to set up initially. The platform handles the build and deploy. Your GitHub Actions workflow focuses on CI (tests, lint). This is often recommended for these platforms.
  • Actions-based CD: Gives you more control over the exact build and deployment steps within GitHub Actions. Useful if you have complex pre-deploy or post-deploy steps, or if deploying to a platform that doesn't have tight Git integration.

✨ Section 5: Using Secrets in GitHub Actions

Sensitive information like API keys, deployment tokens (NETLIFY_AUTH_TOKEN, VERCEL_TOKEN), or production-specific environment variables should never be hardcoded in your workflow YAML files.

Use GitHub Secrets:

  1. In your GitHub repository, go to "Settings" -> "Secrets and variables" -> "Actions".
  2. Click "New repository secret."
  3. Add your secrets (e.g., PROD_API_URL, NETLIFY_SITE_ID, NETLIFY_AUTH_TOKEN).
  4. Access them in your workflow using the ${{ secrets.YOUR_SECRET_NAME }} syntax.

GitHub encrypts these secrets and only makes them available to workflows running from your repository.


🚀 Section 6: Benefits of Automating Your Deployments

Automating your build, test, and deployment pipeline with CI/CD tools like GitHub Actions brings numerous benefits that compound over time:

  • Consistency: Every deployment follows the exact same process.
  • Speed: Faster time-to-market for new features and bug fixes.
  • Reliability: Automated tests catch regressions before they hit production.
  • Reduced Toil: Frees up developers from manual, error-prone deployment tasks.
  • Confidence: Knowing that a robust pipeline is in place increases confidence in shipping code.
  • Collaboration: Clear visibility into build and deployment status for the whole team.

💡 Conclusion & Key Takeaways: The End of the Beginning!

Congratulations! You've reached the end of "React JS: From Zero to Hero." Setting up a CI/CD pipeline with GitHub Actions (or a similar tool) is a hallmark of a professional development workflow. It automates the path from code commit to live deployment, ensuring quality and speed.

Key Takeaways:

  • CI/CD automates the integration, testing, and deployment of code changes.
  • GitHub Actions allows you to define workflows in YAML to respond to repository events.
  • A typical CI workflow includes checking out code, setting up Node, installing dependencies, linting, testing, and building.
  • CD can be handled by platforms like Netlify/Vercel through their Git integration, or more explicitly within GitHub Actions using CLI tools.
  • Always use GitHub Secrets for sensitive data like API keys and deployment tokens.

This is not the end, but rather the end of the beginning. The world of React and web development is vast and constantly evolving. You now have a very strong foundation across a wide range of React concepts, from the basics to advanced state management, performance optimization, testing, and deployment.


➡️ Next Steps: Your Journey Continues!

Where do you go from here?

  • Build More Projects: The best way to solidify your knowledge is to build. Create diverse applications, experiment with different libraries, and solve real-world problems.
  • Deep Dive into Specific Areas:
    • Advanced State Management: Explore Zustand, Jotai, Recoil if you haven't already.
    • TypeScript: If you haven't embraced it, strongly consider learning TypeScript for larger, more maintainable React applications.
    • Next.js or Remix: Explore these powerful React frameworks for server-side rendering, static site generation, routing, and more.
    • Testing Specializations: Dive deeper into E2E testing with Cypress/Playwright, visual regression testing, or performance testing.
    • GraphQL & API Design: Learn about GraphQL as an alternative to REST APIs.
    • Web Performance In-Depth: Core Web Vitals, advanced optimization techniques.
    • Accessibility (a11y): Continue to prioritize building accessible applications.
  • Contribute to Open Source: A great way to learn and give back.
  • Stay Updated: Follow React blogs, influential developers, and community discussions. The ecosystem moves fast!
  • Teach Others: Explaining concepts to someone else is a fantastic way to deepen your own understanding.

Thank you for joining this "React JS: From Zero to Hero" journey. The skills you've acquired are highly valuable and will serve you well. The most important thing is to keep learning, keep building, and enjoy the process.

Happy Coding, React Hero!


glossary

  • CI/CD (Continuous Integration/Continuous Deployment or Delivery): Practices for automating software build, test, and release processes.
  • GitHub Actions: An automation platform integrated into GitHub for CI/CD and other workflow automation.
  • Workflow (GitHub Actions): An automated process defined in a YAML file, triggered by repository events.
  • Event (GitHub Actions): A specific activity that triggers a workflow (e.g., push, pull_request).
  • Job (GitHub Actions): A set of steps in a workflow that run on a runner.
  • Step (GitHub Actions): An individual task in a job (command or action).
  • Action (GitHub Actions): A reusable unit of code for common tasks.
  • Runner (GitHub Actions): A virtual machine that executes jobs.
  • GitHub Secrets: Encrypted variables stored in GitHub for use in Actions workflows, ideal for sensitive data.
  • npm ci: A command similar to npm install, but designed for CI environments; it installs dependencies based strictly on package-lock.json or yarn.lock.

Further Reading