GitHub Actions là gì?
GitHub Actions là platform CI/CD tích hợp sẵn trong GitHub, cho phép bạn tự động hóa build, test, và deploy ngay từ repository. Với GitHub Actions, bạn có thể tạo workflows phản ứng với các sự kiện như push, pull request, hoặc theo lịch định kỳ.
Ưu điểm của GitHub Actions
- Tích hợp sẵn: Không cần cấu hình server riêng
- Marketplace phong phú: Hàng nghìn actions có sẵn
- Matrix builds: Test trên nhiều OS/version cùng lúc
- Secrets management: Quản lý credentials an toàn
- Free tier hào phóng: 2000 phút/tháng cho public repos, 3000 phút cho Pro
Khái niệm cơ bản
- Workflow: File YAML định nghĩa automation process
- Job: Tập hợp các steps chạy trên cùng runner
- Step: Một task đơn lẻ (command hoặc action)
- Action: Reusable unit có thể dùng trong nhiều workflows
- Runner: Server thực thi workflow (GitHub-hosted hoặc self-hosted)
1. Cấu trúc Workflow cơ bản
Tạo Workflow File
Tạo file tại .github/workflows/ci.yml:
name: CI/CD Pipeline
# Kích hoạt workflow
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
workflow_dispatch: # Cho phép chạy thủ công
# Biến môi trường global
env:
NODE_VERSION: '20'
REGISTRY: ghcr.io
jobs:
lint:
name: Lint & Format Check
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run ESLint
run: npm run lint
- name: Check Prettier formatting
run: npm run format:check
- name: TypeScript type check
run: npm run typecheck
test:
name: Unit & Integration Tests
runs-on: ubuntu-latest
needs: lint
strategy:
matrix:
node-version: [18, 20, 22]
fail-fast: false # Tiếp tục test các version khác nếu 1 version fail
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 with coverage
run: npm run test:coverage
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
if: matrix.node-version == 20
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage/lcov.info
fail_ci_if_error: true
build:
name: Build Application
runs-on: ubuntu-latest
needs: [lint, test]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build application
run: npm run build
env:
NEXT_PUBLIC_API_URL: ${{ secrets.API_URL }}
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: build-output
path: |
.next/
public/
retention-days: 7
if-no-files-found: error
2. Deploy to Vercel
Production Deployment
deploy-production:
name: Deploy to Production
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
environment:
name: production
url: https://example.com
steps:
- uses: actions/checkout@v4
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: build-output
- name: Deploy to Vercel
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args: '--prod'
- name: Comment PR with deployment URL
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '✅ Deployed to production: https://example.com'
})
Preview Deployment
deploy-preview:
name: Deploy Preview
runs-on: ubuntu-latest
needs: build
if: github.event_name == 'pull_request'
environment:
name: preview
url: https://preview-${{ github.event.pull_request.number }}.example.com
steps:
- uses: actions/checkout@v4
- name: Deploy Preview to Vercel
uses: amondnet/vercel-action@v25
id: deploy
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
- name: Comment PR with preview URL
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '🚀 Preview deployed: ${{ steps.deploy.outputs.preview-url }}'
})
3. Cấu hình Secrets và Environments
Thêm Secrets trong GitHub
Repository Secrets (Settings > Secrets and variables > Actions):
VERCEL_TOKEN # Token từ Vercel account settings
VERCEL_ORG_ID # Org ID từ Vercel
VERCEL_PROJECT_ID # Project ID từ Vercel
CODECOV_TOKEN # Token từ Codecov
SLACK_WEBHOOK # Webhook URL từ Slack
DATABASE_URL # Connection string cho database
API_KEY # API keys các dịch vụ external
Cấu hình Environments
Settings > Environments - Tạo các environments:
Production Environment:
- Required reviewers: 2 người (team leads)
- Wait timer: 5 phút
- Deployment branches: Chỉ main branch
- Environment secrets: Production-specific keys
Staging Environment:
- Required reviewers: 1 người
- Deployment branches: main, develop
- Environment secrets: Staging keys
Preview Environment:
- No restrictions
- Auto-deploy cho mọi PR
4. Docker Build & Push
Workflow cho Docker
name: Docker Build & Push
on:
push:
branches: [main, develop]
tags: ['v*.*.*']
pull_request:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GitHub 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=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,prefix={{branch}}-
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
BUILD_DATE=${{ steps.meta.outputs.created }}
VERSION=${{ steps.meta.outputs.version }}
- name: Run Trivy security scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload Trivy results to GitHub Security
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: 'trivy-results.sarif'
5. Cache Dependencies
Cache npm packages
- name: Cache node modules
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Install dependencies
run: npm ci
Cache Docker layers
- name: Build with cache
uses: docker/build-push-action@v5
with:
cache-from: type=gha
cache-to: type=gha,mode=max
6. Notifications
Slack Notification
notify:
name: Send Notifications
runs-on: ubuntu-latest
needs: [deploy-production]
if: always()
steps:
- name: Slack Notification
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: |
Deploy to production ${{ job.status }}
Author: ${{ github.actor }}
Commit: ${{ github.event.head_commit.message }}
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
if: always()
7. Reusable Workflows
Tạo Reusable Workflow
File: .github/workflows/reusable-build.yml
name: Reusable Build Workflow
on:
workflow_call:
inputs:
node-version:
required: true
type: string
environment:
required: true
type: string
secrets:
api-key:
required: true
jobs:
build:
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
- name: Build
run: npm run build
env:
API_KEY: ${{ secrets.api-key }}
Sử dụng Reusable Workflow
name: Main CI/CD
on: [push, pull_request]
jobs:
call-build:
uses: ./.github/workflows/reusable-build.yml
with:
node-version: '20'
environment: 'production'
secrets:
api-key: ${{ secrets.API_KEY }}
8. Status Badges
Thêm badges vào README.md
# My Awesome Project





