Một pull request có thể không đổi dòng nào trong workflow, nhưng lần chạy sau lại thực thi code khác. Lý do rất đơn giản: uses: owner/action@v4 trỏ tới một tag có thể di chuyển. Ngày 14/10/2026, GitHub đưa workflow dependency lockfile vào public preview để giữ cách viết dễ đọc ở YAML, còn commit thực thi được ghi trong file khóa.

Bài này không coi lockfile là lá chắn supply chain vạn năng. Nó biến thay đổi dependency thành diff có thể review và giữ run ở commit đã khóa; dependency đó có an toàn hay không vẫn là việc của người review. Để bớt lý thuyết, phần dưới dùng gh-actions-lock v0.2.0 trên repo fixture công khai, không có secret và không chạy workflow.

Public preview ngày 14/10 thay đổi điều gì?

GitHub Changelog ghi public preview cho GitHub Enterprise Cloud, Enterprise Cloud with data residency, Team và Free; editor note cùng ngày nói phạm vi plan đã được cập nhật. Trong khi đó, một số trang how-to vẫn hiển thị khung “Who can use this feature?” hẹp hơn. Vì feature còn preview và tài liệu đang chuyển động, hãy kiểm tra ngay trên repo/tài khoản của bạn thay vì suy từ tên plan.

Cơ chế có ba lớp. Workflow giữ reference dễ đọc. .github/workflows/actions.lock ghi commit cụ thể. Hai trường số owner_id và repo_id giúp runtime nhận ra repository đứng sau một cái tên có còn là repository đã khóa hay không. Theo tài liệu khái niệm của GitHub, lockfile bao phủ action trực tiếp, dependency bắc cầu và reusable workflow.

Lockfile khác pin full SHA trong YAML ở đâu?

Pin full SHA vẫn là cách rõ ràng để khóa action trực tiếp, nhưng nó làm file workflow khó đọc và không tự mô tả dependency mà composite action gọi tiếp. Lockfile để YAML giữ @v4.3.1, sau đó ghi commit và identity vào một file machine-generated. Khi review, thay đổi commit nằm trong diff dependency thay vì ẩn trong việc resolve tag lúc runner bắt đầu.

Ranh giới dễ nhầm nhất là reusable workflow. Lockfile của repo A ghi commit của reusable workflow mà A gọi, nhưng action bên trong reusable workflow được quản lý bởi lockfile của repo B — nơi workflow đó được định nghĩa. Nếu tổ chức có repo shared-workflows, GitHub khuyên onboard repo đó trước.

Sơ đồ ranh giới lockfile giữa repo ứng dụng và repo định nghĩa reusable workflow
Repo gọi khóa commit của reusable workflow; dependency bên trong được quản lý bởi lockfile của repo định nghĩa workflow đó.

Nói gọn: một file khóa không thể ôm toàn bộ graph qua mọi repository. Cấu trúc phù hợp hơn là mỗi repo chịu trách nhiệm dependency graph mà nó định nghĩa.

Chạy v0.2.0 trên fixture đã thay những file nào?

Môi trường kiểm tra là Windows, GitHub CLI 2.95.0, Go 1.26.3 và gh-actions-lock v0.2.0 tại commit d5dca22. Repo nodeselector/actions-test-fixtures được clone tạm thời; ba workflow được chọn gồm action trực tiếp, composite action có dependency bắc cầu và reusable workflow. Không workflow nào được chạy, không có production environment hay self-hosted runner.

gh actions-lock \
  .github/workflows/happy-path.yml \
  .github/workflows/deep-self-ref.yml \
  .github/workflows/reusable-build.yml

Lần generate đầu exit 0, báo “Pinned 3 actions (+ 1 transitive) across 3 workflows” và ghi format v0.0.3. Lockfile chứa ref, commit, owner_id, repo_id; dependency bắc cầu có thêm quan hệ uses. Scoped run chỉ giữ workflow được chọn trong file khóa, vì vậy diff cũng loại entry của workflow test “imposter” nằm ngoài phạm vi.

V0.2.0 còn “narrow” ref semver mới. Ví dụ, @v4 có thể được viết lại thành full version cao nhất đang khớp như @v4.6.2; branch name như main không đi theo luật semver này. Nếu team muốn giữ nguyên ref rộng trong giai đoạn pilot, CLI có --no-narrow. Dù chọn cách nào, workflow diff và lockfile diff phải được review cùng nhau; chỉ nhìn file khóa sẽ bỏ lỡ việc tool đã làm ref trong YAML cụ thể hơn.

Full-directory run và scoped run cũng không hoàn toàn giống nhau. Chạy không truyền path quét toàn bộ .github/workflows/ và có thể dọn entry orphan của workflow đã xóa. Chạy theo một vài path không nên được hiểu là “lockfile toàn repo đã xanh”. Trong fixture, chọn ba workflow giúp tránh case cố ý dùng commit giả mạo, nhưng đồng thời buộc báo cáo phải nói rõ phạm vi thay vì gọi đó là pass toàn repo.

