Skip to content

CI và gác trước khi merge ​

Hai repo (16.10.2026)

Từ 16.10.2026 job www đã gỡ khỏi ci-checks.yml (www sang repo conan-web); bộ chọn job còn các job dưới đây trừ www. Xem Hai repo.

ci-checks.yml chạy trên mọi pull request vào main: không deploy, không cần secret. Trang này mô tả nó sau Đợt 0 / PR-A của kế hoạch tách repo (14.10.2026). Quy tắc deploy ở Quy tắc triển khai; các gác viết thế nào ở Guardrails và skill ci-guards.

Hình dạng một lượt chạy ​

text
changes (luôn chạy, cả cây, cả lịch sử)
  1. scripts/ci/select-jobs.mjs --github-output   chọn job theo file đổi của PR
  2. scripts/run-guards.mjs --self-test
  3. scripts/run-guards.mjs                       mọi gác ở gốc repo, chạy hết rồi mới báo
        |
        | output chọn job (vẫn tới nơi khi bước 3 đỏ)
        v
api, tool-plane, play, admin-router, admin, com, www, mam, video, sites[...]
  if: !cancelled() && output == 'true'           chạy cả khi gác đỏ
  sparse checkout                                 chỉ phần cây job đó đọc
  npm ci (không rơi về npm install)

Năm điều đổi ở PR-A, và vì sao ​

