CI/CD Pipeline for React Apps Using GitHub Actions

Loading

GitHub Actions provides a powerful platform for automating your React application’s build, test, and deployment processes. Here’s a comprehensive guide to setting up a production-grade CI/CD pipeline:

Core Pipeline Structure

1. Basic Workflow File (.github/workflows/main.yml)

name: React CI/CD Pipeline

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

env:
  CI: true
  NODE_VERSION: 18.x

jobs:
  test:
    name: Run Tests
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - uses: actions/setup-node@v3
      with:
        node-version: ${{ env.NODE_VERSION }}
    - run: yarn install --frozen-lockfile
    - run: yarn test --ci --coverage
    - uses: actions/upload-artifact@v3
      if: always()
      with:
        name: test-results
        path: |
          coverage
          junit.xml

  build:
    name: Build Production
    needs: test
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - uses: actions/setup-node@v3
      with:
        node-version: ${{ env.NODE_VERSION }}
    - run: yarn install --frozen-lockfile
    - run: yarn build
    - uses: actions/upload-artifact@v3
      with:
        name: build-artifacts
        path: build

Advanced Deployment Workflows

2. Deployment to AWS S3

deploy-s3:
  name: Deploy to S3
  needs: build
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v3
    - uses: actions/download-artifact@v3
      with:
        name: build-artifacts
        path: build
    - uses: aws-actions/configure-aws-credentials@v2
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: us-east-1
    - run: aws s3 sync build s3://your-bucket-name --delete
    - run: aws cloudfront create-invalidation --distribution-id ${{ secrets.CF_DISTRIBUTION_ID }} --paths "/*"

3. Kubernetes Deployment

deploy-k8s:
  name: Deploy to Kubernetes
  needs: build
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v3
    - uses: azure/setup-kubectl@v3
    - uses: actions/download-artifact@v3
      with:
        name: build-artifacts
        path: build
    - run: kubectl config set-cluster ${{ secrets.K8S_CLUSTER }} --server=${{ secrets.K8S_SERVER }}
    - run: kubectl config set-credentials ci-user --token=${{ secrets.K8S_TOKEN }}
    - run: kubectl config set-context ci-context --cluster=${{ secrets.K8S_CLUSTER }} --user=ci-user
    - run: kubectl config use-context ci-context
    - run: kubectl rollout restart deployment/react-app -n production

Docker Image Building

4. Build and Push Docker Image

docker-build:
  name: Build and Push Docker Image
  needs: build
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v3
    - uses: actions/download-artifact@v3
      with:
        name: build-artifacts
        path: build
    - uses: docker/setup-buildx-action@v2
    - uses: docker/login-action@v2
      with:
        username: ${{ secrets.DOCKER_HUB_USERNAME }}
        password: ${{ secrets.DOCKER_HUB_TOKEN }}
    - run: docker build -t your-username/react-app:${{ github.sha }} .
    - run: docker push your-username/react-app:${{ github.sha }}
    - run: |
        echo "IMAGE_TAG=${{ github.sha }}" >> $GITHUB_ENV
        echo "DEPLOY_TIMESTAMP=$(date +%Y%m%d%H%M%S)" >> $GITHUB_ENV

Advanced Features

5. Multi-environment Deployment

deploy:
  name: Deploy
  needs: build
  runs-on: ubuntu-latest
  strategy:
    matrix:
      environment: [staging, production]
    fail-fast: false
  steps:
    - uses: actions/checkout@v3
    - uses: actions/download-artifact@v3
      with:
        name: build-artifacts
        path: build
    - run: |
        if [ "${{ matrix.environment }}" = "production" ]; then
          aws s3 sync build s3://prod-bucket --delete
        else
          aws s3 sync build s3://stage-bucket --delete
        fi
      env:
        AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
        AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

6. Automated Version Bumping

version-bump:
  name: Bump Version
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v3
    - uses: actions/setup-node@v3
    - run: yarn install --frozen-lockfile
    - uses: mathieudutour/github-tag-action@v6
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        custom_tag: "v$(node -p 'require(\"./package.json\").version')"

Security Scanning

7. Integrated Security Checks

security:
  name: Security Scan
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v3
    - uses: actions/setup-node@v3
    - run: yarn install --frozen-lockfile
    - uses: shiftleftio/scan-action@master
      with:
        output: reports
    - uses: actions/upload-artifact@v3
      with:
        name: security-reports
        path: reports
    - uses: snyk/actions/node@master
      with:
        args: --all-projects
      env:
        SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}

Performance Optimization

8. Lighthouse Audit

lighthouse:
  name: Lighthouse Audit
  needs: deploy-s3
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v3
    - uses: actions/setup-node@v3
    - uses: treosh/lighthouse-ci-action@v9
      with:
        urls: |
          https://your-site.com
          https://your-site.com/dashboard
        uploadArtifacts: true
        temporaryPublicStorage: true

