GitHub Actions — tự build, tự deploy
Một file YAML cho bạn máy chủ miễn phí tự đưa web lên mỗi lần push. Kèm bẫy quyền, bẫy bindings bị ghi đè, và quy trình hai bước preview → duyệt → production.
GitHub Actions là máy tính của GitHub chạy việc hộ bạn mỗi khi có sự kiện trong repo. Miễn phí cho repo công khai; repo riêng tư có hạn mức phút/tháng, đủ thừa cho mọi việc trong giáo trình.
Từ bài này, bạn sẽ không gõ lệnh deploy nữa: git push là web cập nhật.
Hai cách tự deploy — chọn đúng cái
| Pages nối Git (không cần Actions) | GitHub Actions | |
|---|---|---|
| Cấu hình | Bấm vài nút trên dashboard | Viết một file YAML |
| Có bước build riêng | Chỉ các build phổ biến | Bất kỳ (Python, script tự viết) |
| Chạy test trước khi deploy | Không | Có |
| Deploy hai bước (duyệt rồi mới lên) | Không | Có |
| Phù hợp | Web tĩnh đơn giản | Project có build, có test, có khách |
Lời khuyên thật: nếu project chỉ là HTML tĩnh, dùng Pages nối Git, bỏ qua Actions cho tới khi cần. Đừng thêm phức tạp vì nghe sang. Khi nào cần build bằng script riêng, hoặc cần chặn bản lỗi, hãy quay lại bài này.
File Actions đầu tiên
Tạo .github/workflows/deploy.yml:
name: Deploy len Cloudflare Pages
on:
push:
branches: [main]
workflow_dispatch: # cho phep bam chay tay tren web
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Build
run: |
pip install markdown pygments
python build.py
- name: Deploy
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy dist --project-name=web-dau-tien
Đọc từng khối — bốn ý, hiểu rồi thì mọi file Actions khác đều đọc được:
on:— khi nào chạy. Ở đây: mỗi lần đẩy vàomain, hoặc bấm tay.runs-on: ubuntu-latest— GitHub cấp cho bạn một máy Linux sạch, miễn phí, mỗi lần chạy.steps:— các bước, chạy tuần tự.uses:là dùng việc người khác viết sẵn;run:là lệnh của bạn.${{ secrets.X }}— lấy bí mật từ kho của repo, không bao giờ viết thẳng khóa vào file này.
Lấy token Cloudflare đúng quyền
Đây là chỗ hay sai nhất, và sai thì thông báo lỗi rất tối nghĩa.
- dash.cloudflare.com/profile/api-tokens → Create Token.
- Chọn mẫu Edit Cloudflare Workers (mẫu này phủ cả Pages và Workers).
- Giới hạn vào đúng tài khoản của bạn, đừng để All accounts.
- Tạo, rồi sao chép ngay — nó chỉ hiện một lần.
Lấy Account ID: dashboard → Workers & Pages → cột phải có Account ID.
Nạp vào GitHub:
gh secret set CLOUDFLARE_API_TOKEN
gh secret set CLOUDFLARE_ACCOUNT_ID
Lệnh này hỏi giá trị rồi nhập — bạn tự dán, giá trị không nằm lại trong lịch sử dòng lệnh. Hoặc làm trên web: repo → Settings → Secrets and variables → Actions → New repository secret.
Một token "Edit Cloudflare Workers" cho phép sửa mọi Worker và Pages trong tài khoản. Nếu token rò rỉ, người lấy được nó có thể thay nội dung app đang chạy của bạn.
Ba việc phải làm, không phải ba việc nên làm: 1. Đặt ngày hết hạn cho token (6–12 tháng), đừng để vĩnh viễn. 2. Chỉ nạp token vào Secrets, không bao giờ vào file YAML, không vào mã nguồn, không dán trong chat. 3. Khi nghi rò rỉ: thu hồi trước, điều tra sau. Tạo token mới mất 2 phút.
Kiểm nó chạy
git add . ; git commit -m "Them workflow tu deploy" ; git push
gh run watch # xem trực tiếp trong dòng lệnh
Hoặc mở tab Actions trên repo. Xanh là xong, đỏ là lỗi — bấm vào bước đỏ để đọc log.
Lỗi hay gặp lần đầu:
| Log báo | Nguyên nhân |
|---|---|
Authentication error [code: 10000] |
Token sai, hoặc thiếu quyền, hoặc chưa nạp secret |
Project not found |
Sai --project-name, hoặc project chưa tồn tại — tạo bằng tay một lần trước |
No such file or directory: dist |
Bước build không sinh ra dist — kiểm run: ở trên |
python: command not found |
Thiếu bước setup-python |
Đừng kể lại. Lấy log thật:
gh run view --log-failed > loi.txt
Rồi trong Claude Code:
Đọc loi.txt (log của bước Actions bị lỗi) và .github/workflows/deploy.yml.
Nói nguyên nhân gốc trong 2 dòng, rồi sửa đúng chỗ đó trong file YAML.
Đừng viết lại cả workflow.
gh run view --log-failed chỉ lấy phần bước bị lỗi — gọn hơn log đầy đủ hàng nghìn dòng, và AI trả lời chính xác hơn vì không bị loãng.
Quy trình hai bước: preview → duyệt → production
Đây là mức Cấp 5 nhưng dựng ngay từ bây giờ thì về sau khỏi phải sửa. Dùng cho mọi project có người ngoài dùng.
Ý tưởng: push vào main chỉ tạo bản preview. Lên bản thật phải bấm duyệt.
name: Deploy hai buoc
on:
push:
branches: [main]
jobs:
preview:
runs-on: ubuntu-latest
outputs:
url: ${{ steps.dep.outputs.deployment-url }}
steps:
- uses: actions/checkout@v4
- name: Build
run: python build.py
- id: dep
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy dist --project-name=web-dau-tien --branch=preview
production:
needs: preview
runs-on: ubuntu-latest
environment: production # <-- cho nay chan lai, cho duyet
steps:
- uses: actions/checkout@v4
- name: Build
run: python build.py
- uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy dist --project-name=web-dau-tien --branch=main
Rồi bật cổng duyệt: repo → Settings → Environments → New environment tên production → tích Required reviewers → chọn chính bạn.
Từ đó mỗi lần push: GitHub deploy bản preview, gửi bạn thông báo "chờ duyệt". Bạn mở link preview, bấm thử, thấy ổn thì vào Actions bấm Approve. Lúc đó bản thật mới đổi.
Người ta hay hỏi: tự duyệt mình thì chặn được gì?
Nó chặn đúng một thứ, và thứ đó là nguyên nhân phần lớn sự cố: thói quen push rồi quên kiểm. Khi bản thật tự lên, bạn push lúc 11 giờ đêm rồi đi ngủ, sáng ra khách gọi. Khi có cổng duyệt, cái nút "Approve" buộc bạn mở link preview — và trong 20 lần, có 1–2 lần bạn thấy thứ sai ngay ở bước đó.
Một hiệu ứng phụ bất ngờ: tôi đưa quyền Approve cho khách ở vài dự án. Khách thấy mình kiểm soát được bản lên, và từ đó hết hẳn chuyện "sao tự nhiên web đổi".
Việc khác Actions làm được, rất đáng dùng
Chặn bản lỗi: thêm bước test trước bước deploy. Test đỏ là không deploy.
- name: Kiem tra co ban
run: |
python build.py
test -f dist/index.html
grep -q "Chang An" dist/index.html
Ba dòng này đã bắt được lỗi thật: build "thành công" nhưng sinh ra file rỗng.
Việc định kỳ: thay cho cron trên máy bạn.
on:
schedule:
- cron: '0 1 * * *' # 01:00 UTC = 08:00 gio Viet Nam
Hai chuyện phải biết trước khi tin vào nó:
1. Giờ là UTC. Việt Nam = UTC+7. Muốn 8 giờ sáng thì viết 0 1 * * *. Sai chỗ này là báo cáo hằng ngày gửi vào 3 giờ chiều.
2. Actions không đúng giờ. Lịch của GitHub có thể trễ 5–30 phút lúc cao điểm, và có thể bỏ hẳn một lượt. Đừng dùng cho việc không được phép trượt (chốt sổ, tính lương). Việc đó dùng Cron Triggers của Cloudflare, đúng giờ hơn nhiều.
Bẫy này chỉ gặp từ Cấp 3, nhưng ghi ở đây để bạn khỏi mất dữ liệu: khi CI chạy wrangler deploy cho một Worker, cấu hình trong wrangler.toml của repo thay thế cấu hình đang chạy. Nếu bạn từng thêm một binding (D1, KV, R2, secret) bằng tay trên dashboard mà chưa ghi vào file, lần deploy sau nó biến mất — app chạy nhưng không thấy cơ sở dữ liệu nữa.
Luật: mọi binding phải có trong wrangler.toml của repo. Dashboard chỉ để xem, không để cấu hình. Và sau lần deploy CI đầu tiên, mở dashboard đối chiếu đủ danh sách binding.
Cả hai lần đều vì dấu cách. YAML không cho dùng Tab, và lùi sai hai dấu cách là file vô nghĩa, thông báo lỗi thì nói về dòng khác.
Hai mẹo cứu tôi:
1. Mở file .yml trong VS Code — nó tự tô màu, sai lùi đầu dòng là màu trông "lệch" ngay.
2. Đừng gõ lại từ đầu. Sao chép một file đang chạy rồi sửa từng dòng.
Và một điều an ủi: Actions chạy trên máy GitHub, không trên máy bạn. Sai thì chỉ đỏ một lượt chạy, không hỏng gì cả. Cứ thử.
- Đủ: workflow deploy một bước, lấy token đúng quyền, đọc log lỗi, quy trình hai bước có cổng duyệt, cron, test chặn bản lỗi.
- Đã kiểm thật: quy trình preview → duyệt → production và bẫy ghi đè bindings đều từ dự án đang chạy.
- Rủi ro còn lại:
cloudflare/wrangler-actionlà việc của bên thứ ba; số phiên bản (@v3) sẽ đổi. Nếu một ngày workflow đỏ mà bạn không sửa gì, kiểm phiên bản action trước tiên.
- Dựng workflow deploy một bước, push, xem nó xanh.
- Cố ý làm nó đỏ (xóa bước build), đọc log, sửa lại.
- Thêm bước kiểm
grep -qmột chữ chắc chắn có trên trang. Rồi cố ý đổi chữ đó trong nguồn → thấy CI chặn deploy. Đây là lần đầu bạn có một cái lưới tự động. - Dựng tiếp quy trình hai bước với environment
productionvà tự duyệt một lần.
Xong 4 việc này là đậu Cấp 2: mã an toàn, lịch sử rõ, và việc đưa lên mạng đã tự động nhưng có phanh.