Tạo file workflow GitHub Actions: chọn trigger, runner, matrix rồi lấy YAML đúng thụt lề
Công cụ dựng sẵn khung file YAML đặt trong thư mục .github/workflows, cho phép bật tắt từng trigger, chọn runner, khai báo matrix nhiều phiên bản Node, Python hoặc Go và chọn từng bước từ checkout tới upload artifact. Có bốn mẫu dựng sẵn cho Next.js, thư viện npm, image Docker và bộ test đa phiên bản.
Tính năng nổi bật
- Bốn trigger bật tắt độc lập: push theo nhánh, pull_request theo nhánh, schedule theo cron giờ UTC và workflow_dispatch
- Chọn runner trong danh sách ubuntu-latest, ubuntu-22.04, windows-latest, macos-latest và self-hosted
- Khai báo strategy matrix nhiều phiên bản runtime kèm công tắc fail-fast riêng
- Chín bước bật tắt: checkout, setup ngôn ngữ, cache dependency, cài đặt, lint, test, build, upload artifact và job deploy
- Mẫu Next.js sinh sẵn bước cache thư mục .next/cache và job deploy qua SSH có khai báo environment
- Mẫu publish npm dùng registry-url, biến NODE_AUTH_TOKEN và quyền id-token để bật provenance
- Mẫu Docker sinh chuỗi setup-qemu, setup-buildx, login, metadata và build-push với cache type gha
- Nút chép và nút tải file, YAML luôn giữ đúng hai dấu cách cho mỗi cấp thụt lề
Vì sao nên dựng khung workflow trước khi gõ tay
GitHub Actions không báo lỗi cú pháp cho tới khi bạn push. Một dấu cách thừa ở khối with, một tên khóa viết sai như run-on thay vì runs-on, hay đặt uses cùng cấp với run trong một step đều làm cả file bị từ chối, và bạn chỉ biết sau khi đã tạo commit. Vòng lặp sửa rồi push rồi chờ đó tốn nhiều thời gian hơn người ta tưởng, nhất là khi repository đã có nhiều người cùng làm việc. Khung sinh sẵn ở đây giữ đúng thứ tự các khóa mà GitHub mong đợi và đặt sẵn những phần hay bị quên: khối permissions để giới hạn quyền của GITHUB_TOKEN, khối concurrency để lần chạy mới hủy lần chạy cũ trên cùng nhánh, tham số retention-days cho artifact, điều kiện if cho job deploy. Bạn chỉnh tên lệnh cho khớp với dự án của mình rồi commit một lần là chạy được, thay vì mò từng lỗi thụt lề.
Lợi ích khi sử dụng
- Không phải nhớ tên và phiên bản của từng action phổ biến
- Tránh lỗi thụt lề YAML vốn chỉ lộ ra sau khi đã push lên GitHub
- Có sẵn permissions, concurrency và retention-days, những phần hay bị bỏ quên
- Nhìn thấy ngay khác biệt giữa bật và tắt matrix trước khi commit
- Bốn mẫu bám sát bốn tình huống thật hay gặp nhất, không phải ví dụ hello world
Cách tạo file workflow GitHub Actions
- 1Chọn một mẫu dựng sẵn nếu tình huống của bạn khớp: Next.js build và deploy, publish thư viện npm, build và push Docker image, hoặc chạy test đa phiên bản.
- 2Đặt tên workflow và tên file, ví dụ ci.yml, rồi chọn runner cùng ngôn ngữ chính của dự án.
- 3Bật các trigger cần dùng và nhập danh sách nhánh. Nếu bật schedule, nhớ biểu thức cron của GitHub tính theo giờ UTC.
- 4Bật strategy matrix rồi nhập danh sách phiên bản ngăn cách bằng dấu phẩy nếu bạn muốn chạy song song trên nhiều runtime.
- 5Bật tắt từng bước cho khớp với dự án, chép YAML rồi lưu vào .github/workflows trong repository và push để GitHub nhận workflow.
Ba tầng của một file workflow: workflow chứa jobs, jobs chứa steps
Cấu trúc của GitHub Actions xếp theo đúng ba tầng và mỗi tầng có bộ khóa riêng. Tầng ngoài cùng là workflow, khai báo name, on và tùy chọn permissions, env, concurrency, defaults. Tầng giữa là jobs, mỗi job là một khóa con dưới jobs và bắt buộc có runs-on để chỉ định máy chạy. Job cũng nhận needs, if, strategy, environment, outputs và services. Tầng trong cùng là steps, một danh sách có thứ tự nằm trong job, và đây là điểm khác biệt lớn nhất so với các hệ CI khác: mỗi step chỉ được chọn một trong hai dạng, hoặc uses để gọi một action đóng gói sẵn, hoặc run để chạy lệnh shell. Viết cả uses lẫn run trong cùng một step là lỗi. Step dùng uses nhận tham số qua khối with, step dùng run nhận biến qua khối env và có thể đổi shell bằng khóa shell. Hiểu đúng ba tầng này giúp bạn biết một khóa bị lỗi thuộc về đâu: nếu GitHub báo không nhận ra khóa cache, gần như chắc chắn bạn đặt nó ở tầng step thay vì trong khối with của action setup.
Trigger: push và pull_request lọc theo nhánh và theo đường dẫn, schedule luôn tính giờ UTC
Khóa on nhận nhiều sự kiện cùng lúc và mỗi sự kiện có bộ lọc riêng. Với push và pull_request bạn lọc bằng branches, branches-ignore, tags, paths và paths-ignore. Bộ lọc paths rất đáng dùng cho repository nhiều thư mục: khai báo paths chỉ chứa thư mục frontend thì commit sửa tài liệu sẽ không kích hoạt workflow build. Cần lưu ý một khác biệt dễ nhầm: với pull_request, mã được checkout là bản hợp nhất giả định giữa nhánh nguồn và nhánh đích, không phải commit cuối của nhánh nguồn, nên số commit và nội dung có thể khác điều bạn nghĩ. Sự kiện schedule dùng cú pháp cron năm trường và luôn tính theo giờ UTC, không theo múi giờ của tài khoản, nên muốn chạy chín giờ sáng giờ Việt Nam thì phải đặt hai giờ UTC. GitHub cũng không đảm bảo chạy đúng phút đã đặt khi hệ thống quá tải, và lịch tự động ngừng sau sáu mươi ngày repository không có hoạt động. Sự kiện workflow_dispatch cho phép bấm chạy tay từ tab Actions và khai báo thêm inputs để nhận tham số từ người bấm.
Strategy matrix: một job nhân thành nhiều lần chạy, include và exclude tinh chỉnh danh sách
Khối strategy matrix biến một định nghĩa job thành nhiều lần chạy song song, mỗi lần nhận một tổ hợp giá trị khác nhau. Khai báo hai chiều, ví dụ node-version có ba giá trị và os có hai giá trị, sẽ tạo ra sáu lần chạy, tức tích số phần tử của các chiều. Trong step bạn đọc giá trị bằng biểu thức matrix chấm tên chiều. Hai khóa hay dùng kèm là include và exclude: include thêm một tổ hợp riêng lẻ hoặc gắn thêm biến cho một tổ hợp đã có, exclude bỏ đúng một tổ hợp khỏi danh sách, chẳng hạn bỏ Node 18 trên Windows vì chưa hỗ trợ. Khóa fail-fast mặc định là true, nghĩa là chỉ cần một nhánh matrix thất bại thì mọi nhánh còn lại bị hủy ngay. Với công việc dò lỗi tương thích đa phiên bản, hãy đặt fail-fast là false để nhìn thấy đầy đủ nhánh nào hỏng nhánh nào chạy được. Khóa max-parallel giới hạn số lần chạy đồng thời, hữu ích khi bạn dùng runner tự dựng có số máy hạn chế hoặc khi job gọi tới một dịch vụ bên ngoài có giới hạn tần suất.
Cache: khóa cache trên GitHub là bất biến, hiểu sai restore-keys là cache không bao giờ trúng
Có hai cách bật cache. Cách đơn giản là dùng tham số cache ngay trong action setup, ví dụ setup-node nhận cache npm và tự tìm lockfile để dựng khóa. Cách thủ công là dùng action cache với ba tham số path, key và restore-keys. Điểm quan trọng nhất mà nhiều người bỏ qua: một khóa cache đã được ghi thì không bao giờ được ghi đè. Nếu bạn đặt khóa cố định như build-cache, lần đầu lưu gì thì mãi mãi trả về nội dung đó, cache thành ra vô dụng. Vì vậy khóa phải chứa một hàm băm của tệp mô tả dependency, thường viết bằng hashFiles trên package-lock.json hoặc requirements.txt hoặc go.sum. Khi lockfile đổi, khóa đổi theo, và lúc đó restore-keys mới phát huy tác dụng: đó là danh sách tiền tố để tìm cache gần đúng, giúp lần chạy mới vẫn khôi phục được phần lớn nội dung cũ thay vì tải lại từ đầu. Với dự án Next.js, ngoài cache dependency còn nên cache riêng thư mục .next/cache vì nó chứa kết quả biên dịch giữa các lần build, và mẫu Next.js trong công cụ này đã sinh sẵn bước đó.
Secrets, quyền của GITHUB_TOKEN và artifact: ba chỗ hay gây sự cố bảo mật
Mỗi lần chạy workflow, GitHub cấp một token tạm tên GITHUB_TOKEN có hiệu lực đúng trong lần chạy đó. Quyền mặc định của token này do cấu hình của tổ chức quyết định và có thể rộng hơn mức cần thiết, nên thói quen tốt là khai báo khối permissions ngay ở đầu file với contents read, rồi chỉ nâng quyền cho job nào thật sự cần, ví dụ packages write cho job đẩy image hoặc id-token write cho job publish có provenance. Với secret của bên thứ ba như token npm hay khóa SSH, hãy lưu trong Settings, Secrets and variables, Actions, và đọc bằng biểu thức secrets chấm tên biến. Không bao giờ in secret ra log, kể cả để gỡ lỗi, vì log của workflow trong repository công khai ai cũng đọc được. Một lưu ý riêng cho pull_request từ fork: các lần chạy này không được cấp secret và token chỉ có quyền đọc, đó là chủ ý chống lộ khóa chứ không phải lỗi cấu hình. Về artifact, action upload-artifact lưu tệp trên GitHub và tính vào dung lượng của tài khoản, nên hãy đặt retention-days ngắn cho artifact tạm và tuyệt đối không upload thư mục chứa tệp cấu hình có khóa.
So sánh với GitLab CI: cùng bài toán nhưng hai mô hình cấu hình khác nhau
Nếu dự án của bạn nằm trên GitLab thay vì GitHub, cú pháp không dịch một đổi một được. GitHub xếp workflow chứa jobs chứa steps, và mỗi step gọi một action đóng gói sẵn qua uses hoặc chạy lệnh shell qua run. GitLab bỏ hẳn tầng step: mỗi job có một danh sách script gồm các dòng lệnh shell, không có khái niệm action lắp ghép, mọi việc như đăng nhập registry hay dựng image đều viết bằng lệnh trực tiếp. Thứ tự chạy ở GitHub do khóa needs quyết định trong khi ở GitLab do khóa stages quyết định trước, needs chỉ dùng khi bạn muốn phá thứ tự stage để chạy theo đồ thị. GitHub chọn máy chạy bằng runs-on, GitLab chọn môi trường bằng image và có thêm services để gắn container phụ như database ngay trong job. Điều kiện chạy ở GitHub viết bằng biểu thức if trên job hoặc step, ở GitLab viết bằng rules với các mệnh đề if, changes, when. Nếu bạn cần đúng cú pháp của GitLab, hãy dùng công cụ tạo file .gitlab-ci.yml tại đường dẫn /vi/tools/gitlab-ci-generator, trang đó đi sâu vào stages, cache có key theo lockfile, artifacts có expire_in, extends và anchor YAML.
Câu hỏi thường gặp (FAQ)
File workflow phải đặt ở đâu thì GitHub mới nhận?
Đúng thư mục .github/workflows ở gốc repository, tên file kết thúc bằng .yml hoặc .yaml. Đặt trong thư mục con khác, đặt ở nhánh khác mà chưa merge, hoặc đặt sai tên thư mục đều khiến tab Actions không hiện gì cả. Một repository có thể chứa nhiều file workflow, mỗi file chạy độc lập.
Một step có được vừa dùng uses vừa dùng run không?
Không. Mỗi step chỉ chọn một trong hai. Dùng uses khi gọi một action đóng gói sẵn và truyền tham số qua khối with. Dùng run khi chạy lệnh shell và truyền biến qua khối env. Muốn làm cả hai việc thì tách thành hai step riêng.
Nên ghim action theo tag như v4 hay theo mã commit?
Tag dạng v4 tiện vì tự nhận bản vá, nhưng tag có thể bị tác giả trỏ lại sang commit khác. Với repository nội bộ hoặc dự án cần độ ổn định cao, hãy ghim theo mã commit bốn mươi ký tự để nội dung action không đổi sau lưng bạn, rồi dùng Dependabot cập nhật định kỳ.
Vì sao job schedule chạy lệch giờ so với lịch tôi đặt?
Biểu thức cron của GitHub luôn tính theo giờ UTC chứ không theo múi giờ tài khoản, nên chín giờ sáng giờ Việt Nam tương ứng hai giờ UTC. Ngoài ra GitHub chỉ đảm bảo chạy gần thời điểm đã đặt, lúc hệ thống bận có thể trễ vài phút, và lịch tự ngừng sau sáu mươi ngày repository không có hoạt động.
fail-fast nên bật hay tắt khi chạy matrix?
Tắt khi bạn đang kiểm tra tính tương thích trên nhiều phiên bản, vì bạn cần thấy đủ nhánh nào hỏng nhánh nào chạy được. Bật khi matrix chỉ để chạy nhanh hơn và bạn muốn tiết kiệm phút chạy, vì một nhánh hỏng nghĩa là cả bản thay đổi đó đã cần sửa.
Vì sao cache của tôi không bao giờ trúng dù đã khai báo?
Thường do khóa cache đặt cố định. Trên GitHub, một khóa đã ghi thì không được ghi đè, nên khóa cố định sẽ mãi trả về nội dung của lần lưu đầu tiên. Khóa cần chứa hàm băm của lockfile bằng hashFiles, còn restore-keys chỉ là danh sách tiền tố dự phòng để khôi phục cache gần đúng.
Job deploy chạy cả khi mở pull request, làm sao chặn?
Thêm điều kiện if ở cấp job, ví dụ kiểm tra github.ref bằng refs/heads/main và github.event_name khác pull_request. Kèm theo nên khai báo environment cho job deploy để bật quy tắc duyệt trước khi chạy, khi đó GitHub sẽ chờ người có quyền bấm đồng ý.
Concurrency dùng để làm gì và có nên bật không?
Khối concurrency gom các lần chạy vào cùng một nhóm, thường theo tên workflow cộng tên nhánh, và bật cancel-in-progress để commit mới hủy lần chạy cũ. Rất nên bật cho workflow test vì tiết kiệm phút chạy. Không nên bật cho job deploy vì hủy giữa chừng có thể để lại trạng thái dở dang trên máy chủ.
Pull request từ fork không đọc được secret, đó có phải lỗi không?
Không, đó là thiết kế bảo mật. Lần chạy sinh từ fork không được cấp secret và GITHUB_TOKEN chỉ có quyền đọc, để người ngoài không thể gửi một pull request chứa mã lấy trộm khóa của bạn. Nếu cần chạy tác vụ có secret, hãy dùng workflow riêng chạy sau khi mã đã được người trong dự án xem xét.
Truyền tệp giữa hai job trong cùng workflow bằng cách nào?
Dùng upload-artifact ở job trước và download-artifact ở job sau, kèm needs để job sau chờ job trước. Mỗi job chạy trên một máy sạch nên không chia sẻ thư mục làm việc. Với dữ liệu nhỏ như một chuỗi phiên bản, hãy dùng outputs của job thay vì tạo artifact.
Runner self-hosted khác gì runner của GitHub?
Runner self-hosted là máy của bạn, không tính phút chạy, giữ được cache trên đĩa và truy cập được mạng nội bộ. Đổi lại bạn tự lo cập nhật, tự dọn thư mục làm việc và phải cẩn thận vì thư mục không được làm sạch giữa các lần chạy. Không nên dùng runner self-hosted cho repository công khai.
YAML sinh ra dùng thẳng cho dự án thật được chưa?
Nên xem là khung để chỉnh tiếp. Hãy kiểm tra lại tên lệnh cài đặt và lệnh build cho đúng với package của bạn, đường dẫn thư mục xuất bản, tên secret, phiên bản runtime và nội dung script deploy. Chạy thử trên một nhánh phụ trước khi trỏ workflow vào nhánh chính.
Từ khóa liên quan
- github actions generator
- tạo file workflow github actions
- github actions yaml mẫu
- cấu hình ci cd github
- github actions matrix nhiều phiên bản node
- github actions cache dependency
- hashFiles restore-keys github actions
- github actions upload artifact
- workflow_dispatch là gì
- github actions schedule cron utc
- github actions deploy nextjs
- publish npm bằng github actions
- docker build push github actions
- github actions permissions GITHUB_TOKEN
- concurrency cancel-in-progress github actions
- runs-on ubuntu-latest
- self-hosted runner github
- github actions fail-fast matrix
- lỗi thụt lề yaml github actions
- github actions secrets npm token