ĐổiTrướcVì sao
Gác ở một danh sách, scripts/run-guards.mjs19 step trong yml, check:guards có 16, qc-scan đọc lại bằng regexBa danh sách lệch nhau; 16/42 lượt sửa ci-checks.yml chỉ để thêm gác, mỗi lượt từng chạy lại mọi job build
Chạy hết rồi mới báoStep đỏ đầu tiên dừng jobBa nhánh phải push thêm 4 lần chỉ để thấy lỗi kế tiếp
Job build !cancelled()Gác đỏ thì skip mọi build16/25 lượt đỏ: một lỗi gạch dài che luôn lỗi tsc
Bộ chọn job bằng Node, so với điểm rẽ nhánhBash, diff hai chấm, so lượt pushHỏng ba lần im lặng: SIGPIPE (PR #332), job thừa ở 5/32 run, commit merge main chạy TẤT CẢ
Sparse checkout cho job buildCheckout cả repo ~600 MBCheckout chiếm 42% thời gian job; một nửa repo là migration
Chỉ npm ci + gác lockfilesnpm ci || npm install, kể cả lúc deployLockfile lệch không làm đỏ gì; 1.649 run chưa cần đường rơi lần nào
Gác workspace-depsKhông có3/7 lỗi mã thật là import gói không khai hoặc gói dùng chung không được cài

Thêm một gác ​

Thêm một dòng vào GUARDS của scripts/run-guards.mjs:

js
{ id: 'ten-gac', run: [NODE, 'scripts/check-ten-gac.mjs'], fast: true,
  why: 'Sự cố thật đã sinh ra gác này, ngày, PR.' },

Không sửa ci-checks.yml, không sửa check:guards. Script scripts/check-*.mjs không đăng ký thì runner đỏ. Gác dưới ~2 giây thì đánh fast: hook trước git push chạy nhóm đó.

bash
node scripts/run-guards.mjs --list          # danh sách
node scripts/run-guards.mjs                 # chạy đủ (~11 giây ấm, 4 luồng)
node scripts/run-guards.mjs --only seo,jtbd # vài gác
node scripts/run-guards.mjs --fast          # nhóm nhanh, như hook trước push

Thêm một job hay một site ​

  1. Thêm job vào ci-checks.yml, thêm dòng <khoá>: ${{ steps.f.outputs.<khoá> }} vào outputs: của job changes và khoá vào vòng dự phòng ở step f, với if: ${{ !cancelled() && needs.changes.outputs.<khoá> == 'true' }} và sparse checkout (cone, chỉ ghi THƯ MỤC).
  2. Thêm luật vào rules() của scripts/ci/select-jobs.mjs: mọi đường dẫn job đó ĐỌC (thư mục app, gói dùng chung nó import, deploy-<app>.yml). Site không có job riêng thì thêm vào mảng SITES.
  3. node scripts/ci/select-jobs.mjs --self-test: mẫu 15 đỏ nếu thiếu bất kỳ chỗ nào trong bốn chỗ (luật, outputs, if:, vòng dự phòng). Thiếu dòng outputs thì job bị skip ở mọi PR mà run vẫn xanh.

.nvmrc (bản Node của mọi job và mọi deploy) đổi thì bộ chọn bật tất cả, và mọi workflow deploy chạy lại.

Thử bộ chọn bằng tay:

bash
node scripts/ci/select-jobs.mjs --files packages/design-system/theme.css,api.conan.school/migrations/x.sql

Những gì job api đọc ngoài thư mục của nó ​

Không chép tay: select-jobs.mjs QUÉT từ mã. Thư mục content/ mà src import (hiện chỉ content/bmc), file migration mà tests đọc (dựng bảng SQLite trong bộ nhớ), và file ngoài api mà test đọc (router của tokyo và www, danh mục bất biến D1 ở scripts/lib/). Những thứ đó bật job api; mọi migration khác và nguồn nội dung thì không.

Job api lấy cây sparse NỀN (bỏ migrations/, content/ trừ bmc, assets/), rồi bước select-jobs.mjs --print-api-reads | xargs git sparse-checkout add lấy thêm đúng những file kể trên, rồi --assert-api-sparse kiểm lại. Ngày 14.10.2026 danh sách chép tay đã phải sửa ba lần vì ba PR thêm test đọc file mới; giờ test mới đọc file nào thì tự được lấy theo.

Bẫy đã gặp khi dựng ​

Phần lớn bẫy dưới đây do một vòng rà soát đối kháng tìm ra trước khi push (17 phát hiện, mỗi cái được một agent khác tái hiện): rà soát kiểu đó rẻ hơn nhiều so với một lượt CI đỏ trên main.

  • npm run -s làm gác lockfiles xanh giả: npm con nhận loglevel silent, thoát 1 mà không in gì.

  • Build trước khi cài gói dùng chung ở job admin và com: deploy chỉ cài thư mục app.

  • Hết giờ phải giết cả nhóm tiến trình: python của check-whats-new giữ ống stdio, runner chờ nó (trước 17.10.2026 là check-jtbd, nay ở repo conan-content).

  • Job coursepack (CoursePack + PlaybookPack --check) đã gỡ ngày 17.10.2026: nguồn nội dung và bộ biên dịch sang repo conan-content, CI bên đó chạy phép kiểm này (repo-slim).

  • Gác của phiên khác tự kiểm "đã được nối" bằng cách đọc yml: check-sentry (luật m) và check-email-docs đòi một step run: node scripts/... trong ci-checks.yml và một npm run check:x trong check:guards. Sau khi gác chuyển vào runner, cả hai đỏ dù gác vẫn chạy. Gác mới hãy hỏi GUARDS của run-guards (hoặc gọi selectJobs), đừng đọc chữ yml.

  • Hai phiên cùng ngày dựng hai gác lockfile trùng tên (PR #431 và PR-A, 14.10.2026). Khi gộp, giữ bản đã lên main (kiểm NGHIÊM ở mọi thư mục, ngoại lệ phải khai ALLOW_LEGACY_PEERS kèm lý do). Gác mới của #431 (sentry, hooks, smoke-production, d1-invariants) chuyển từ step yml vào GUARDS; luật "gác đã được nối" của check-sentry giờ hỏi GUARDS và gọi thẳng selectJobs, không đọc chữ yml.

  • Danh sách sparse viết tay lệch mã: #431 cho tool-plane import packages/observability, sparse của job chỉ có conan-tool-plane, lượt CI đầu sau khi gộp đỏ TS2307. Gác sparse giờ so tập file mỗi job build (từ analyze của check-workspace-deps) với mẫu sparse của job.

  • Ghim paths trong tsconfig của app ("zod": ["./node_modules/zod"]) cho gói dùng chung import type gói đó mà không cần cài gói dùng chung; gác workspace-deps tha đúng trường hợp đó (import giá trị vẫn đỏ, vì Vite không đọc paths).

  • Cây sparse làm gác xanh giả. git ls-files vẫn liệt kê file không có trên đĩa, còn check-no-emdash và qc-scan bỏ qua file vắng. Vì vậy job changes luôn lấy cả cây, và run-guards từ chối chạy khi core.sparseCheckout bật.

  • Cone mode với đường dẫn FILE lấy cả thư mục chứa nó, không báo gì: ghi api.conan.school/migrations/a.sql ở cone là kéo về 318 MB. File lẻ chỉ ghi ở non-cone.

  • always() thay vì !cancelled() sẽ build tiếp ở lượt đã bị cancel-in-progress huỷ.

  • Glob khớp 0 file: node --test "tests/*.test.ts" in # tests 0 và thoát 0. Job tool-plane có bước đếm file test; CoursePack/PlaybookPack 0 pack là ::error::.

  • cwd của gác: check-no-emdash, check-analytics, check-worker-observability tính từ cwd; chạy từ một thư mục con thì chúng thoát 0 mà chỉ soi thư mục con. run-guards luôn chạy ở gốc repo.

Số liệu nền (07.09 tới 13.10.2026) ​

ci-checks p50 1,52 phút, p90 1,90; xếp hàng 3 giây. 25/351 lượt đỏ, 0 chập chờn, 18 do gác chính sách. Khoảng 2.200 phút tính tiền mỗi 30 ngày trên hạn mức 2.000 của gói Free, 15 startup_failure. Đo lại 14 ngày sau khi merge PR-A, so với bảng "Chỉ số thành công" của kế hoạch.

Tài liệu nội bộ nền tảng Conan School.