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.
- 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 Never log secrets:
- name: Deploy
run: deploy.sh
env:
SECRET_KEY: ${{ secrets.SECRET_KEY }}
# Never: run: echo ${{ secrets.SECRET_KEY }}
- 1 Use environment protection:
jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: production
url: https://prod.example.com
- 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 Limit permissions:
permissions:
contents: read
pull-requests: write
Performance Optimization
- 1 Use caching:
- uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
- 1 Run jobs in parallel:
jobs:
test:
# Runs in parallel
lint:
# Runs in parallel
deploy:
needs: [test, lint] # Waits for both
- 1 Use matrix efficiently:
strategy:
fail-fast: true # Stop all jobs if one fails
max-parallel: 3 # Limit concurrent jobs
matrix:
# ...
Practice Scenarios
Master CI/CD automation with hands-on scenarios.
FAQ
GitHub offers GitHub Actions training but no formal certification exam yet. This guide prepares you for practical GitHub Actions mastery.
Free for public repositories. Private repos get 2,000 minutes/month free, then pay-per-use.
Yes, you can integrate with Jenkins, CircleCI, Travis CI, etc., or migrate workflows gradually.
GitHub Actions is GitHub-native with better integration. Azure Pipelines offers more enterprise features and Azure integration.
Use actions/upload-artifact for logs, enable debug logging with secrets ACTIONS_STEP_DEBUG, and use tmate action for SSH access.
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!
CertPractice Team
Expert certification guides and study tips