Skip to content

DevOps Pipelines

Weixian edited this page May 25, 2026 · 7 revisions

GitHub Actions Pipeline


  • Proposed 3 GitHub Action workflows to support continuous delivery with our branching strategy.
  • GitHub and Azure Environments
Azure / GitHub Environment Git Branch Approval Required Description
SIT main No Continuous integration environment where feature branches are merged and validated through automated build, unit testing, and integration testing before release preparation.
UAT release/1.4 Yes User Acceptance Testing environment used for business validation and sign-off of the scoped release version before promotion to higher environments.
Pre-Production release/1.4 Yes Production-like staging environment used for final regression testing, deployment verification, and operational readiness checks before go-live.

Main Branch to SIT Environment

name: Main branch to SIT

on:
  push:
    branches:
      - main

permissions:
  contents: read
  id-token: write   # required for Azure OIDC login

env:
  APP_NAME: my-app
  ACR_NAME: myacr                          # e.g. myacr (without .azurecr.io)
  ACR_LOGIN_SERVER: myacr.azurecr.io
  RESOURCE_GROUP: rg-sit
  CONTAINER_APP_NAME: my-app-sit
  CONTAINER_APP_ENV: cae-sit               # Container Apps environment name

concurrency:
  group: main-sit-deploy
  cancel-in-progress: true

jobs:
  # -------------------------
  # 1. BUILD & PUSH IMAGE
  # -------------------------
  build:
    name: Build, Test & Push Image
    runs-on: ubuntu-latest
    outputs:
      image_tag: ${{ steps.vars.outputs.image_tag }}
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Install dependencies
        run: echo "install dependencies"

      - name: Run unit tests
        run: echo "run tests"

      - name: Set image tag
        id: vars
        run: echo "image_tag=${GITHUB_SHA::7}" >> $GITHUB_OUTPUT

      - name: Azure Login (OIDC)
        uses: azure/login@v2
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

      - name: Log in to ACR
        run: az acr login --name ${{ env.ACR_NAME }}

      - name: Build and push Docker image
        run: |
          docker build -t ${{ env.ACR_LOGIN_SERVER }}/${{ env.APP_NAME }}:${{ steps.vars.outputs.image_tag }} .
          docker push ${{ env.ACR_LOGIN_SERVER }}/${{ env.APP_NAME }}:${{ steps.vars.outputs.image_tag }}

  # -------------------------
  # 2. DEPLOY TO SIT (AUTO)
  # -------------------------
  deploy-sit:
    name: Deploy to SIT
    needs: build
    runs-on: ubuntu-latest
    environment: sit   # no approval
    steps:
      - name: Azure Login (OIDC)
        uses: azure/login@v2
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

      - name: Deploy to Azure Container Apps
        uses: azure/container-apps-deploy-action@v2
        with:
          resourceGroup: ${{ env.RESOURCE_GROUP }}
          containerAppName: ${{ env.CONTAINER_APP_NAME }}
          containerAppEnvironment: ${{ env.CONTAINER_APP_ENV }}
          imageToDeploy: ${{ env.ACR_LOGIN_SERVER }}/${{ env.APP_NAME }}:${{ needs.build.outputs.image_tag }}
          targetPort: 8080
          ingress: external

      - name: Smoke Test
        run: |
          echo "Running smoke tests on SIT..."
          FQDN=$(az containerapp show -n ${{ env.CONTAINER_APP_NAME }} -g ${{ env.RESOURCE_GROUP }} --query properties.configuration.ingress.fqdn -o tsv)
          curl -fsSL "https://${FQDN}/health" || exit 1

RBUPP - Release Branch - UAT, Pre-Production, Production

name: Release branch → UAT → pre-prod → production
on:
  push:
    branches:
      - 'release/*'
permissions:
  contents: write   # needed for tagging
  id-token: write   # needed for Azure OIDC login
env:
  APP_NAME: my-app
  ACR_NAME: myacr                       # without .azurecr.io
  ACR_LOGIN_SERVER: myacr.azurecr.io
  TARGET_PORT: 8080