Sau đó fixture được thêm actions/upload-artifact@v4 nhưng chưa regenerate. Cả --verify-local và --verify đều exit 1; bản online nói rõ action chưa được pin. Khi relock, tool cụ thể hóa ref thành v4.6.2 và ghi commit ea165f8…. Đây là diff cần review như một dependency update, không phải file sinh ra rồi merge bằng niềm tin.

Khi đọc diff, commit là code runner sẽ dùng; thay đổi owner_id hoặc repo_id còn đáng ngại hơn một bump bình thường vì repository đứng sau cái tên có thể đã khác. Phần uses trong entry composite cho biết vì sao một dependency bắc cầu có mặt. Nếu diff trông sai, sửa workflow hoặc action source rồi generate lại; GitHub ghi rõ không sửa lockfile bằng tay.

Diff fixture thêm upload artifact và các trường commit owner ID repo ID trong actions lock
Diff rút gọn từ fixture: ref được cụ thể hóa, commit và identity được đưa vào phần review dependency.

Local action cho thấy preview vẫn đang tiến hóa

Fixture còn thêm một composite action hợp lệ tại ./local-demo. Trước khi sửa, --verify-local exit 1 và gắn finding local-action. Lần chạy có fix chuyển reference thành $/local-demo, sau đó verify local exit 0. Ký hiệu $/ nghĩa là action trong chính repository, resolve theo commit đang chạy nên không cần entry riêng trong lockfile.

Chi tiết này cũng nhắc rằng preview thay đổi khá nhanh: README v0.2.0 mô tả migration ./… sang $/…, trong khi mục limitation vẫn còn câu local-path action là short-term gap. Hãy pin version của extension trong bằng chứng và đọc diff thật; đừng viết policy dựa trên một screenshot cũ.

--verify-local, --verify và --relock không thay nhau được

--verify-local không cần mạng hay authentication, không ghi file và chỉ kiểm coverage/staleness cục bộ. Nó hợp cho pre-commit hook vì phản hồi nhanh, nhưng không xác minh pin trên GitHub còn đáng tin hay không. --verify cũng không ghi file, nhưng có truy vấn online để kiểm commit, ref và identity; đây mới là ứng viên cho required CI check.

--relock thì có ghi file: nó resolve lại những ref đã di chuyển hợp lệ và tạo diff commit mới. Nếu commit cũ không còn reachable, --relock vẫn báo lỗi; --accept-moved chỉ nên dùng sau khi điều tra upstream. Biến nút này thành “sửa CI” mặc định thì coi như bỏ qua lý do lockfile tồn tại.

So sánh verify local verify và relock trong quy trình GitHub Actions lockfile
Verify-local dành cho coverage offline; verify kiểm pin online; relock tạo thay đổi cần review.

Dependabot và policy chỉ hữu ích khi có quy trình review

Theo hướng dẫn khóa dependency, Dependabot có thể regenerate entry trong cùng pull request khi nó bump action ở một workflow đã onboard. Nó không tự onboard workflow mới; nếu workflow chưa có entry, Dependabot chỉ báo rằng cần onboard. Vì vậy automation này duy trì coverage, không mở rộng coverage thay cho maintainer.

Tài liệu enforcement đề xuất pilot trước, đặt actions.lock vào CODEOWNERS, giữ verify check, rồi mới chuyển policy sang Active. Evaluate mode chỉ có trên GitHub Enterprise. Ngoài ra, allowlist cũ viết theo tag có thể không match locked commit SHA; cần kiểm lại rule trước khi merge.

Nếu team đang quan tâm đến workflow từ fork, token và approval, bài GitHub Actions chặn workflow đáng ngờ xử lý lớp khác của bài toán. Còn khi agent chỉnh workflow, checklist dynamic workflows là chỗ hợp lý để thêm bước regenerate/verify. Để thiết kế negative control cho CI, cách đặt tiêu chí fail có thể tham khảo bài Node.js coverage không bỏ sót file.

Khi nào nên pilot, khi nào nên chờ?

Bối cảnhNên làmLý do
Repo cá nhân, ít workflowPilot ngay trên một nhánhDiff nhỏ, dễ đọc graph và rollback.
Monorepo nhiều workflowOnboard từng workflowGiữ phạm vi review và quan sát friction.
Repo shared workflowOnboard repo shared trướcDependency bên trong thuộc lockfile nơi workflow được định nghĩa.
Tổ chức EnterpriseEvaluate, CODEOWNERS, verify rồi ActivePolicy chỉ nên chặn run sau khi quy trình update đã ổn.
Tool/pre-1.0 đụng gap chínhGiữ SHA pin và theo dõi previewKhông ép rollout khi local action hoặc entitlement chưa khớp.

Lộ trình thực dụng là chọn một repo ít rủi ro, generate, đọc toàn bộ diff, thêm --verify vào CI, sau đó mới nối Dependabot và policy. Repo github/gh-actions-lock hiện là pre-1.0, giấy phép MIT; repo format/parser cũng nói rõ schema còn có thể đổi. Pilot lúc này hoàn toàn hợp lý, còn rollout cưỡng bức trên mọi repo thì hơi sớm.