Hands-on Lab

#Lab: Advanced GitHub Actions Pipeline

Build a production-ready CI/CD pipeline with GitHub Actions.

#🎯 Objectives

  • Create multi-stage deployment pipeline
  • Implement matrix builds for multiple environments
  • Set up environment protection rules
  • Use reusable workflows

#📋 Prerequisites

  • GitHub repository
  • Basic GitHub Actions knowledge

#⏱️ Duration: 45 minutes


#Task 1: Multi-Stage Pipeline (15 min)

Create .github/workflows/production-pipeline.yml:

yaml
1name: Production Pipeline
2
3on:
4  push:
5    branches: [main, develop]
6  pull_request:
7    branches: [main]
8
9env:
10  REGISTRY: ghcr.io
11  IMAGE_NAME: ${{ github.repository }}
12
13jobs:
14  # Stage 1: Build and Test
15  test:
16    runs-on: ubuntu-latest
17    steps:
18      - uses: actions/checkout@v4
19      
20      - name: Setup Node.js
21        uses: actions/setup-node@v4
22        with:
23          node-version: '20'
24          cache: 'npm'
25      
26      - name: Install dependencies
27        run: npm ci
28      
29      - name: Run tests
30        run: npm test
31      
32      - name: Run linting
33        run: npm run lint
34
35  # Stage 2: Build Docker Image
36  build:
37    needs: test
38    runs-on: ubuntu-latest
39    outputs:
40      image-tag: ${{ steps.meta.outputs.tags }}
41    steps:
42      - uses: actions/checkout@v4
43      
44      - name: Set up Docker Buildx
45        uses: docker/setup-buildx-action@v3
46      
47      - name: Login to Container Registry
48        uses: docker/login-action@v3
49        with:
50          registry: ${{ env.REGISTRY }}
51          username: ${{ github.actor }}
52          password: ${{ secrets.GITHUB_TOKEN }}
53      
54      - name: Extract metadata
55        id: meta
56        uses: docker/metadata-action@v5
57        with:
58          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
59          tags: |
60            type=sha,prefix=
61            type=ref,event=branch
62      
63      - name: Build and push
64        uses: docker/build-push-action@v5
65        with:
66          context: .
67          push: true
68          tags: ${{ steps.meta.outputs.tags }}
69          cache-from: type=gha
70          cache-to: type=gha,mode=max
71
72  # Stage 3: Deploy to Staging
73  deploy-staging:
74    needs: build
75    runs-on: ubuntu-latest
76    environment: staging
77    steps:
78      - name: Deploy to staging
79        run: |
80          echo "Deploying to staging..."
81          # kubectl set image deployment/app app=${{ needs.build.outputs.image-tag }}
82
83  # Stage 4: Deploy to Production
84  deploy-production:
85    needs: [build, deploy-staging]
86    runs-on: ubuntu-latest
87    environment: production
88    if: github.ref == 'refs/heads/main'
89    steps:
90      - name: Deploy to production
91        run: |
92          echo "Deploying to production..."

#Task 2: Matrix Builds (10 min)

yaml
1name: Matrix Build
2
3on: push
4
5jobs:
6  test:
7    runs-on: ${{ matrix.os }}
8    strategy:
9      matrix:
10        os: [ubuntu-latest, windows-latest, macos-latest]
11        node-version: [18, 20, 21]
12        exclude:
13          - os: windows-latest
14            node-version: 21
15    
16    steps:
17      - uses: actions/checkout@v4
18      
19      - name: Use Node.js ${{ matrix.node-version }}
20        uses: actions/setup-node@v4
21        with:
22          node-version: ${{ matrix.node-version }}
23      
24      - run: npm ci
25      - run: npm test

#Task 3: Reusable Workflow (10 min)

Create .github/workflows/deploy-template.yml:

yaml
1name: Reusable Deploy
2
3on:
4  workflow_call:
5    inputs:
6      environment:
7        required: true
8        type: string
9      image-tag:
10        required: true
11        type: string
12    secrets:
13      DEPLOY_TOKEN:
14        required: true
15
16jobs:
17  deploy:
18    runs-on: ubuntu-latest
19    environment: ${{ inputs.environment }}
20    steps:
21      - name: Deploy
22        run: |
23          echo "Deploying ${{ inputs.image-tag }} to ${{ inputs.environment }}"

Call it from another workflow:

yaml
1jobs:
2  deploy:
3    uses: ./.github/workflows/deploy-template.yml
4    with:
5      environment: staging
6      image-tag: myapp:latest
7    secrets:
8      DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}

#Task 4: Conditional Jobs (10 min)

yaml
1jobs:
2  changes:
3    runs-on: ubuntu-latest
4    outputs:
5      backend: ${{ steps.filter.outputs.backend }}
6      frontend: ${{ steps.filter.outputs.frontend }}
7    steps:
8      - uses: actions/checkout@v4
9      - uses: dorny/paths-filter@v2
10        id: filter
11        with:
12          filters: |
13            backend:
14              - 'backend/**'
15            frontend:
16              - 'frontend/**'
17
18  backend:
19    needs: changes
20    if: ${{ needs.changes.outputs.backend == 'true' }}
21    runs-on: ubuntu-latest
22    steps:
23      - run: echo "Backend changed"
24
25  frontend:
26    needs: changes
27    if: ${{ needs.changes.outputs.frontend == 'true' }}
28    runs-on: ubuntu-latest
29    steps:
30      - run: echo "Frontend changed"

#✅ Success Criteria

  • Multi-stage pipeline created
  • Matrix builds working
  • Reusable workflow implemented
  • Conditional jobs based on path changes

#🔗 Resources