jobs:
  # -------------------------
  # 1. BUILD ONCE & PUSH IMAGE
  # -------------------------
  build:
    name: Build & Push Image
    runs-on: ubuntu-latest
    # Skip if the commit is a merge of a hotfix (avoid re-deploying hotfix merges to UAT)
    if: ${{ !(contains(toLowerCase(github.event.head_commit.message), 'merge') && contains(toLowerCase(github.event.head_commit.message), 'hotfix')) }}
    outputs:
      version: ${{ steps.extract_version.outputs.version }}
      image: ${{ steps.image.outputs.image }}
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      - name: Extract version from branch
        id: extract_version
        run: |
          BRANCH_NAME=${GITHUB_REF#refs/heads/}
          VERSION=${BRANCH_NAME#release/}
          echo "version=$VERSION" >> $GITHUB_OUTPUT
      - name: Compute image reference
        id: image
        run: |
          SHORT_SHA=${GITHUB_SHA::7}
          IMAGE="${{ env.ACR_LOGIN_SERVER }}/${{ env.APP_NAME }}:${{ steps.extract_version.outputs.version }}-${SHORT_SHA}"
          echo "image=$IMAGE" >> $GITHUB_OUTPUT
      - name: Azure Login (OIDC)
        uses: azure/login@v2
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
      - name: Log in to ACR
        run: az acr login --name ${{ env.ACR_NAME }}
      - name: Build and push Docker image
        run: |
          docker build -t ${{ steps.image.outputs.image }} .
          docker push ${{ steps.image.outputs.image }}
  # -------------------------
  # 2. DEPLOY TO UAT (AUTO)
  # -------------------------
  deploy-uat:
    name: Deploy to UAT
    needs: build
    runs-on: ubuntu-latest
    environment: uat   # no approval
    env:
      RESOURCE_GROUP: rg-uat
      CONTAINER_APP_NAME: my-app-uat
      CONTAINER_APP_ENV: cae-uat
    steps:
      - name: Azure Login (OIDC)
        uses: azure/login@v2
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
      - name: Deploy to Azure Container Apps (UAT)
        uses: azure/container-apps-deploy-action@v2
        with:
          resourceGroup: ${{ env.RESOURCE_GROUP }}
          containerAppName: ${{ env.CONTAINER_APP_NAME }}
          containerAppEnvironment: ${{ env.CONTAINER_APP_ENV }}
          imageToDeploy: ${{ needs.build.outputs.image }}
          targetPort: ${{ env.TARGET_PORT }}
          ingress: external
      - name: Smoke Test (UAT)

HBPP - Hotfix branch - Pre-Production, Production

name: Hotfix branch → Pre-production → Prod

on:
  push:
    branches:
      - 'hotfix/*'   # ONLY triggers on hotfix branches

permissions:
  contents: write   # needed for tagging
  id-token: write   # needed for Azure OIDC login

env:
  APP_NAME: my-app
  ACR_NAME: myacr                       # without .azurecr.io
  ACR_LOGIN_SERVER: myacr.azurecr.io
  TARGET_PORT: 8080

jobs:
  # -------------------------
  # 1. BUILD ONCE & PUSH IMAGE
  # -------------------------
  build-and-test-hotfix:
    name: Build & Verify Hotfix
    runs-on: ubuntu-latest
    outputs:
      version: ${{ steps.extract_version.outputs.version }}
      image: ${{ steps.image.outputs.image }}
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Extract hotfix version from branch
        id: extract_version
        run: |
          BRANCH_NAME=${GITHUB_REF#refs/heads/}
          VERSION=${BRANCH_NAME#hotfix/}
          echo "version=$VERSION" >> $GITHUB_OUTPUT

      - name: Run hotfix tests
        run: echo "Running unit tests for hotfix..."

      - name: Compute image reference
        id: image
        run: |
          SHORT_SHA=${GITHUB_SHA::7}
          IMAGE="${{ env.ACR_LOGIN_SERVER }}/${{ env.APP_NAME }}:hotfix-${{ steps.extract_version.outputs.version }}-${SHORT_SHA}"
          echo "image=$IMAGE" >> $GITHUB_OUTPUT

      - name: Azure Login (OIDC)
        uses: azure/login@v2
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

      - name: Log in to ACR
        run: az acr login --name ${{ env.ACR_NAME }}

      - name: Build and push Docker image
        run: |
          docker build -t ${{ steps.image.outputs.image }} .
          docker push ${{ steps.image.outputs.image }}

  # -------------------------
  # 2. DEPLOY TO PRE-PROD (MANUAL APPROVAL)
  # -------------------------
  deploy-preprod:
    name: Deploy to Pre-Prod
    needs: build-and-test-hotfix
    runs-on: ubuntu-latest
    environment: preprod   # approval in GitHub UI
    env:
      RESOURCE_GROUP: rg-preprod
      CONTAINER_APP_NAME: my-app-preprod
      CONTAINER_APP_ENV: cae-preprod
    steps:
  1. Agile Delivery
    1. Product Requirement Template
    2. Documenting Product Requirements
    3. Estimation & Sizing
  2. DevOps
    1. Gitlab Flow with Release Branches
    2. DevOps Pipelines
  3. Software Architecture Design
    1. Agent Engineering
    2. Logging
  4. Tech Stack
  5. Testing & Quality
  6. Observability & Operations

Clone this wiki locally