Back to Blog
Lesson 59 of the CI/CD: Continuous Integration from Scratch course
DevOpsSeptember 19, 20265 min read

Capstone: The Full CI/CD Pipeline in GitHub Actions

Master the final capstone project of our DevOps course. Review and assemble all learned components into a professional-grade, multi-stage CI/CD pipeline.

ci-cdgithub-actionscapstonedevopspipelineautomation
Close-up of colorful programming code on a computer screen, showcasing digital technology.

Previously in this course, we explored advanced troubleshooting techniques in Pipeline Resilience: Configuring Retries, Timeouts, and Error Handling. Now, we bring everything together in our final capstone.

Over the course of this curriculum, we've built our DevOps knowledge from local Git configurations and basic YAML syntax up to secure secrets management, container builds, staging environments, and production deployments. In this lesson, we consolidate all those concepts into a single, cohesive, professional-grade YAML workflow file. You'll review how each piece fits into an end-to-end delivery lifecycle and deploy a complete pipeline from code commit to production release.

Anatomy of the Capstone Pipeline

A professional-grade pipeline isn't just a linear sequence of shell commands; it's a multi-job, secure orchestration engine. It protects your codebase, validates quality, builds artifacts, runs security scans, and promotes code through environments with manual safety gates.

Let's review the architecture of our final capstone workflow. It consists of four distinct, interconnected phases:

  1. Validation & Quality Gate: Runs tests, checks code style, and scans dependencies.
  2. Container Build & Registry Push: Packages the application into a container image and pushes it to a secure registry.
  3. Staging Deployment: Automatically deploys the image to a staging environment for integration testing.
  4. Production Deployment: Requires manual approval before safely rolling out updates to production.

Here is how these phases interact structurally:

Flow diagram: Code Push / PR → Validate: Test & Lint; Validate: Test & Lint → Build: Docker Image; Build: Docker Image → Deploy to Staging; Deploy to Staging → Manual Approval Gate; Manual Approval Gate → Deploy to Production

To understand the foundational principles behind this multi-stage approach, revisit our early discussions in CI/CD Pipeline Fundamentals: Automating Deployment and Testing and Setting Up a CI Pipeline: Automating Your Testing Workflow.

Worked Example: The Unified Workflow File

A focused professional man in an office exchanging documents while working late.

Below is the complete capstone workflow configuration. Save this file as .github/workflows/capstone-pipeline.yml in your repository. It integrates automated testing, container building, staging releases, and manual production gates into one unified script.

YAML
name: Capstone Production Pipeline

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

jobs:
  validate:
    name: Lint and Test
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'
          cache: 'pip'

      - name: Install Dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
          pip install flake8 pytest

      - name: Run Linter
        run: flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics

      - name: Run Unit Tests
        run: pytest

  build-and-push:
    name: Containerize and Push
    needs: validate
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4

      - name: Log in to Container Registry
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Build and Push Docker Image
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: user/my-app:${{ github.sha }},user/my-app:latest

  deploy-staging:
    name: Deploy to Staging
    needs: build-and-push
    runs-on: ubuntu-latest
    environment: staging
    steps:
      - name: Trigger Staging Webhook
        run: |
          echo "Deploying container tag ${{ github.sha }} to staging server..."
          # Insert your remote deployment trigger script here

  deploy-production:
    name: Deploy to Production
    needs: deploy-staging
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: Trigger Production Webhook
        run: |
          echo "Promoting container tag ${{ github.sha }} to production environment..."
          # Insert your secure production release script here

Hands-on Exercise

It's time to put your skills to the test and finalize your running project repository.

  1. Open your repository workspace and ensure your application code contains a basic test suite and a valid Dockerfile.
  2. Create the .github/workflows/capstone-pipeline.yml file using the YAML snippet provided above, updating the Docker image tags to match your own registry username.
  3. Configure your repository secrets (DOCKER_USERNAME and DOCKER_PASSWORD) under Settings > Secrets and variables > Actions.
  4. Set up a GitHub Environment named production with required reviewers enabled to practice manual approval gates.
  5. Push your changes to the main branch and monitor the multi-job execution in the GitHub Actions UI.

Common Pitfalls

Even seasoned engineers run into hurdles when assembling large pipelines. Keep these production lessons in mind:

  • Job Dependency Mismatches: If a job uses the needs keyword, make sure the referenced job name matches exactly (case-sensitive string), not the display name.
  • Secret Scoping Failures: Forgetting to define environment-specific protection rules will cause deployments to skip your manual approval gates.
  • Uncached Dependencies: Omitting caching mechanisms for pip, npm, or Docker layers can unnecessarily bloat your pipeline execution times.

Frequently Asked Questions

What happens if the validate job fails?

Because build-and-push and subsequent deployment jobs depend on validate via the needs keyword, GitHub Actions will automatically skip all downstream jobs, protecting your environments from broken code.

Can I run tests and lints in parallel instead of sequentially?

Yes. You can split linting and unit testing into two separate jobs without a needs relationship between them, allowing them to execute concurrently on separate runners to reduce total pipeline duration.

Recap

Team members presenting a project in a modern office setting with a focus on collaboration.

In this final lesson, we reviewed and assembled our entire DevOps curriculum into a professional-grade, multi-stage CI/CD pipeline. You've learned how to orchestrate validation, containerization, staging releases, and production deployments with absolute confidence. Treat your pipelines as production-grade code, maintain strict security boundaries, and keep iterating on your automation workflows.

Up next: Now that you've completed the entire curriculum, apply your newfound expertise to automate, secure, and scale real-world applications across your engineering organization.

Similar Posts