Complete Production Pipeline

name: Production CI/CD Pipeline

on:
  push:
    branches: [ main ]
    tags: [ 'v*' ]
  pull_request:
    branches: [ main ]

env:
  CI: true
  NODE_VERSION: 18.x
  DOCKER_IMAGE: your-org/react-app

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - uses: actions/setup-node@v3
      with:
        node-version: ${{ env.NODE_VERSION }}
    - run: yarn install --frozen-lockfile
    - run: yarn lint

  test:
    runs-on: ubuntu-latest
    needs: lint
    steps:
    - uses: actions/checkout@v3
    - uses: actions/setup-node@v3
      with:
        node-version: ${{ env.NODE_VERSION }}
    - run: yarn install --frozen-lockfile
    - run: yarn test --ci --coverage --reporters=default --reporters=jest-junit
    - uses: actions/upload-artifact@v3
      with:
        name: test-results
        path: |
          coverage
          junit.xml

  build:
    runs-on: ubuntu-latest
    needs: test
    steps:
    - uses: actions/checkout@v3
    - uses: actions/setup-node@v3
      with:
        node-version: ${{ env.NODE_VERSION }}
    - run: yarn install --frozen-lockfile
    - run: yarn build
    - uses: actions/upload-artifact@v3
      with:
        name: build-artifacts
        path: build

  docker:
    runs-on: ubuntu-latest
    needs: build
    steps:
    - uses: actions/checkout@v3
    - uses: actions/download-artifact@v3
      with:
        name: build-artifacts
        path: build
    - uses: docker/setup-buildx-action@v2
    - uses: docker/login-action@v2
      with:
        username: ${{ secrets.DOCKER_HUB_USERNAME }}
        password: ${{ secrets.DOCKER_HUB_TOKEN }}
    - run: |
        docker build -t ${{ env.DOCKER_IMAGE }}:${{ github.sha }} .
        docker push ${{ env.DOCKER_IMAGE }}:${{ github.sha }}
        if [[ ${{ github.ref }} == 'refs/heads/main' ]]; then
          docker tag ${{ env.DOCKER_IMAGE }}:${{ github.sha }} ${{ env.DOCKER_IMAGE }}:latest
          docker push ${{ env.DOCKER_IMAGE }}:latest
        fi

  deploy:
    runs-on: ubuntu-latest
    needs: docker
    if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')
    steps:
    - uses: azure/setup-kubectl@v3
    - run: |
        kubectl config set-cluster ${{ secrets.K8S_CLUSTER }} --server=${{ secrets.K8S_SERVER }}
        kubectl config set-credentials ci-user --token=${{ secrets.K8S_TOKEN }}
        kubectl config set-context ci-context --cluster=${{ secrets.K8S_CLUSTER }} --user=ci-user
        kubectl config use-context ci-context
        kubectl set image deployment/react-app react-app=${{ env.DOCKER_IMAGE }}:${{ github.sha }} -n production
        kubectl rollout status deployment/react-app -n production --timeout=90s

  notify:
    runs-on: ubuntu-latest
    needs: deploy
    steps:
    - uses: actions/checkout@v3
    - uses: rtCamp/action-slack-notify@v2
      env:
        SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
        SLACK_COLOR: ${{ job.status }} # 'success' or 'failure'
        SLACK_TITLE: 'Deployment ${{ job.status }}'
        SLACK_MESSAGE: 'React app deployed to production (${{ github.sha }})'

Best Practices Checklist

  1. [ ] Implement parallel job execution where possible
  2. [ ] Use caching for dependencies (node_modules)
  3. [ ] Store secrets properly in GitHub Secrets
  4. [ ] Implement proper error handling and notifications
  5. [ ] Include security scanning in the pipeline
  6. [ ] Set up branch protection rules
  7. [ ] Implement manual approval for production
  8. [ ] Include performance testing
  9. [ ] Use matrix strategies for multi-environment testing
  10. [ ] Document your pipeline architecture

Performance Optimization Tips

1. Dependency Caching

- uses: actions/cache@v3
  with:
    path: |
      node_modules
      */*/node_modules
    key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
    restore-keys: |
      ${{ runner.os }}-yarn-

2. Parallel Testing

test:
  strategy:
    matrix:
      os: [ubuntu-latest, windows-latest]
      node-version: [16.x, 18.x]
  runs-on: ${{ matrix.os }}
  steps:
    - uses: actions/setup-node@v3
      with:
        node-version: ${{ matrix.node-version }}

3. Selective Job Execution

jobs:
  build:
    if: "!contains(github.event.head_commit.message, '[skip ci]')"

Leave a Reply

Your email address will not be published. Required fields are marked *