GitHub Actions
GitHub Actions¶
1. What is GitHub Actions?¶
GitHub Actions is a CI/CD automation platform. It automatically runs workflows based on events like code pushes and PR creation.
Main Uses¶
| Use | Examples |
|---|---|
| CI (Continuous Integration) | Run tests automatically, linting |
| CD (Continuous Deployment) | Auto deployment, Docker image builds |
| Automation | Issue labeling, release note generation |
Core Concepts¶
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Workflow β
β (.github/workflows/ci.yml) β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββ β
β β Job: build β β
β β βββββββββββ βββββββββββ βββββββββββ β β
β β β Step 1 βββ Step 2 βββ Step 3 β β β
β β βCheckout β β Install β β Test β β β
β β βββββββββββ βββββββββββ βββββββββββ β β
β βββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β βββββββββββββββββββββββββββββββββββββββββββββββ β
β β Job: deploy β β
β β βββββββββββ βββββββββββ β β
β β β Build βββ Deploy β β β
β β βββββββββββ βββββββββββ β β
β βββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| Concept | Description |
|---|---|
| Workflow | Entire automation process (YAML file) |
| Event | Event that triggers workflow |
| Job | Group of steps running on same runner |
| Step | Individual work unit |
| Action | Reusable work unit |
| Runner | Server that executes workflow |
2. Workflow File Structure¶
Workflows are stored as YAML files in .github/workflows/ directory.
Basic Structure¶
# .github/workflows/ci.yml
name: CI Pipeline # Workflow name
on: # Trigger events
push:
branches: [main]
pull_request:
branches: [main]
jobs: # Job definitions
build: # Job name
runs-on: ubuntu-latest # Execution environment
steps: # Steps
- name: Checkout # Step name
uses: actions/checkout@v4
- name: Run tests
run: npm test
3. Trigger Events (on)¶
push / pull_request¶
on:
push:
branches:
- main
- 'release/**'
paths:
- 'src/**' # Only when src folder changes
paths-ignore:
- '**.md' # Ignore md file changes
pull_request:
branches: [main]
types: [opened, synchronize, reopened]
Manual Execution (workflow_dispatch)¶
on:
workflow_dispatch:
inputs:
environment:
description: 'Deployment environment'
required: true
default: 'staging'
type: choice
options:
- staging
- production
Schedule (cron)¶
on:
schedule:
- cron: '0 9 * * 1-5' # Weekdays at 9 AM (UTC)
# cron format: minute hour day month weekday
# 0 9 * * 1-5 = Monday-Friday 09:00
After Another Workflow Completes¶
on:
workflow_run:
workflows: ["Build"]
types: [completed]
4. Jobs Configuration¶
Basic Job¶
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm install
- run: npm test
Execution Environment (runs-on)¶
jobs:
build:
runs-on: ubuntu-latest # Ubuntu latest
# runs-on: ubuntu-22.04 # Specific version
# runs-on: macos-latest # macOS
# runs-on: windows-latest # Windows
Job Dependencies (needs)¶
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: npm run build
test:
needs: build # Run after build completes
runs-on: ubuntu-latest
steps:
- run: npm test
deploy:
needs: [build, test] # Run after both complete
runs-on: ubuntu-latest
steps:
- run: ./deploy.sh
Parallel Execution¶
jobs:
test-node-16:
runs-on: ubuntu-latest
steps:
- uses: actions/setup-node@v4
with:
node-version: '16'
- run: npm test
test-node-18: # Runs in parallel
runs-on: ubuntu-latest
steps:
- uses: actions/setup-node@v4
with:
node-version: '18'
- run: npm test
Matrix Strategy¶
Matrix builds allow you to run jobs across multiple configurations automatically.
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
node-version: [18, 20, 22]
os: [ubuntu-latest, macos-latest, windows-latest]
# Creates 9 jobs (3 versions Γ 3 OSes)
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm test
Advanced Matrix Options¶
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
node-version: [18, 20]
include:
# Add specific combination
- os: windows-latest
node-version: 20
exclude:
# Exclude specific combination
- os: macos-latest
node-version: 18
fail-fast: false # Continue other jobs if one fails
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm test
Python Matrix Example¶
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.9', '3.10', '3.11', '3.12']
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- run: |
pip install -r requirements.txt
pytest
Conditional Execution (if)¶
jobs:
deploy:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- run: ./deploy.sh
notify:
if: failure() # Only on failure
runs-on: ubuntu-latest
steps:
- run: echo "Build failed!"
5. Steps Configuration¶
Using Actions (uses)¶
steps:
# Official Action
- uses: actions/checkout@v4
# Specific version
- uses: actions/setup-node@v4
with:
node-version: '18'
# Marketplace Action
- uses: docker/build-push-action@v5
Running Commands (run)¶
steps:
# Single command
- run: npm install
# Multiple commands
- run: |
npm install
npm run build
npm test
# Specify working directory
- run: npm install
working-directory: ./frontend
# Specify shell
- run: echo "Hello"
shell: bash
Environment Variables¶
steps:
- run: echo $MY_VAR
env:
MY_VAR: "Hello"
- run: echo ${{ env.MY_VAR }}
Using Secrets¶
steps:
- run: echo ${{ secrets.API_KEY }}
env:
API_KEY: ${{ secrets.API_KEY }}
Secrets Setup: Repository β Settings β Secrets and variables β Actions
6. Advanced Features¶
Dependency Caching¶
Caching speeds up workflows by storing dependencies between runs.
Using actions/cache¶
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# npm cache
- uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- run: npm ci
- run: npm test
Python pip cache¶
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip' # Built-in caching support
- run: pip install -r requirements.txt
Go modules cache¶
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.21'
cache: true # Caches go modules automatically
- run: go build
Multiple cache paths¶
- uses: actions/cache@v4
with:
path: |
~/.npm
~/.cache
node_modules
key: ${{ runner.os }}-deps-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-deps-
Reusable Workflows¶
Create workflows that can be called by other workflows.
Callable 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: "Deployed application URL"
value: ${{ jobs.deploy.outputs.url }}
jobs:
deploy:
runs-on: ubuntu-latest
outputs:
url: ${{ steps.deploy.outputs.url }}
steps:
- uses: actions/checkout@v4
- name: Deploy to ${{ inputs.environment }}
id: deploy
run: |
echo "Deploying version ${{ inputs.version }} to ${{ inputs.environment }}"
# Deployment logic here
echo "url=https://${{ inputs.environment }}.example.com" >> $GITHUB_OUTPUT
env:
DEPLOY_TOKEN: ${{ secrets.deploy-token }}
Calling the Reusable Workflow¶
# .github/workflows/main.yml
name: Main Pipeline
on: push
jobs:
deploy-staging:
uses: ./.github/workflows/reusable-deploy.yml
with:
environment: staging
version: ${{ github.sha }}
secrets:
deploy-token: ${{ secrets.DEPLOY_TOKEN }}
deploy-production:
needs: deploy-staging
if: github.ref == 'refs/heads/main'
uses: ./.github/workflows/reusable-deploy.yml
with:
environment: production
version: ${{ github.sha }}
secrets:
deploy-token: ${{ secrets.DEPLOY_TOKEN }}
notify:
needs: deploy-production
runs-on: ubuntu-latest
steps:
- run: echo "Deployed to ${{ needs.deploy-production.outputs.deployment-url }}"
Composite Actions¶
Create custom actions from multiple steps.
# .github/actions/setup-app/action.yml
name: 'Setup Application'
description: 'Install and configure application'
inputs:
node-version:
description: 'Node.js version'
required: false
default: '20'
install-deps:
description: 'Install dependencies'
required: false
default: 'true'
outputs:
cache-hit:
description: 'Whether cache was restored'
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@v4
with:
path: node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
- name: Install dependencies
if: inputs.install-deps == 'true' && steps.cache.outputs.cache-hit != 'true'
shell: bash
run: npm ci
- name: Display info
shell: bash
run: |
echo "Node version: $(node --version)"
echo "npm version: $(npm --version)"
Using the Composite Action¶
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup application
uses: ./.github/actions/setup-app
with:
node-version: '20'
install-deps: 'true'
- run: npm test
- run: npm run build
OIDC Authentication to Cloud Providers¶
OpenID Connect (OIDC) allows keyless authentication to cloud providers without storing credentials.
AWS OIDC¶
jobs:
deploy-to-aws:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole
aws-region: us-east-1
- name: Deploy to S3
run: |
aws s3 sync ./build s3://my-bucket
GCP OIDC¶
jobs:
deploy-to-gcp:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4
- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v2
with:
workload_identity_provider: 'projects/123456789/locations/global/workloadIdentityPools/github/providers/github-provider'
service_account: 'github-actions@my-project.iam.gserviceaccount.com'
- name: Deploy to Cloud Run
run: |
gcloud run deploy my-service --image gcr.io/my-project/my-image
Azure OIDC¶
jobs:
deploy-to-azure:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4
- name: Azure Login
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Deploy to Azure
run: |
az webapp deploy --resource-group myRG --name myApp
Concurrency Control¶
Prevent multiple workflow runs from interfering with each other.
name: Deploy
on:
push:
branches: [main]
# Only one deployment at a time
concurrency:
group: production-deploy
cancel-in-progress: false # Wait for current deployment to finish
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: ./deploy.sh
Branch-specific Concurrency¶
concurrency:
group: deploy-${{ github.ref }}
cancel-in-progress: true # Cancel old runs when new push arrives
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: ./deploy.sh
PR-specific Concurrency¶
name: CI
on:
pull_request:
concurrency:
group: ci-${{ github.event.pull_request.number }}
cancel-in-progress: true # Cancel outdated PR builds
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm test
7. Practice Examples¶
Example 1: Node.js Test Automation (Updated)¶
# .github/workflows/node-ci.yml
name: Node.js CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20, 22]
steps:
- name: Checkout
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 linter
run: npm run lint
- name: Run tests
run: npm test
- name: Build
run: npm run build
Example 2: Docker Image Build & Push (Updated)¶
# .github/workflows/docker.yml
name: Docker Build & Push
on:
push:
branches: [main]
tags: ['v*']
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=sha
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
Example 3: PR Auto-Labeling¶
# .github/workflows/labeler.yml
name: PR Labeler
on:
pull_request:
types: [opened, synchronize]
jobs:
label:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/labeler@v5
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
# .github/labeler.yml (label rules)
frontend:
- 'src/frontend/**'
- '*.css'
- '*.html'
backend:
- 'src/backend/**'
- 'api/**'
documentation:
- '**/*.md'
- 'docs/**'
Example 4: Auto Deployment (Vercel)¶
# .github/workflows/deploy.yml
name: Deploy to Vercel
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
- name: Install Vercel CLI
run: npm install -g vercel
- name: Deploy to Vercel
run: vercel --prod --token=${{ secrets.VERCEL_TOKEN }}
env:
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
Example 5: Release Automation¶
# .github/workflows/release.yml
name: Release
on:
push:
tags: ['v*']
jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Generate changelog
id: changelog
uses: orhun/git-cliff-action@v3
with:
args: --latest --strip header
- name: Create Release
uses: softprops/action-gh-release@v1
with:
body: ${{ steps.changelog.outputs.content }}
draft: false
prerelease: ${{ contains(github.ref, 'beta') }}
8. Useful Actions (Updated)¶
| Action | Purpose | Latest Version |
|---|---|---|
actions/checkout@v4 |
Code checkout | v4 |
actions/setup-node@v4 |
Node.js setup | v4 |
actions/setup-python@v5 |
Python setup | v5 |
actions/setup-go@v5 |
Go setup | v5 |
actions/cache@v4 |
Dependency caching | v4 |
docker/setup-buildx-action@v3 |
Docker Buildx setup | v3 |
docker/build-push-action@v6 |
Docker build/push | v6 |
docker/login-action@v3 |
Docker registry login | v3 |
aws-actions/configure-aws-credentials@v4 |
AWS authentication (OIDC) | v4 |
google-github-actions/auth@v2 |
GCP authentication (OIDC) | v2 |
azure/login@v2 |
Azure authentication (OIDC) | v2 |
Speed Up with Caching¶
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm' # npm cache auto-handled
- run: npm ci
9. Debugging¶
View Logs¶
- Check workflow execution history in Actions tab
- Expand each Step's logs
Debug Mode¶
steps:
- run: echo "Debug info"
env:
ACTIONS_RUNNER_DEBUG: true
Local Testing (act)¶
# Install act (macOS)
brew install act
# Run workflow
act push
# Run specific job only
act -j build
Command/Syntax Summary¶
| Keyword | Description |
|---|---|
name |
Workflow/step name |
on |
Trigger events |
jobs |
Job definitions |
runs-on |
Execution environment |
steps |
Step definitions |
uses |
Use Action |
run |
Run command |
with |
Action parameters |
env |
Environment variables |
if |
Conditional execution |
needs |
Job dependencies |
strategy.matrix |
Matrix builds |
Next Steps¶
Let's learn about container orchestration with Kubernetes! β Docker/06_Kubernetes_Introduction.md