cloud GitHub

GitHub Actions Certification: CI/CD Automation Mastery

Master GitHub Actions for CI/CD automation. Workflows, runners, marketplace actions, secrets management, and DevOps automation with GitHub's native platform.

calendar_today December 29, 2025 schedule 17 min read person CertPractice Team

GitHub Actions Certification Overview

GitHub Actions has become the de facto CI/CD solution for projects hosted on GitHub, powering millions of workflows daily. This certification (based on GitHub's official curriculum) validates your ability to automate software workflows, build CI/CD pipelines, and leverage GitHub's ecosystem for DevOps automation.

lightbulb
Who Should Master GitHub Actions?
  • Developers automating build and test workflows
  • DevOps engineers implementing CI/CD pipelines
  • Platform engineers building reusable actions
  • Open source maintainers automating releases
  • Anyone using GitHub for software development

GitHub Actions Fundamentals

What Are GitHub Actions?

GitHub Actions is GitHub's native automation platform that allows you to:

  • Automate software workflows
  • Build, test, and deploy code
  • Respond to GitHub events (push, pull requests, issues)
  • Orchestrate complex workflows
  • Integrate with thousands of tools

Core Concepts

Workflows: YAML files defining automation in .github/workflows/ directory

Events: GitHub events that trigger workflows (push, pull_request, schedule, etc.)

Jobs: Sets of steps that execute on the same runner

Steps: Individual tasks within a job (run commands or actions)

Actions: Reusable units of code (from marketplace or custom)

Runners: Servers that execute workflows (GitHub-hosted or self-hosted)

Workflow Syntax and Structure

Basic Workflow Example

name: CI Pipeline

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

jobs:
  build:
    runs-on: ubuntu-latest
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
      
    - name: Set up Node.js
      uses: actions/setup-node@v4
      with:
        node-version: '18'
        
    - name: Install dependencies
      run: npm ci
      
    - name: Run tests
      run: npm test
      
    - name: Build
      run: npm run build

Workflow Triggers (Events)

Push Events:

on:
  push:
    branches:
      - main
      - 'releases/**'
    tags:
      - v*
    paths:
      - 'src/**'
      - '!**.md'

Pull Request Events:

on:
  pull_request:
    types: [opened, synchronize, reopened]
    branches: [ main ]

Schedule (Cron):

on:
  schedule:
    - cron: '0 0 * * *'  # Daily at midnight
    - cron: '0 */6 * * *'  # Every 6 hours

Manual Trigger (workflow_dispatch):

on:
  workflow_dispatch:
    inputs:
      environment:
        description: 'Environment to deploy'
        required: true
        type: choice
        options:
          - development
          - staging
          - production
      version:
        description: 'Version to deploy'
        required: true
        default: 'latest'

Multiple Events:

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]
  release:
    types: [published]
  workflow_dispatch:

Advanced Workflow Patterns

Matrix Strategy

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        node-version: [16, 18, 20]
        include:
          - os: ubuntu-latest
            node-version: 18
            is-primary: true
        exclude:
          - os: macos-latest
            node-version: 16
    
    steps:
    - uses: actions/checkout@v4
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v4
      with:
        node-version: ${{ matrix.node-version }}
    - run: npm ci
    - run: npm test

Conditional Execution

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    
    - name: Run only on main branch
      if: github.ref == 'refs/heads/main'
      run: echo "This is the main branch"
      
    - name: Run on pull request
      if: github.event_name == 'pull_request'
      run: echo "This is a pull request"
      
    - name: Run if previous step succeeded
      if: success()
      run: echo "Previous step succeeded"
      
    - name: Run even if previous failed
      if: always()
      run: echo "This always runs"
      
    - name: Run only on failure
      if: failure()
      run: echo "Something failed"

Reusable Workflows

Reusable workflow (.github/workflows/reusable-deploy.yml):

name: Reusable Deploy

