GitHub Actions Certification

Continuous Deployment with GitHub Actions

If Expressions and Pull Request

In this guide, we’ll set up a GitHub Actions CI/CD workflow that uses conditional if expressions to deploy an application to development on feature branches and to production only from the main branch. Pull requests will gate production releases behind review and environment protection rules.

Table of Contents

Environment Variables

Define shared environment variables at the top of your workflow file:

env:
  MONGO_URI: 'mongodb+srv://supercluster.d83jj.mongodb.net/superData'
  MONGO_USERNAME: ${{ vars.MONGO_USERNAME }}
  MONGO_PASSWORD: ${{ secrets.MONGO_PASSWORD }}

Warning

Always store sensitive data such as database credentials and API keys in GitHub Secrets or Variables.

Job Overview

Below is a summary of each job, its trigger condition, and dependencies:

Job NameConditionNeedsDescription
dockerAlways (push or PR)noneBuilds and pushes Docker image
dev-deploycontains(github.ref, 'feature/')dockerDeploys to the development environment
dev-integration-testingcontains(github.ref, 'feature/')dev-deployRuns health checks against the dev deployment
prod-deploygithub.ref == 'refs/heads/main'dockerDeploys to the production environment
prod-integration-testinggithub.ref == 'refs/heads/main'prod-deployValidates the live production endpoint

Dev Deploy Job

The dev-deploy job runs on any branch matching feature/. It depends on the docker job:

jobs:
  dev-deploy:
    if: contains(github.ref, 'feature/')
    needs: docker
    environment:
      name: development
      url: https://${{ steps.set-ingress-host-address.outputs.APP_INGRESS_HOST }}
    outputs:
      APP_INGRESS_URL: ${{ steps.set-ingress-host-address.outputs.APP_INGRESS_HOST }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Repo
        uses: actions/checkout@v4

      # Add Kubernetes deployment steps here, for example:
      # - name: Apply Manifests
      #   run: kubectl apply -f kubernetes/development

Note

The contains function evaluates whether the branch ref string includes feature/. See GitHub Actions expressions for more.

Dev Integration Testing Job

After deployment to development, run integration tests to verify application health:

  dev-integration-testing:
    name: Dev Integration Testing
    if: contains(github.ref, 'feature/')
    needs: dev-deploy
    runs-on: ubuntu-latest
    steps:
      - name: Test Application Health
        run: |
          curl https://${{ needs.dev-deploy.outputs.APP_INGRESS_URL }}/health

Prod Deploy Job

Production deployments trigger only on the main branch. This job also sets up Kubernetes credentials and applies manifests:

  prod-deploy:
    if: github.ref == 'refs/heads/main'
    needs: docker
    environment:
      name: production
      url: https://${{ steps.set-ingress-host-address.outputs.APP_INGRESS_HOST }}
    outputs:
      APP_INGRESS_URL: ${{ steps.set-ingress-host-address.outputs.APP_INGRESS_HOST }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Repo
        uses: actions/checkout@v4

      - name: Set up Kubernetes Credentials
        uses: azure/setup-kubectl@v3
        with:
          method: kubeconfig
          kubeconfig: ${{ secrets.KUBECONFIG }}

      - name: Fetch Kubernetes Cluster Details
        run: |
          kubectl version --short
          kubectl get nodes

      - name: Save Nginx Ingress IP
        id: set-ingress-host
        run: |
          echo "APP_INGRESS_HOST=$(kubectl -n ingress-nginx get service ingress-nginx-controller \
            -o jsonpath='{.status.loadBalancer.ingress[0].ip}')"
          echo "::set-output name=APP_INGRESS_HOST::$APP_INGRESS_HOST"

      - name: Replace Tokens in Manifests
        uses: cscheidlen/replace-tokens@v1
        with:
          tokenPrefix: '_{'
          tokenSuffix: '}_'
          files: ["kubernetes/production/*.yaml"]
        env:
          NAMESPACE: ${{ vars.NAMESPACE }}
          REPLICAS: ${{ vars.REPLICAS }}
          IMAGE: ${{ secrets.DOCKERHUB_USERNAME }}/solar-system:${{ github.sha }}
          INGRESS_IP: ${{ steps.set-ingress-host.outputs.APP_INGRESS_HOST }}

      - name: Deploy to Production
        run: kubectl apply -f kubernetes/production

Prod Integration Testing Job

Once production is deployed, run a final health check:

  prod-integration-testing:
    name: Prod Integration Testing
    if: github.ref == 'refs/heads/main'
    needs: prod-deploy
    runs-on: ubuntu-latest
    steps:
      - name: Validate Production Health
        env:
          URL: ${{ needs.prod-deploy.outputs.APP_INGRESS_URL }}
        run: |
          echo "Testing URL: $URL"
          curl https://$URL/live -s -k | jq -r .status | grep -i live

Workflow Execution & Pull Request Flow

  1. Feature Branch Push
    • docker builds and pushes the image.
    • dev-deploy and dev-integration-testing run automatically.
    • Production jobs are skipped on feature branches.

The image shows a GitHub Actions workflow interface for a project named "solar-system," displaying a workflow in progress with steps like unit testing, containerization, and deployment.

  1. Review Feature Deployments
    • The workflow summary marks all dev jobs as successful.

The image shows a GitHub Actions workflow summary with various jobs like unit testing, containerization, and deployment, all marked as successful. It includes a visual representation of the workflow steps and deployment protection rules.

  1. Open Pull Request
    • Create a PR from feature/* into main to prepare a production release.

The image shows a GitHub interface where a user is creating a pull request to merge changes from a feature branch into the main branch, with a description about adding GitHub Actions workflows for CI/CD automation.

  1. Confirm Previous Deployments
    • The PR page lists all commits and verifies the dev deployment.

The image shows a GitHub pull request page with a list of commits, most of which are verified, and a notification that the branch was successfully deployed.

  1. Merge to Main
    • Merging triggers a new workflow: dev jobs skip, prod job awaits manual approval or timer.

The image shows a GitHub Actions workflow interface for a project named "solar-system," displaying the status of various jobs like unit testing, code coverage, and deployment processes. The workflow is waiting for a review to deploy to production.

  1. Approve or Reject
    • A reviewer approves the production deployment via the Actions UI.

The image shows a GitHub Actions interface with a pending deployment review for a production environment. There are options to reject or approve and deploy the changes.

  1. Production Deployment Complete
    • Once approved, the production deployment proceeds and can be monitored in the Deployments UI.

This image shows a GitHub deployment page for a project named "solar-system," displaying active deployments and a list of recent deployment activities.

By leveraging conditional if expressions, pull requests, and environment protection rules, you can build a robust, secure CI/CD pipeline that separates development and production deployments seamlessly.

Watch Video

Watch video content

Previous
Workflow Deploy to Kubernetes Prod Environment