Bluewoo HRMS
Deployment

CI/CD Pipeline

GitHub Actions workflow for automated testing and deployment

CI/CD Pipeline

This guide covers the GitHub Actions workflow for continuous integration and deployment.

Pipeline Overview

┌─────────────────────────────────────────────────────────────────┐
│  Push to Branch / PR                                             │
└─────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
│  TEST JOB                                                        │
│  - Checkout code                                                 │
│  - Install dependencies                                          │
│  - Run linting                                                   │
│  - Run type checking                                             │
│  - Run tests                                                     │
│  - Build application                                             │
└─────────────────────────────────────────────────────────────────┘

              ┌───────────────┴───────────────┐
              │                               │
              ▼                               ▼
┌──────────────────────┐         ┌──────────────────────┐
│  PREVIEW (PR only)   │         │  STAGING (main only) │
│  - Build Docker      │         │  - Build Docker      │
│  - Push to Registry  │         │  - Push to Registry  │
│  - Deploy to Cloud   │         │  - Deploy to Cloud   │
│    Run (preview URL) │         │    Run (staging)     │
└──────────────────────┘         └──────────────────────┘


                                 ┌──────────────────────┐
                                 │  PRODUCTION          │
                                 │  (requires approval) │
                                 │  - Deploy to Cloud   │
                                 │    Run (production)  │
                                 └──────────────────────┘

GitHub Repository Secrets

Add these secrets to your GitHub repository:

SecretDescriptionHow to Get
GCP_PROJECT_IDGCP project IDbluewoo-hrms
GCP_REGIONGCP regioneurope-west6
GCP_WORKLOAD_IDENTITY_PROVIDERWorkload identity providerSee infrastructure setup
GCP_SERVICE_ACCOUNTService account emailgithub-actions@bluewoo-hrms.iam.gserviceaccount.com

GitHub Actions Workflow

Create .github/workflows/deploy.yml:

name: Build and Deploy

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

env:
  PROJECT_ID: bluewoo-hrms
  REGION: europe-west6
  SERVICE_NAME: hrms-docs
  REGISTRY: europe-west6-docker.pkg.dev

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run linting
        run: npm run lint

      - name: Run type checking
        run: npm run types:check

      - name: Build
        run: npm run build

  deploy-preview:
    if: github.event_name == 'pull_request'
    needs: test
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write
      pull-requests: write

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Authenticate to Google Cloud
        uses: google-github-actions/auth@v2
        with:
          workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }}
          service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }}

      - name: Set up Cloud SDK
        uses: google-github-actions/setup-gcloud@v2

      - name: Configure Docker
        run: gcloud auth configure-docker ${{ env.REGISTRY }}

      - name: Build and Push
        run: |
          docker build -t ${{ env.REGISTRY }}/${{ env.PROJECT_ID }}/bluewoo-images/${{ env.SERVICE_NAME }}:pr-${{ github.event.number }} .
          docker push ${{ env.REGISTRY }}/${{ env.PROJECT_ID }}/bluewoo-images/${{ env.SERVICE_NAME }}:pr-${{ github.event.number }}

      - name: Deploy to Cloud Run (Preview)
        run: |
          gcloud run deploy ${{ env.SERVICE_NAME }}-pr-${{ github.event.number }} \
            --image ${{ env.REGISTRY }}/${{ env.PROJECT_ID }}/bluewoo-images/${{ env.SERVICE_NAME }}:pr-${{ github.event.number }} \
            --region ${{ env.REGION }} \
            --platform managed \
            --allow-unauthenticated \
            --tag pr-${{ github.event.number }}

      - name: Get Preview URL
        id: preview-url
        run: |
          URL=$(gcloud run services describe ${{ env.SERVICE_NAME }}-pr-${{ github.event.number }} --region ${{ env.REGION }} --format 'value(status.url)')
          echo "url=$URL" >> $GITHUB_OUTPUT

      - name: Comment PR with Preview URL
        uses: actions/github-script@v7
        with:
          script: |
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: '🚀 Preview deployed to: ${{ steps.preview-url.outputs.url }}'
            })

  deploy-staging:
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    needs: test
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Authenticate to Google Cloud
        uses: google-github-actions/auth@v2
        with:
          workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }}
          service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }}

      - name: Set up Cloud SDK
        uses: google-github-actions/setup-gcloud@v2

      - name: Configure Docker
        run: gcloud auth configure-docker ${{ env.REGISTRY }}

      - name: Build and Push
        run: |
          docker build -t ${{ env.REGISTRY }}/${{ env.PROJECT_ID }}/bluewoo-images/${{ env.SERVICE_NAME }}:${{ github.sha }} .
          docker push ${{ env.REGISTRY }}/${{ env.PROJECT_ID }}/bluewoo-images/${{ env.SERVICE_NAME }}:${{ github.sha }}

      - name: Deploy to Cloud Run (Staging)
        run: |
          gcloud run deploy ${{ env.SERVICE_NAME }}-staging \
            --image ${{ env.REGISTRY }}/${{ env.PROJECT_ID }}/bluewoo-images/${{ env.SERVICE_NAME }}:${{ github.sha }} \
            --region ${{ env.REGION }} \
            --platform managed \
            --allow-unauthenticated

  deploy-production:
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    needs: deploy-staging
    runs-on: ubuntu-latest
    environment: production
    permissions:
      contents: read
      id-token: write

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Authenticate to Google Cloud
        uses: google-github-actions/auth@v2
        with:
          workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }}
          service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }}

      - name: Set up Cloud SDK
        uses: google-github-actions/setup-gcloud@v2

      - name: Deploy to Cloud Run (Production)
        run: |
          gcloud run deploy ${{ env.SERVICE_NAME }} \
            --image ${{ env.REGISTRY }}/${{ env.PROJECT_ID }}/bluewoo-images/${{ env.SERVICE_NAME }}:${{ github.sha }} \
            --region ${{ env.REGION }} \
            --platform managed \
            --allow-unauthenticated

GitHub Environment Setup

  1. Go to repository Settings → Environments
  2. Create environment named production
  3. Add required reviewers (yourself)
  4. Optionally add deployment branch rules

Workflow Triggers

EventTriggerActions
Push to branchCreate PRTest job only
PR created/updatedPR eventTest + Preview deploy
Merge to mainPush to mainTest + Staging + Production (with approval)

Rollback

To rollback production:

# List revisions
gcloud run revisions list --service=hrms-docs --region=europe-west6

# Route traffic to previous revision
gcloud run services update-traffic hrms-docs \
  --region=europe-west6 \
  --to-revisions=hrms-docs-PREVIOUS_REVISION=100

Cleanup Preview Environments

Add a cleanup workflow for closed PRs:

name: Cleanup Preview

on:
  pull_request:
    types: [closed]

jobs:
  cleanup:
    runs-on: ubuntu-latest
    steps:
      - name: Delete Preview Service
        run: |
          gcloud run services delete ${{ env.SERVICE_NAME }}-pr-${{ github.event.number }} \
            --region ${{ env.REGION }} \
            --quiet

Workflow File Location

The complete workflow is at .github/workflows/deploy.yml in the repository.

Summary

JobTriggerPurpose
testAll pushes/PRsLint, type check, build
deploy-previewPR opened/updatedDeploy unique preview URL
deploy-stagingMerge to mainAuto-deploy to staging
deploy-productionAfter stagingManual approval required
cleanup-previewPR closedDelete preview resources

Status: Verified - Workflow file created at .github/workflows/deploy.yml