on:
  workflow_call:
    inputs:
      environment:
        required: true
        type: string
      version:
        required: false
        type: string
        default: 'latest'
    secrets:
      deploy-token:
        required: true
    outputs:
      deployment-url:
        description: "Deployment URL"
        value: ${{ jobs.deploy.outputs.url }}

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: ${{ inputs.environment }}
    outputs:
      url: ${{ steps.deploy.outputs.url }}
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Deploy
      id: deploy
      run: |
        echo "Deploying ${{ inputs.version }} to ${{ inputs.environment }}"
        echo "url=https://${{ inputs.environment }}.example.com" >> $GITHUB_OUTPUT
      env:
        DEPLOY_TOKEN: ${{ secrets.deploy-token }}

Calling reusable workflow:

name: Deploy to Production

on:
  release:
    types: [published]

jobs:
  deploy-prod:
    uses: ./.github/workflows/reusable-deploy.yml
    with:
      environment: production
      version: ${{ github.event.release.tag_name }}
    secrets:
      deploy-token: ${{ secrets.PROD_DEPLOY_TOKEN }}

Composite Actions

Custom composite action (.github/actions/setup-app/action.yml):

name: 'Setup Application'
description: 'Sets up Node.js, installs dependencies, and caches'

inputs:
  node-version:
    description: 'Node.js version'
    required: false
    default: '18'

outputs:
  cache-hit:
    description: 'Whether dependencies were cached'
    value: ${{ steps.cache.outputs.cache-hit }}

runs:
  using: "composite"
  steps:
    - name: Setup Node.js
      uses: actions/setup-node@v4
      with:
        node-version: ${{ inputs.node-version }}
    
    - name: Cache dependencies
      id: cache
      uses: actions/cache@v3
      with:
        path: ~/.npm
        key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
    
    - name: Install dependencies
      if: steps.cache.outputs.cache-hit != 'true'
      run: npm ci
      shell: bash

Using composite action:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - uses: ./.github/actions/setup-app
      with:
        node-version: '20'

Secrets and Environment Variables

Using Secrets

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - name: Deploy with secrets
      run: |
        echo "Deploying..."
        deploy.sh
      env:
        AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
        AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        DATABASE_URL: ${{ secrets.DATABASE_URL }}

Environment Variables

env:
  # Workflow-level environment variables
  NODE_ENV: production
  API_VERSION: v2

jobs:
  build:
    runs-on: ubuntu-latest
    env:
      # Job-level environment variables
      BUILD_CONFIG: release
    
    steps:
    - name: Use environment variables
      env:
        # Step-level environment variables
        DEPLOY_TARGET: production
      run: |
        echo "NODE_ENV: $NODE_ENV"
        echo "BUILD_CONFIG: $BUILD_CONFIG"
        echo "DEPLOY_TARGET: $DEPLOY_TARGET"

GitHub Context

jobs:
  context-example:
    runs-on: ubuntu-latest
    steps:
    - name: Print context information
      run: |
        echo "Repository: ${{ github.repository }}"
        echo "Ref: ${{ github.ref }}"
        echo "SHA: ${{ github.sha }}"
        echo "Actor: ${{ github.actor }}"
        echo "Event name: ${{ github.event_name }}"
        echo "Run ID: ${{ github.run_id }}"
        echo "Run number: ${{ github.run_number }}"

Artifacts and Caching

Upload and Download Artifacts

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - run: npm ci
    - run: npm run build
    
    - name: Upload build artifacts
      uses: actions/upload-artifact@v4
      with:
        name: build-output
        path: dist/
        retention-days: 7
  
  test:
    needs: build
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    
    - name: Download build artifacts
      uses: actions/download-artifact@v4
      with:
        name: build-output
        path: dist/
    
    - run: npm test

Dependency Caching

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    
    - name: Cache Node modules
      uses: actions/cache@v3
      with:
        path: ~/.npm
        key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
        restore-keys: |
          ${{ runner.os }}-node-
    
    - run: npm ci
    - run: npm run build

Self-Hosted Runners

Setting Up Self-Hosted Runners

Benefits:

  • Full control over environment
  • Access to private networks
  • Custom hardware/software
  • Cost savings for high-volume workloads
  • Faster builds with persistent caching

Using Self-Hosted Runner:

