Workflow đặt ở .github/workflows/*.yml. Biểu thức dùng ${{ ... }}. Tag version action trong ví dụ là major mới nhất tại thời điểm viết, nên chạy Dependabot để tự nâng.
Trigger (on:)
| Mục đích | Cú pháp |
|---|---|
Push lên main, chỉ khi đổi file trong src/ |
on: { push: { branches: [main], paths: ['src/**'] } } |
| Bỏ qua khi chỉ sửa docs | paths-ignore: ['docs/**', '**.md'] |
| Pull request vào main | on: { pull_request: { branches: [main] } } |
| Push tag release | on: { push: { tags: ['v*'] } } |
| Theo lịch (giờ UTC) | on: { schedule: [ { cron: '0 1 * * *' } ] } |
Bấm tay trên UI / gh workflow run |
on: workflow_dispatch (thêm inputs: nếu cần tham số) |
| Cho workflow khác gọi | on: workflow_call |
| Khi tạo release | on: { release: { types: [published] } } |
Job và step
| Mục đích | Cú pháp |
|---|---|
Quyền tối thiểu cho GITHUB_TOKEN |
permissions: { contents: read } |
| Cho phép OIDC (AWS/GCP/Azure không cần key) | permissions: { id-token: write, contents: read } |
| Job chạy sau job khác | needs: [test] |
| Chỉ chạy trên main | if: github.ref == 'refs/heads/main' |
| Step chạy kể cả khi step trước lỗi | if: always() / chỉ khi lỗi: if: failure() |
| Giới hạn thời gian | timeout-minutes: 15 |
| Cho phép job lỗi mà không fail workflow | continue-on-error: true |
| Thư mục làm việc mặc định | defaults: { run: { working-directory: app } } |
| Gắn môi trường có approval/secret riêng | environment: production |
Matrix
| Mục đích | Cú pháp |
|---|---|
| Chạy nhiều version | strategy: { matrix: { python: ['3.12', '3.13'] } } → ${{ matrix.python }} |
| Nhiều OS | matrix: { os: [ubuntu-latest, macos-latest] } + runs-on: ${{ matrix.os }} |
| Không huỷ job khác khi một job lỗi | strategy: { fail-fast: false } |
| Bớt / thêm tổ hợp | exclude: [ { os: macos-latest, python: '3.12' } ] / include: [...] |
| Giới hạn số job song song | strategy: { max-parallel: 2 } |
Secrets, biến, output
| Mục đích | Cú pháp |
|---|---|
Đọc secret (đặt dưới env: của step/job) |
NETBOX_TOKEN: ${{ secrets.NETBOX_TOKEN }} |
| Biến cấu hình không bí mật | ${{ vars.NETBOX_URL }} |
| Tạo secret bằng CLI | gh secret set NETBOX_TOKEN < token.txt |
| Ẩn một giá trị khỏi log | echo "::add-mask::$VALUE" |
| Ghi output của step | echo "ver=1.2.0" >> "$GITHUB_OUTPUT" → ${{ steps.<id>.outputs.ver }} |
| Đặt env cho các step sau | echo "IMAGE_TAG=$GITHUB_SHA" >> "$GITHUB_ENV" |
| Ghi tóm tắt lên trang run | echo "### Kết quả" >> "$GITHUB_STEP_SUMMARY" |
| Giá trị mặc định khi rỗng | ${{ inputs.env || 'staging' }} |
| Context hay dùng | github.sha, github.ref_name, github.event_name, github.actor, runner.os |
Cache, artifact, concurrency
| Mục đích | Cú pháp |
|---|---|
| Cache có sẵn của setup-python | uses: actions/setup-python@v7 + with: { python-version: '3.12', cache: pip } |
| Cache của setup-uv | uses: astral-sh/setup-uv@v7 + with: { enable-cache: true } |
| Cache tuỳ ý | uses: actions/cache@v6 + with: { path: ~/.cache/x, key: "${{ runner.os }}-x-${{ hashFiles('**/uv.lock') }}" } |
| Upload artifact | uses: actions/upload-artifact@v7 + with: { name: report, path: out/, retention-days: 7 } |
| Download ở job sau | uses: actions/download-artifact@v8 + with: { name: report, path: out/ } |
| Huỷ run cũ khi push mới cùng branch | concurrency: { group: "${{ github.workflow }}-${{ github.ref }}", cancel-in-progress: true } |
Workflow mẫu: test Python với uv
name: ci
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python: ["3.12", "3.13"]
steps:
- uses: actions/checkout@v7
- uses: astral-sh/setup-uv@v7
with:
python-version: ${{ matrix.python }}
enable-cache: true
- run: uv sync --locked
- run: uv run pytest -q --junitxml=out/junit.xml
- uses: actions/upload-artifact@v7
if: always()
with:
name: junit-${{ matrix.python }}
path: out/Reusable workflow
on:
workflow_call:
inputs:
env:
type: string
required: true
secrets:
DEPLOY_TOKEN:
required: true
jobs:
deploy:
runs-on: ubuntu-latest
environment: ${{ inputs.env }}
steps:
- run: ./deploy.sh "${{ inputs.env }}"
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}jobs:
deploy-prod:
uses: ./.github/workflows/deploy.yml # repo khác: org/repo/.github/workflows/deploy.yml@main
with:
env: production
secrets: inheritgh CLI
| Mục đích | Lệnh |
|---|---|
| Liệt kê run gần đây | gh run list --limit 10 |
| Theo dõi run đang chạy | gh run watch |
| Xem log của step lỗi | gh run view <run-id> --log-failed |
| Chạy lại job lỗi | gh run rerun <run-id> --failed |
| Kích hoạt workflow_dispatch | gh workflow run ci.yml -f env=staging |
Workflow chạy bởi pull_request từ fork không đọc được secrets. Đừng chuyển sang pull_request_target rồi checkout code của PR, vì code lạ sẽ chạy với quyền và secret của repo.