9. Advanced Patterns
Conditional Jobs
deploy:
if: |
github.event_name == 'push' &&
github.ref == 'refs/heads/main' &&
!contains(github.event.head_commit.message, '[skip ci]')
Job Outputs
jobs:
build:
outputs:
version: ${{ steps.version.outputs.value }}
steps:
- id: version
run: echo "value=1.0.0" >> $GITHUB_OUTPUT
deploy:
needs: build
steps:
- run: echo "Deploying version ${{ needs.build.outputs.version }}"
Matrix với exclude
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node: [18, 20, 22]
exclude:
- os: macos-latest
node: 18
- os: windows-latest
node: 22
10. Best Practices
Performance Optimization
1. Cache aggressively: Cache dependencies, build outputs, Docker layers
2. Run jobs in parallel: Sử dụng needs để tạo dependency graph tối ưu
3. Skip unnecessary jobs: Dùng if conditions và path filters
4. Use artifacts wisely: Chỉ upload artifacts cần thiết, set retention days hợp lý
on:
push:
paths:
- 'src/**'
- 'package*.json'
paths-ignore:
- 'docs/**'
- '**.md'
Security Best Practices
1. Pin action versions: Dùng commit SHA thay vì tags
# ❌ Không an toàn
- uses: actions/checkout@v4
# ✅ An toàn hơn
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
2. Least privilege principle: Chỉ grant permissions cần thiết
permissions:
contents: read
packages: write
pull-requests: write
3. Use environment protection rules: Required reviewers cho production
4. Rotate secrets regularly: Thay đổi tokens/keys định kỳ
5. Scan for vulnerabilities: Sử dụng Dependabot, Trivy, Snyk
Debugging Tips
1. Enable debug logging:
- Thêm secret ACTIONS_STEP_DEBUG = true
- Thêm secret ACTIONS_RUNNER_DEBUG = true
2. Use tmate for SSH debugging:
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
if: failure()
3. Check workflow syntax: Sử dụng GitHub CLI
gh workflow view ci.yml
Cost Optimization
1. Sử dụng self-hosted runners cho workloads lớn
2. Set timeout cho jobs để tránh chạy mãi
jobs:
build:
timeout-minutes: 30
3. Clean up artifacts: Xóa artifacts cũ không cần thiết
4. Monitor usage: Check Actions usage trong Settings > Billing
Kết luận
GitHub Actions là công cụ CI/CD mạnh mẽ và linh hoạt. Các điểm chính cần nhớ:
- Bắt đầu đơn giản với lint, test, build cơ bản
- Tận dụng cache và parallel jobs để tối ưu thời gian
- Sử dụng matrix builds để test trên nhiều môi trường
- Implement proper security với secrets và environments
- Sử dụng reusable workflows để tránh duplicate code
- Monitor và optimize costs thường xuyên
- Luôn test workflows trên branches trước khi merge