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
- [ ] Implement parallel job execution where possible
- [ ] Use caching for dependencies (node_modules)
- [ ] Store secrets properly in GitHub Secrets
- [ ] Implement proper error handling and notifications
- [ ] Include security scanning in the pipeline
- [ ] Set up branch protection rules
- [ ] Implement manual approval for production
- [ ] Include performance testing
- [ ] Use matrix strategies for multi-environment testing
- [ ] 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]')"