jobs:
  build:
    runs-on: self-hosted
    # Or with labels
    runs-on: [self-hosted, linux, x64, gpu]
    
    steps:
    - uses: actions/checkout@v4
    - run: ./build.sh

Common Workflow Patterns

Node.js CI/CD

name: Node.js CI/CD

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

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [16, 18, 20]
    
    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
      with:
        node-version: ${{ matrix.node-version }}
        cache: 'npm'
    - run: npm ci
    - run: npm run lint
    - run: npm test
    - run: npm run build

  deploy:
    needs: test
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
      with:
        node-version: '18'
    - run: npm ci
    - run: npm run build
    - name: Deploy to production
      run: npm run deploy
      env:
        DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}

Docker Build and Push

name: Docker Build and Push

on:
  push:
    branches: [ main ]
    tags: [ 'v*' ]

jobs:
  docker:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    
    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v3
    
    - name: Login to Docker Hub
      uses: docker/login-action@v3
      with:
        username: ${{ secrets.DOCKER_USERNAME }}
        password: ${{ secrets.DOCKER_TOKEN }}
    
    - name: Extract metadata
      id: meta
      uses: docker/metadata-action@v5
      with:
        images: username/app
        tags: |
          type=ref,event=branch
          type=semver,pattern={{version}}
          type=semver,pattern={{major}}.{{minor}}
    
    - name: Build and push
      uses: docker/build-push-action@v5
      with:
        context: .
        push: true
        tags: ${{ steps.meta.outputs.tags }}
        cache-from: type=registry,ref=username/app:buildcache
        cache-to: type=registry,ref=username/app:buildcache,mode=max

Best Practices

Security Best Practices

  1. 1 Never log secrets:
- name: Deploy
  run: deploy.sh
  env:
    SECRET_KEY: ${{ secrets.SECRET_KEY }}
  # Never: run: echo ${{ secrets.SECRET_KEY }}
  1. 1 Use environment protection:
jobs:
  deploy:
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://prod.example.com
  1. 1 Pin action versions:
# Good - specific commit SHA
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab

# Better - tagged version
- uses: actions/[email protected]

# Avoid - mutable tag
- uses: actions/checkout@v4
  1. 1 Limit permissions:
permissions:
  contents: read
  pull-requests: write

Performance Optimization

  1. 1 Use caching:
- uses: actions/cache@v3
  with:
    path: ~/.npm
    key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
  1. 1 Run jobs in parallel:
jobs:
  test:
    # Runs in parallel
  lint:
    # Runs in parallel
  deploy:
    needs: [test, lint]  # Waits for both
  1. 1 Use matrix efficiently:
strategy:
  fail-fast: true  # Stop all jobs if one fails
  max-parallel: 3  # Limit concurrent jobs
  matrix:
    # ...

Practice Scenarios

FAQ

quizFrequently Asked Questions
Q
Is there an official GitHub Actions certification?

GitHub offers GitHub Actions training but no formal certification exam yet. This guide prepares you for practical GitHub Actions mastery.

Q
How much does GitHub Actions cost?

Free for public repositories. Private repos get 2,000 minutes/month free, then pay-per-use.

Q
Can I use GitHub Actions with other CI/CD tools?

Yes, you can integrate with Jenkins, CircleCI, Travis CI, etc., or migrate workflows gradually.

Q
What's the difference between GitHub Actions and Azure Pipelines?

GitHub Actions is GitHub-native with better integration. Azure Pipelines offers more enterprise features and Azure integration.

Q
How do I debug failed workflows?

Use actions/upload-artifact for logs, enable debug logging with secrets ACTIONS_STEP_DEBUG, and use tmate action for SSH access.

Q
Can I run GitHub Actions on-premises?

Yes, with self-hosted runners on your own infrastructure.


GitHub Actions Mastery:

  • Practice building real workflows
  • Master YAML syntax
  • Understand event triggers
  • Leverage marketplace actions
  • Implement security best practices

GitHub Actions is the future of CI/CD for GitHub-hosted projects. Master it to automate everything from testing to deployment!

group

CertPractice Team

Expert certification guides and study tips

Ready to Get GitHub Certified?

Start practicing with our GitHub-Actions question bank. Get instant feedback and track your progress.