GitHub Actions: CI/CD Pipelines for Your Repositories
GitHub Actions are event-driven workflows defined in YAML files that automate testing, building, and deploying code directly from your repository.
In this tutorial, you'll learn GitHub Actions for continuous integration and deployment. GitHub Actions automates testing, building, and deploying your code directly from your repository. By the end, you'll write custom workflows, use matrix builds, cache dependencies, and deploy applications to cloud platforms.
flowchart TD
A[Push code] --> B[Workflow triggered]
B --> C[Job 1: Lint]
B --> D[Job 2: Test]
D --> E{Matrix: OS x Version}
E --> F[Ubuntu + Node 18]
E --> G[Ubuntu + Node 20]
E --> H[Windows + Node 18]
C --> I[Job 3: Deploy]
D --> I
I --> J[Deploy to production]
Writing Your First Workflow
Create .github/workflows/ci.yml:
name: CI Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: 18
- run: npm ci
- run: npm test
Push this file and see the workflow run. The on key defines triggers — when the workflow runs. Jobs run on runners (virtual machines). Steps are individual commands or actions.
Matrix Builds
Test across multiple OS and language versions:
jobs:
test:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
node: [16, 18, 20]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- run: npm ci
- run: npm test
Expected output: 6 parallel jobs (3 Node versions x 2 OS) run simultaneously.
Caching Dependencies
Speed up workflows by caching:
- name: Cache npm
uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
Deploying to Cloud
Deploy to AWS after tests pass:
jobs:
deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- run: npm run build
- name: Deploy to S3
run: |
aws s3 sync build/ s3://my-app-bucket
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
Workflow Components
| Component | Description | Example |
|---|---|---|
name |
Workflow display name | CI Pipeline |
on |
Trigger events | push, pull_request, schedule |
jobs |
Collection of jobs | test, build, deploy |
runs-on |
Runner OS | ubuntu-latest, <a href="/operating-systems/windows/">windows</a>-latest |
steps |
Commands or actions | run: npm test |
<a href="/design-patterns/strategy/">strategy</a>.matrix |
Multi-version testing | node: [16, 18] |
needs |
Job dependencies | needs: test |
secrets |
Encrypted variables | ${{ secrets.MY_SECRET }} |
Workflow Trigger Events
GitHub Actions supports many trigger events:
on:
push:
branches: [main, develop]
paths: ["src/**", "tests/**"]
pull_request:
branches: [main]
types: [opened, synchronize, reopened]
schedule:
- cron: "0 6 * * 1" # Every Monday at 6 AM
workflow_dispatch: # Manual trigger
inputs:
environment:
type: choice
options: [staging, production]
release:
types: [published]
Self-Hosted Runners
For custom infrastructure, host your own runners:
jobs:
deploy:
runs-on: self-hosted
steps:
- uses: actions/checkout@v4
- name: Deploy to internal server
run: ./deploy-internal.sh
Register a self-hosted runner in Settings > Actions > Runners. Use them for GPU builds, internal network access, or cost savings.
Using Reusable Workflows
Avoid duplication by calling shared workflows:
# .github/workflows/ci.yml
jobs:
call-lint:
uses: your-org/shared-workflows/.github/workflows/lint.yml@v1
with:
node-version: 18
call-test:
uses: your-org/shared-workflows/.github/workflows/test.yml@v1
secrets:
coveralls-token: ${{ secrets.COVERALLS_TOKEN }}
Common Errors
| Error | Cause | Fix |
|---|---|---|
No workflow found |
Wrong file location | File must be in .github/workflows/ |
Syntax error |
Bad YAML | Validate YAML online before committing |
Command not found |
Missing setup step | Add setup action before running commands |
Resource not accessible |
Wrong permissions | Add contents: read permission |
Secret not found |
Secret not defined | Add secret in repo Settings > Secrets |
Workflow not running |
Trigger doesn't match | Check branch name and event type |
Matrix too large |
Too many combinations | Limit matrix or use max-parallel |
Job timeout |
Execution too long | Increase timeout-minutes or optimize |
Practice Questions
Challenge
Create a GitHub Actions workflow for a Node.js project that: lints on every push, runs tests on pull requests with a matrix of Node 18 and 20, caches dependencies, and deploys to a staging environment only when tests pass on the main branch. Include a scheduled weekly dependency audit.
Real-World Task
Set up a complete CI/CD pipeline for a Python project using GitHub Actions. The pipeline should run linting (flake8), tests (pytest), build a Docker image, push it to Docker Hub, and deploy to a Kubernetes cluster on main branch pushes. Use secrets for Docker credentials and Kube config. This mirrors the deployment pipeline at DodaTech for DodaZIP's backend services.
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro