GitHub Actions CI/CD: Complete 2026 Guide
in Development on Github-actions, Ci-cd, DevOps, Automation
GitHub Actions CI/CD: Complete 2026 Guide
GitHub Actions has become the standard for CI/CD pipelines in modern software development. This comprehensive guide covers everything from basic workflows to advanced deployment patterns.
Why GitHub Actions?
- Native integration - Built into GitHub
- Generous free tier - 2,000 minutes/month for public repos
- Marketplace - Thousands of pre-built actions
- Matrix builds - Test across multiple environments
- Self-hosted runners - Use your own infrastructure
- Secrets management - Secure credential storage
Workflow Basics
Workflow Structure
# .github/workflows/ci.yml
name: CI Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
workflow_dispatch: # Manual trigger
env:
NODE_VERSION: '20'
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: $
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
Triggers
on:
# Push events
push:
branches:
- main
- 'feature/**'
paths:
- 'src/**'
- '!src/**/*.md'
tags:
- 'v*'
# Pull request events
pull_request:
types: [opened, synchronize, reopened]
branches: [main]
# Scheduled events
schedule:
- cron: '0 2 * * *' # Daily at 2 AM UTC
# Manual trigger with inputs
workflow_dispatch:
inputs:
environment:
description: 'Deployment environment'
required: true
default: 'staging'
type: choice
options:
- staging
- production
debug:
description: 'Enable debug mode'
type: boolean
default: false
# External webhook
repository_dispatch:
types: [deploy]
Job Configuration
Matrix Builds
jobs:
test:
runs-on: $
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
node-version: [18, 20, 22]
exclude:
- os: windows-latest
node-version: 18
include:
- os: ubuntu-latest
node-version: 20
coverage: true
steps:
- uses: actions/checkout@v4
- name: Use Node.js $
uses: actions/setup-node@v4
with:
node-version: $
- run: npm ci
- run: npm test
- name: Upload coverage
if: matrix.coverage
uses: codecov/codecov-action@v4
Job Dependencies
jobs:
build:
runs-on: ubuntu-latest
outputs:
version: $
steps:
- uses: actions/checkout@v4
- id: version
run: echo "version=$(cat package.json | jq -r .version)" >> $GITHUB_OUTPUT
- run: npm ci && npm run build
- uses: actions/upload-artifact@v4
with:
name: build
path: dist/
test:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
with:
name: build
path: dist/
- run: npm test
deploy:
needs: [build, test]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- run: echo "Deploying version $"
Concurrency
concurrency:
group: $-$
cancel-in-progress: true
jobs:
deploy:
concurrency:
group: production-deploy
cancel-in-progress: false # Don't cancel ongoing deploys
Complete CI/CD Pipeline
Full-Stack Application
# .github/workflows/ci-cd.yml
name: CI/CD Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: $
jobs:
# Lint and type check
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run lint
- run: npm run type-check
# Unit tests
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: test
POSTGRES_PASSWORD: test
POSTGRES_DB: test
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- name: Run tests
run: npm test -- --coverage
env:
DATABASE_URL: postgresql://test:test@localhost:5432/test
REDIS_URL: redis://localhost:6379
- name: Upload coverage
uses: codecov/codecov-action@v4
with:
token: $
# E2E tests
e2e:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npx playwright install --with-deps
- name: Run E2E tests
run: npm run test:e2e
- uses: actions/upload-artifact@v4
if: failure()
with:
name: playwright-report
path: playwright-report/
# Build Docker image
build:
needs: [lint, test]
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
outputs:
image-tag: $
image-digest: $
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: $
username: $
password: $
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: $/$
tags: |
type=sha,prefix=
type=ref,event=branch
type=semver,pattern=
- name: Build and push
id: build
uses: docker/build-push-action@v5
with:
context: .
push: $
tags: $
labels: $
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64,linux/arm64
# Deploy to staging
deploy-staging:
needs: build
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment:
name: staging
url: https://staging.example.com
steps:
- uses: actions/checkout@v4
- name: Deploy to staging
run: |
echo "Deploying $"
# Add your deployment commands here
# Deploy to production (manual approval)
deploy-production:
needs: [build, deploy-staging]
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment:
name: production
url: https://example.com
steps:
- uses: actions/checkout@v4
- name: Deploy to production
run: |
echo "Deploying $"
# Add your deployment commands here
Reusable Workflows
Creating Reusable Workflow
# .github/workflows/reusable-deploy.yml
name: Reusable Deploy
on:
workflow_call:
inputs:
environment:
required: true
type: string
image-tag:
required: true
type: string
secrets:
DEPLOY_KEY:
required: true
outputs:
url:
description: "Deployment URL"
value: $
jobs:
deploy:
runs-on: ubuntu-latest
environment: $
outputs:
url: $
steps:
- name: Deploy
id: deploy
run: |
echo "Deploying $ to $"
echo "url=https://$.example.com" >> $GITHUB_OUTPUT
Using Reusable Workflow
jobs:
deploy-staging:
uses: ./.github/workflows/reusable-deploy.yml
with:
environment: staging
image-tag: $
secrets:
DEPLOY_KEY: $
Composite Actions
Creating Composite Action
# .github/actions/setup-project/action.yml
name: 'Setup Project'
description: 'Setup Node.js and install dependencies'
inputs:
node-version:
description: 'Node.js version'
required: false
default: '20'
install-playwright:
description: 'Install Playwright browsers'
required: false
default: 'false'
runs:
using: 'composite'
steps:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: $
cache: 'npm'
- name: Install dependencies
shell: bash
run: npm ci
- name: Install Playwright
if: inputs.install-playwright == 'true'
shell: bash
run: npx playwright install --with-deps
Using Composite Action
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-project
with:
node-version: '20'
install-playwright: 'true'
- run: npm test
Secrets and Environments
Repository Secrets
steps:
- name: Deploy
env:
API_KEY: $
DATABASE_URL: $
run: ./deploy.sh
Environment Secrets
jobs:
deploy:
runs-on: ubuntu-latest
environment: production # Uses production environment secrets
steps:
- run: echo "API_KEY=$"
OIDC Authentication (Keyless)
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- 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 AWS
run: aws s3 sync ./dist s3://my-bucket
Advanced Patterns
Dynamic Matrix
jobs:
prepare:
runs-on: ubuntu-latest
outputs:
matrix: $
steps:
- uses: actions/checkout@v4
- id: set-matrix
run: |
SERVICES=$(ls services/ | jq -R -s -c 'split("\n") | map(select(. != ""))')
echo "matrix={\"service\":$SERVICES}" >> $GITHUB_OUTPUT
build:
needs: prepare
runs-on: ubuntu-latest
strategy:
matrix: $
steps:
- run: echo "Building $"
Path-based Triggering
on:
push:
paths:
- 'packages/api/**'
- 'packages/shared/**'
jobs:
detect-changes:
runs-on: ubuntu-latest
outputs:
api: $
frontend: $
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
api:
- 'packages/api/**'
frontend:
- 'packages/frontend/**'
build-api:
needs: detect-changes
if: needs.detect-changes.outputs.api == 'true'
runs-on: ubuntu-latest
steps:
- run: echo "Building API"
build-frontend:
needs: detect-changes
if: needs.detect-changes.outputs.frontend == 'true'
runs-on: ubuntu-latest
steps:
- run: echo "Building Frontend"
Caching
steps:
# NPM cache
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
# Custom cache
- uses: actions/cache@v4
with:
path: |
~/.cache/pip
~/.cache/pre-commit
key: $-pip-$
restore-keys: |
$-pip-
# Docker layer cache
- uses: docker/build-push-action@v5
with:
cache-from: type=gha
cache-to: type=gha,mode=max
Notifications
jobs:
notify:
needs: [build, deploy]
if: always()
runs-on: ubuntu-latest
steps:
- name: Slack notification
uses: 8398a7/action-slack@v3
with:
status: $
fields: repo,message,commit,author,action,eventName,ref,workflow
env:
SLACK_WEBHOOK_URL: $
if: always()
Security Best Practices
Pinning Actions
steps:
# Bad - uses mutable tag
- uses: actions/checkout@v4
# Good - uses immutable SHA
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
Permissions
permissions:
contents: read # Minimum required
jobs:
deploy:
permissions:
contents: read
packages: write
id-token: write
Security Scanning
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
ignore-unfixed: true
severity: 'CRITICAL,HIGH'
- name: Run CodeQL
uses: github/codeql-action/analyze@v3
Conclusion
GitHub Actions provides a powerful, flexible CI/CD platform that integrates seamlessly with your GitHub workflow. By following these patterns, you can build robust automation pipelines for any project.
Key takeaways:
- Use matrix builds for cross-platform testing
- Leverage reusable workflows and composite actions
- Implement proper caching strategies
- Use OIDC for secure cloud authentication
- Follow security best practices
Resources
이 글이 도움이 되셨다면 공감 및 광고 클릭을 부탁드립니다 :)
