This comprehensive suite contains six progressive practicals that build from fundamental concepts to production-grade CI/CD pipelines. The suite is designed for polytechnic students progressing from beginner to intermediate/advanced levels over a 6-12 week period.
Duration: 2 hours | Level: Beginner
- Understand core GitHub Actions concepts
- Create and execute a simple workflow
- Interpret workflow logs
- Understand YAML syntax fundamentals
- GitHub account (free tier)
- A repository (or ability to create one)
- Basic Git knowledge
GitHub Actions is a CI/CD platform that allows you to automate your workflow directly in your GitHub repository. Automation happens through events—when something happens in your repository (push, pull request, etc.), a workflow automatically triggers.
| Term | Definition | Example |
|---|---|---|
| Workflow | An automated process defined in YAML | File: .github/workflows/build.yml |
| Event | Activity that triggers a workflow | push, pull_request, schedule |
| Job | A set of steps running on same runner | Build job, Test job |
| Step | Individual task within a job | Run test, Deploy application |
| Action | Reusable code unit performing a task | actions/checkout@v4 |
| Runner | Virtual machine executing the workflow | Ubuntu, Windows, macOS |
| Artifact | Files generated during workflow | Build output, test reports |
Steps:
- Create a new repository called
github-actions-learning - Navigate to the Actions tab
- Click set up a workflow yourself
- Create a file named
hello-world.ymlwith the following content:
name: Hello World Workflow
on: push
jobs:
hello-job:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Print greeting
run: echo "Hello from GitHub Actions!"
- name: Print system info
run: |
echo "Operating System: $(uname -s)"
echo "Architecture: $(uname -m)"
echo "Current time: $(date)"- Commit the file
- Go to Actions tab and observe the workflow execution
- Click on the completed workflow to view logs
Key concepts:
- Indentation matters: Use 2 spaces (never tabs)
- Colons (
:): Separate keys from values - Hyphens (
-): Indicate list items - Pipes (
|): Multi-line string values - Comments: Lines starting with
#
Example with syntax annotations:
name: Syntax Example # String value after colon
on: push # Event trigger
jobs: # Start of jobs section (key with no value)
demo-job: # Job name
runs-on: ubuntu-latest # Configuration key-value
steps: # List of steps
- name: First step # List item with name key
run: echo "Step 1"
- name: Multi-line step
run: | # Pipe for multi-line
echo "Line 1"
echo "Line 2"
echo "Line 3"When you click a completed workflow, you see:
- Workflow Summary - Overall status, duration, and trigger info
- Jobs Section - List of all jobs with individual statuses
- Step Details - Expandable sections showing each step's output
| Symbol | Meaning | Color |
|---|---|---|
| ✓ | Step succeeded | Green |
| ✗ | Step failed | Red |
| ⊘ | Step skipped | Gray |
| ⚠ | Warning/notice | Yellow |
- Create a workflow with intentional errors:
name: Debug Practice
on: push
jobs:
debug-job:
runs-on: ubuntu-latest
steps:
- name: Success step
run: echo "This will succeed"
- name: List directory
run: ls -la
- name: Show environment
run: |
echo "User: $(whoami)"
echo "Home: $HOME"
echo "Path: $PATH"- Commit and push
- View the logs and identify what information each step provides
- What triggers the workflow in your hello-world example?
- Name the three jobs components in GitHub Actions
- What does
uses: actions/checkout@v4do? - Why is YAML indentation important?
- How would you run multiple commands in a single step?
Duration: 2.5 hours | Level: Beginner
- Understand different event triggers
- Create workflows responding to specific events
- Use event contexts and payloads
- Filter workflows based on conditions
- Completion of Practical 1
Push Events Triggered when code is pushed to a branch. Perfect for CI tasks.
on: pushPull Request Events Triggered when a pull request is opened, edited, or synchronized.
on: pull_requestScheduled Events Triggered at specific times using cron syntax.
on:
schedule:
- cron: '0 9 * * 1' # 9 AM every Monday UTCManual Trigger Events Triggered manually from GitHub UI using workflow_dispatch.
on: workflow_dispatchCreate workflows that trigger only on specific conditions:
name: Filtered Push Events
on:
push:
branches:
- main
- develop
paths:
- 'src/**'
- '.github/workflows/**'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: echo "Changes detected in src or workflows"This workflow runs only when:
- Push happens to
mainordevelopbranch - AND changes are in
src/or.github/workflows/directories
name: PR Quality Checks
on:
pull_request:
types: [opened, synchronize, reopened]
branches:
- main
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: PR Info
run: |
echo "PR Title: ${{ github.event.pull_request.title }}"
echo "PR Author: ${{ github.event.pull_request.user.login }}"
echo "PR Number: ${{ github.event.pull_request.number }}"Key concepts:
types:specifies which PR actions trigger the workflow- Context variables like
github.event.pull_requestcontain PR data
name: Daily Health Check
on:
schedule:
- cron: '0 2 * * *' # 2 AM UTC every day
workflow_dispatch: # Also allow manual trigger
jobs:
health-check:
runs-on: ubuntu-latest
steps:
- name: Check system
run: echo "Running daily health check"
- name: Current date
run: dateCron format: minute hour day-of-month month day-of-week
Common patterns:
0 9 * * 1- 9 AM every Monday0 */6 * * *- Every 6 hours0 0 * * 0- Midnight every Sunday*/15 * * * *- Every 15 minutes
All workflows have access to the github context containing information about the triggering event:
name: Context Variables Demo
on: push
jobs:
demo:
runs-on: ubuntu-latest
steps:
- run: |
echo "Repository: ${{ github.repository }}"
echo "Event: ${{ github.event_name }}"
echo "Actor: ${{ github.actor }}"
echo "Ref: ${{ github.ref }}"
echo "Commit SHA: ${{ github.sha }}"
echo "Workspace: ${{ github.workspace }}"name: Conditional Workflows
on: push
jobs:
conditional-job:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run only on main
if: github.ref == 'refs/heads/main'
run: echo "This runs only on main branch"
- name: Run on all branches
run: echo "This always runs"
- name: Run if push (not pull request)
if: github.event_name == 'push'
run: echo "This is a push event"Create a workflow that:
- Triggers on push to main and develop branches
- Triggers on pull requests to main
- Triggers manually via workflow_dispatch
- Prints different messages based on the event type
Solution structure:
name: Multi-Event Workflow
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
workflow_dispatch:
jobs:
respond:
runs-on: ubuntu-latest
steps:
- run: |
if [ "${{ github.event_name }}" = "push" ]; then
echo "Code was pushed"
elif [ "${{ github.event_name }}" = "pull_request" ]; then
echo "Pull request detected"
elif [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "Manual trigger"
fiCreate a workflow that:
- Runs every day at 8 AM UTC
- Allows manual trigger
- Generates a simple timestamp report
Duration: 3 hours | Level: Beginner to Intermediate
- Structure complex workflows with multiple jobs
- Understand job dependencies
- Use actions from GitHub Marketplace
- Create reusable step patterns
- Completion of Practicals 1 and 2
Parallel Jobs (Default) Jobs run simultaneously:
name: Parallel Jobs
on: push
jobs:
test:
runs-on: ubuntu-latest
steps:
- run: echo "Testing"
lint:
runs-on: ubuntu-latest
steps:
- run: echo "Linting"
build:
runs-on: ubuntu-latest
steps:
- run: echo "Building"All three jobs start at roughly the same time.
Sequential Jobs with Dependencies Control job execution order:
name: Sequential Jobs
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: echo "Building"
test:
needs: build # This job waits for 'build'
runs-on: ubuntu-latest
steps:
- run: echo "Testing"
deploy:
needs: test # This job waits for 'test'
runs-on: ubuntu-latest
steps:
- run: echo "Deploying"Execution order: build → test → deploy
Actions are reusable blocks of code. They can be:
- Built by GitHub (official actions)
- Built by the community (GitHub Marketplace)
- Custom (created by you)
name: Using Official Actions
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
# Checkout action
- uses: actions/checkout@v4
# Setup Node.js
- uses: actions/setup-node@v4
with:
node-version: '18'
# Upload artifacts
- name: Create artifact
run: mkdir -p dist && echo "Build output" > dist/index.html
- uses: actions/upload-artifact@v4
with:
name: build-output
path: dist/| Action | Purpose | Example |
|---|---|---|
actions/checkout@v4 |
Clone repository | Get your code |
actions/setup-node@v4 |
Setup Node.js | Install Node.js |
actions/setup-python@v4 |
Setup Python | Install Python |
actions/upload-artifact@v4 |
Upload files | Save build artifacts |
actions/download-artifact@v4 |
Download files | Retrieve artifacts |
actions/cache@v4 |
Cache dependencies | Speed up builds |
Create a complete Node.js build workflow:
name: Node.js CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16, 18, 20]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Build application
run: npm run build
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: build-node-${{ matrix.node-version }}
path: dist/Key features:
- Matrix strategy tests multiple Node versions
- Caching speeds up npm install
- Artifacts saved for each version
name: Job Data Flow
on: push
jobs:
setup:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.extract.outputs.version }}
timestamp: ${{ steps.time.outputs.time }}
steps:
- uses: actions/checkout@v4
- id: extract
run: echo "version=1.2.3" >> $GITHUB_OUTPUT
- id: time
run: echo "time=$(date)" >> $GITHUB_OUTPUT
deploy:
needs: setup
runs-on: ubuntu-latest
steps:
- run: |
echo "Deploying version: ${{ needs.setup.outputs.version }}"
echo "Time: ${{ needs.setup.outputs.timestamp }}"Key points:
outputs:declares available outputs from a job$GITHUB_OUTPUTsets output values- Dependent jobs access outputs via
needs.jobname.outputs.variable
Test your code across multiple configurations simultaneously:
name: Matrix Testing
on: push
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.8', '3.9', '3.10', '3.11']
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- run: |
python -m pip install pytest
pytest tests/This creates 12 job combinations:
- 3 operating systems × 4 Python versions
- All run in parallel
- Faster feedback on compatibility issues
strategy:
matrix:
include:
- os: ubuntu-latest
python-version: '3.8'
experimental: false
- os: ubuntu-latest
python-version: '3.11'
experimental: true
exclude:
- os: windows-latest
python-version: '3.8'Create a workflow that:
- Checks out code
- Builds in parallel across 3 Node versions
- Runs tests after builds complete
- Uploads artifacts
Create a workflow that:
- Builds on Ubuntu, Windows, and macOS
- Runs tests on each platform
- Reports success/failure for each platform
- Creates artifacts for successful builds
Duration: 3 hours | Level: Intermediate
- Implement automated testing in workflows
- Use testing frameworks with GitHub Actions
- Create comprehensive CI pipelines
- Report test results
- Completion of Practicals 1-3
name: Node.js Testing
on: push
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
- run: npm ci
- name: Run unit tests
run: npm test -- --coverage
- name: Upload coverage
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: coverage/name: Python Testing
on: push
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11']
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- run: |
python -m pip install --upgrade pip
pip install pytest pytest-cov
pip install -r requirements.txt
- name: Run tests
run: pytest --cov=src tests/name: Java Testing
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v3
with:
java-version: '11'
distribution: 'temurin'
cache: maven
- run: mvn clean testname: Code Quality
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# JavaScript/TypeScript linting
- uses: actions/setup-node@v4
with:
node-version: '18'
- run: npm install -g eslint
- run: eslint src/ --max-warnings 0
# Python linting
- uses: actions/setup-python@v4
- run: pip install pylint
- run: pylint src/*.pyname: Super Linter
on: push
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: github/super-linter@v4
env:
DEFAULT_BRANCH: main
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}name: Complete CI Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
lint:
name: Code Quality
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm run format:check
test:
name: Unit Tests
runs-on: ubuntu-latest
needs: lint
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
- run: npm ci
- name: Run tests
run: npm test -- --coverage
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
files: ./coverage/lcov.info
build:
name: Build Artifacts
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
- run: npm ci
- run: npm run build
- uses: actions/upload-artifact@v4
with:
name: build
path: dist/
security:
name: Security Scan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: github/super-linter@v4
env:
DEFAULT_BRANCH: main
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}Pipeline flow:
- Lint runs first
- Tests run after lint passes
- Build runs after tests pass
- Security scan runs in parallel
name: Test Reporting
on: push
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '18'
- run: npm ci
- name: Run tests with reports
run: npm test -- --reporters=json --reporters=junit
continue-on-error: true
- name: Publish test results
uses: EnricoMi/publish-unit-test-result-action@v2
if: always()
with:
files: test-results.xml- name: Generate coverage
run: npm test -- --coverage --coverageReporters=json
- name: Comment coverage
uses: romeovs/lcov-reporter-action@v0.3.1
with:
lcov-file: ./coverage/lcov.infoDuration: 3.5 hours | Level: Intermediate to Advanced
- Create deployment workflows
- Implement environment-specific deployments
- Handle deployment secrets securely
- Create rollback strategies
- Completion of Practicals 1-4
name: Deployment with Secrets
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- name: Deploy with credentials
env:
API_KEY: ${{ secrets.PRODUCTION_API_KEY }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
run: |
echo "Deploying to production"
# Your deployment script hereHow to add secrets:
- Repository → Settings → Secrets and variables → Actions
- Click "New repository secret"
- Add name (e.g.,
PRODUCTION_API_KEY) and value - Reference in workflow:
${{ secrets.SECRET_NAME }}
name: Multi-Environment Deployment
on:
push:
branches:
- main
- develop
jobs:
deploy:
runs-on: ubuntu-latest
environment: ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }}
steps:
- uses: actions/checkout@v4
- name: Deploy
env:
ENV_VAR: ${{ secrets.ENV_SPECIFIC_VAR }}
DEPLOY_URL: ${{ vars.DEPLOY_URL }}
run: echo "Deploying to ${{ github.ref }}"name: Staged Deployment
on:
push:
branches: [main]
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
outputs:
build-number: ${{ steps.build.outputs.number }}
steps:
- uses: actions/checkout@v4
- id: build
run: echo "number=${{ github.run_number }}" >> $GITHUB_OUTPUT
- run: echo "Building application"
deploy-staging:
needs: build
runs-on: ubuntu-latest
environment:
name: staging
url: https://staging.example.com
steps:
- uses: actions/checkout@v4
- name: Deploy to staging
run: |
echo "Deploying build ${{ needs.build.outputs.build-number }} to staging"
test-staging:
needs: deploy-staging
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run integration tests
run: npm run test:integration -- --url https://staging.example.com
deploy-production:
needs: [build, test-staging]
runs-on: ubuntu-latest
environment:
name: production
url: https://example.com
steps:
- uses: actions/checkout@v4
- name: Deploy to production
run: |
echo "Deploying build ${{ needs.build.outputs.build-number }} to production"name: Blue-Green Deployment
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- name: Deploy to green environment
run: |
echo "Deploying new version to green"
# Deploy new version
- name: Run smoke tests
run: |
echo "Testing green environment"
# Run tests against new deployment
- name: Switch traffic to green
run: |
echo "Switching traffic from blue to green"
# Switch load balancer
- name: Keep blue as rollback
if: always()
run: echo "Blue environment ready for rollback"name: Build and Deploy Docker
on:
push:
branches: [main]
tags: ['v*']
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Log in to Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v4
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
deploy:
needs: build
runs-on: ubuntu-latest
environment: production
steps:
- name: Deploy to Kubernetes
run: |
echo "Deploying container to Kubernetes cluster"
# kubectl deployment commands herename: Deployment with Notifications
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy
id: deploy
run: |
echo "Deploying..."
# Deployment commands
echo "status=success" >> $GITHUB_OUTPUT
- name: Notify Slack - Success
if: steps.deploy.outputs.status == 'success'
uses: slackapi/slack-github-action@v1.24.0
with:
webhook-url: ${{ secrets.SLACK_WEBHOOK }}
payload: |
{
"text": "✅ Deployment Successful",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Deployment Successful*\nRepository: ${{ github.repository }}\nBranch: ${{ github.ref }}\nCommit: ${{ github.sha }}"
}
}
]
}
- name: Notify Slack - Failure
if: failure()
uses: slackapi/slack-github-action@v1.24.0
with:
webhook-url: ${{ secrets.SLACK_WEBHOOK }}
payload: |
{
"text": "❌ Deployment Failed",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Deployment Failed*\nRepository: ${{ github.repository }}\nError: Check logs"
}
}
]
}Duration: 3.5 hours | Level: Advanced
- Create reusable workflows
- Optimize workflow performance
- Implement advanced caching strategies
- Handle complex workflow scenarios
- Completion of Practicals 1-5
File: .github/workflows/reusable-test.yml
name: Reusable Test Workflow
on:
workflow_call:
inputs:
node-version:
required: false
type: string
default: '18'
test-command:
required: false
type: string
default: 'npm test'
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
cache: 'npm'
- run: npm ci
- run: ${{ inputs.test-command }}File: .github/workflows/ci.yml
name: CI Pipeline
on: [push, pull_request]
jobs:
test-node-16:
uses: ./.github/workflows/reusable-test.yml
with:
node-version: '16'
test-command: 'npm test -- --coverage'
test-node-18:
uses: ./.github/workflows/reusable-test.yml
with:
node-version: '18'
test-node-20:
uses: ./.github/workflows/reusable-test.yml
with:
node-version: '20'name: Optimized Build
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Cache Node modules
- uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm-
# Cache pip packages for Python
- uses: actions/cache@v3
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
- run: npm ci
- run: npm run buildname: Optimized Matrix Build
on: push
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16, 18, 20]
include:
- node-version: 18
cache-key-prefix: primary
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-npm-${{ matrix.cache-key-prefix }}-${{ hashFiles('**/package-lock.json') }}
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: npm testname: Smart Deployment
on:
push:
branches: [main, develop, hotfix/*]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm test
deploy:
needs: test
runs-on: ubuntu-latest
if: github.event_name == 'push'
steps:
- uses: actions/checkout@v4
- name: Determine environment
id: env
run: |
if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
echo "environment=production" >> $GITHUB_OUTPUT
elif [[ "${{ github.ref }}" == "refs/heads/develop" ]]; then
echo "environment=staging" >> $GITHUB_OUTPUT
elif [[ "${{ github.ref }}" == refs/heads/hotfix/* ]]; then
echo "environment=hotfix" >> $GITHUB_OUTPUT
fi
- name: Deploy to ${{ steps.env.outputs.environment }}
run: echo "Deploying to ${{ steps.env.outputs.environment }}"name: Approval-Based Deployment
on: workflow_dispatch
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm run build
request-approval:
needs: build
runs-on: ubuntu-latest
steps:
- name: Trigger approval
run: echo "Build ready for approval"
deploy-production:
needs: request-approval
runs-on: ubuntu-latest
environment:
name: production
url: https://example.com
steps:
- uses: actions/checkout@v4
- run: echo "Deploying to production"| Issue | Cause | Solution |
|---|---|---|
| Workflow not triggering | Wrong branch/event | Check on: configuration |
| Job timeout | Long-running tasks | Use longer timeout or parallelize |
| Cache not working | Key mismatch | Use hashFiles() for consistent keys |
| Permission denied | Missing permissions | Check permissions: in workflow |
| Out of memory | Large dependencies | Use smaller runners or parallelize |
name: Debug Workflow
on: push
jobs:
debug:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Enable debug logging
- name: Enable debug logging
run: |
echo "RUNNER_DEBUG=1" >> $GITHUB_ENV
# Print environment
- name: Print environment
run: |
echo "OS: $(uname -s)"
echo "Working directory: $(pwd)"
echo "Disk space: $(df -h)"
echo "Memory: $(free -h)"
# List secrets (sanitized)
- name: Check secrets available
run: |
if [ -n "${{ secrets.SOME_SECRET }}" ]; then
echo "Secret is set (value redacted)"
fi- Cache aggressively - Use caching for dependencies
- Use matrix efficiently - Run tests in parallel across versions
- Fail fast - Run quick checks before slow ones
- Artifact management - Remove old artifacts to save space
- Workflow optimization - Use conditional steps with
if:
Create a full CI/CD pipeline for a Node.js application that:
- Triggers on push to main/develop and all pull requests
- Lints code across JavaScript standards
- Tests with coverage reporting
- Builds for multiple Node versions
- Deploys to staging from develop branch
- Deploys to production from main branch with approval
- Notifies team via Slack on success/failure
Set up GitHub Actions for a project with:
- Frontend (Node.js + React)
- Backend (Python + Flask)
- Database migrations
- Docker containerization
- Separate deployment for each service
Create workflows that:
- Run security scans (SAST/DAST)
- Generate and publish test reports
- Monitor deployment health
- Create automated rollback on failure
- Generate compliance reports
- Successfully create workflows with push events
- Understand and use YAML syntax correctly
- Interpret workflow logs and identify issues
- Create multiple event triggers (push, PR, schedule)
- Structure complex workflows with multiple jobs
- Implement matrix builds
- Set up testing frameworks
- Create comprehensive CI pipelines
- Implement secure deployments with secrets
- Create reusable workflow components
- Optimize performance with caching
- Handle complex deployment scenarios
- GitHub Actions: https://docs.github.com/actions
- Workflow Syntax: https://docs.github.com/actions/using-workflows/workflow-syntax-for-github-actions
- GitHub Marketplace: https://github.com/marketplace?type=actions
- Actions Examples: https://github.com/actions/
- Cron Reference: https://crontab.guru
- YAML Syntax: https://yaml.org/
- Workflow Validator: https://github.com/rhysd/actionlint
- Actions Environment Dumper:
github/contextdebugging - Artifact Inspection: GitHub Actions interface
This comprehensive suite takes students from fundamental GitHub Actions concepts through production-grade CI/CD implementation. Each practical builds on previous knowledge while introducing new capabilities, ensuring mastery at each level.