ข้ามไปเนื้อหาหลัก

Category: guide

GitHub Actions Advanced — Matrix, Cache, Reusable Workflows

GitHub Actions ขั้นสูง: matrix builds, dependency caching, reusable workflows, environment secrets, และ workflow triggers

· อ่านประมาณ 4 นาที

สารบัญ

Matrix Builds — ทดสอบหลาย environments

# .github/workflows/test.yml
name: Test

on: [push, pull_request]

jobs:
  test:
    name: Test (Node ${{ matrix.node }}, OS ${{ matrix.os }})
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        node: [18, 20, 22]
        os: [ubuntu-latest, windows-latest]
      fail-fast: false  # ไม่ cancel jobs อื่นถ้า 1 ตัว fail
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
      - run: npm ci
      - run: npm test

Matrix กับ Include/Exclude

strategy:
  matrix:
    node: [18, 20]
    include:
      # เพิ่ม combination พิเศษ
      - node: 22
        experimental: true
    exclude:
      # ลบ combination ที่ไม่ต้องการ
      - node: 18
        os: windows-latest

Dependency Caching

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'  # ✓ shorthand: cache npm automatically

      - run: npm ci  # ใช้ cache ถ้ามี package-lock.json ไม่เปลี่ยน

      - name: Cache build output
        uses: actions/cache@v4
        with:
          path: .next/cache  # หรือ dist/, .astro/, etc.
          key: ${{ runner.os }}-nextjs-${{ hashFiles('package-lock.json') }}-${{ hashFiles('**/*.ts', '**/*.tsx') }}
          restore-keys: |
            ${{ runner.os }}-nextjs-${{ hashFiles('package-lock.json') }}-
            ${{ runner.os }}-nextjs-

Cache Key Strategy

# Cache key ที่ดี: OS + hash ของ lockfile + hash ของ source
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}

# restore-keys: fallback เมื่อ exact key ไม่ match
restore-keys: |
  ${{ runner.os }}-npm-

Reusable Workflows

สร้าง workflow template ที่ workflows อื่น call ได้:

# .github/workflows/build-and-test.yml (reusable)
name: Build and Test (Reusable)

on:
  workflow_call:
    inputs:
      node-version:
        required: false
        type: string
        default: '20'
      run-e2e:
        required: false
        type: boolean
        default: false
    secrets:
      NPM_TOKEN:
        required: false

jobs:
  build:
    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: npm run build
      - run: npm test
      - if: inputs.run-e2e
        run: npm run test:e2e
        env:
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
# .github/workflows/deploy.yml (caller)
name: Deploy

on:
  push:
    branches: [main]

jobs:
  test:
    uses: ./.github/workflows/build-and-test.yml
    with:
      node-version: '22'
      run-e2e: true
    secrets:
      NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

  deploy:
    needs: test  # รอ test pass ก่อน
    runs-on: ubuntu-latest
    steps:
      - run: echo "Deploying..."

Environments และ Secrets

jobs:
  deploy-staging:
    runs-on: ubuntu-latest
    environment: staging  # ต้องกด approve ใน GitHub UI ถ้าตั้ง protection rules
    steps:
      - run: |
          echo "Deploying to staging"
          echo "URL: ${{ vars.DEPLOY_URL }}"  # environment variable (ไม่ sensitive)
        env:
          API_KEY: ${{ secrets.STAGING_API_KEY }}  # secret (sensitive)

  deploy-production:
    needs: deploy-staging
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://panupongws.com  # แสดงใน GitHub deployment
    steps:
      - run: echo "Deploying to production"
        env:
          API_KEY: ${{ secrets.PROD_API_KEY }}

Conditional Steps

steps:
  - name: Deploy
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    run: npm run deploy

  - name: Notify Slack (on failure)
    if: failure()
    run: |
      curl -X POST ${{ secrets.SLACK_WEBHOOK }} \
        -d '{"text": "Build failed on ${{ github.ref }}"}'

  - name: Run only on PRs
    if: github.event_name == 'pull_request'
    run: npm run size-limit

  # Contexts ที่ useful:
  # github.ref — refs/heads/main
  # github.event_name — push, pull_request, schedule
  # github.actor — ชื่อ user ที่ trigger
  # runner.os — Linux, Windows, macOS
  # job.status — success, failure, cancelled

Workflow Triggers ที่ใช้บ่อย

on:
  # Push ไปยัง specific branches
  push:
    branches: [main, 'release/**']
    paths-ignore: ['docs/**', '**.md']  # skip ถ้าแก้แค่ docs

  # PR สู่ main
  pull_request:
    branches: [main]
    types: [opened, synchronize, reopened]

  # Schedule (cron)
  schedule:
    - cron: '0 2 * * 1'  # ทุกวันจันทร์ 02:00 UTC

  # Manual trigger พร้อม input
  workflow_dispatch:
    inputs:
      environment:
        description: 'Deploy target'
        required: true
        type: choice
        options: [staging, production]
      dry-run:
        type: boolean
        default: false

  # เมื่อ release ถูกสร้าง
  release:
    types: [published]

Artifacts — แชร์ไฟล์ระหว่าง Jobs

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - run: npm run build
      - uses: actions/upload-artifact@v4
        with:
          name: dist
          path: dist/
          retention-days: 3

  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: dist
          path: dist/
      - run: rsync -r dist/ user@server:/var/www/

Concurrency Control

# ยกเลิก run เก่าถ้ามี run ใหม่ของ branch เดียวกัน
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

# หรือ per-environment: ป้องกัน 2 deploys รันพร้อมกัน
concurrency:
  group: deploy-${{ github.ref }}
  cancel-in-progress: false  # รอให้เสร็จ ไม่ cancel

Permissions (Least Privilege)

permissions:
  contents: read     # อ่าน repo เท่านั้น (default ปลอดภัยกว่า)
  actions: read

jobs:
  deploy:
    permissions:
      contents: read
      deployments: write  # ต้องการสร้าง deployment
      id-token: write     # ต้องการสำหรับ OIDC (AWS/GCP auth)