Quy trình Git của Signet
Áp dụng cho cả đội (Backend · AI · Frontend) và người đóng góp bên ngoài. Mục tiêu:
mainchỉ chứa các bản đã phát hành,developlúc nào cũng chạy được, lịch sử commit đọc được, và bản thân repo là bằng chứng cho các tiêu chí PoF #1 (dùng repo thật), #3 (phát hành theo phiên bản), #6 (bug tracker, changelog).
0. Tóm tắt một màn hình
issue #12 ─► nhánh feat/12-keycloak-realm (tách từ develop) ─► commit nhỏ ─► PR vào develop
│
CI xanh + 1 review ─► squash merge vào develop
│
cuối mỗi phase: PR develop → main "chore(release): vX.Y.Z" ─► merge commit ─► thẻ vX.Y.Z trên main
│
GitHub Release (.tar.gz) ◄── Actions ◄─────────────────────────┤
│
đưa lên server: nhánh deploy theo thẻ ◄────────┘
lỗi gấp sau phát hành: hotfix/X.Y.Z từ main ─► PR vào main ─► thẻ ─► PR main → develop (gộp ngược)
7 luật vàng:
- Không ai push thẳng lên
develophaymain. Mọi thay đổi đi qua Pull Request;mainchỉ nhận PR phát hành (mục 6). - Mỗi việc bắt đầu từ một issue. Nhánh và commit đều ghi số issue.
- Commit message theo Conventional Commits (mục 3).
- Mỗi PR cần CI xanh và 1 người ở mảng khác duyệt.
- Tài liệu của tính năng nằm trong cùng PR với mã.
- Không bao giờ commit bí mật,
.env, dữ liệu thật,node_modules/, trọng số mô hình, CSDL chữ ký ClamAV. - Từ 17:00 06/12/2026 không push gì lên repo này nữa — kho mã được chấm 7–9/12.
1. Mô hình nhánh
Đội dùng Git Flow rút gọn: tính năng gộp vào develop, main chỉ nhận bản phát hành. Người tải
mã từ main hay từ trang Release luôn nhận một phiên bản đã chốt, có ghi chép thay đổi; phần đang làm
dở nằm ở develop. So với Git Flow đầy đủ, đội bỏ nhánh release/*: với 3 người, việc chốt bản phát
hành (chuyển mục CHANGELOG) làm bằng một PR vào develop, rồi PR thẳng develop → main.
| Nhánh | Vai trò | Ai được ghi | Cách ghi |
|---|---|---|---|
develop | Nhánh mặc định: mã mới nhất, luôn chạy được | Không ai push trực tiếp | Squash merge từ PR |
main | Chỉ các bản đã phát hành; mỗi commit gộp trên main mang một thẻ | Không ai push trực tiếp | Merge commit từ PR phát hành (mục 6) |
deploy | Cho máy chủ bật reconciler GitOps theo nhánh: đúng phiên bản máy đó chạy. Chưa tạo, vì bản online của đội theo thẻ phát hành trên main (mục 7) | Backend | Theo thẻ phát hành (mục 7) |
<loại>/<issue>-<mô-tả> | Nhánh làm việc tách từ develop, sống ≤ 3 ngày | Người làm issue | Commit, push thoải mái |
hotfix/X.Y.Z | Sửa gấp một lỗi của bản đã phát hành, tách từ main | Người sửa | PR vào main, rồi gộp ngược vào develop |
Thẻ vX.Y.Z | Bản phát hành, gắn trên main | Backend | make release (mục 6); không sửa, không xóa được |
2. Đặt tên nhánh
<loại>/<số-issue>-<mô-tả-ngắn>
<loại>giống loại commit (mục 3.2):feat,fix,docs,refactor,test,build,ci,chore.- Chữ thường, không dấu, nối bằng gạch ngang, tối đa ~5 từ.
- Nhánh sửa gấp bản đã phát hành:
hotfix/<phiên-bản-mới>, ví dụhotfix/1.1.1. - CI (job "Quy trình nhánh và tiêu đề PR") từ chối PR vào
developtừ nhánh không có<loại>/, có chữ hoa hay có dấu; thiếu số issue thì chỉ nhắc.
| Đúng | Sai |
|---|---|
feat/12-keycloak-realm | feature-moi (không loại, không issue) |
fix/31-clamav-timeout | fix/sửa-lỗi-quét-virus (có dấu) |
docs/40-huong-dan-api | hoang-test (không rõ làm gì) |
3. Viết commit message
3.1. Định dạng
<loại>(<phạm vi>): <mô tả ngắn>
<thân — tùy chọn: VÌ SAO thay đổi, không chỉ LÀM GÌ>
<chân — tùy chọn: Closes #12 · Refs #12 · Co-authored-by: … · BREAKING CHANGE: …>
- Dòng đầu ≤ 72 ký tự, viết tiếng Anh, bắt đầu bằng động từ ở thể mệnh lệnh (
add,fix,move…), chữ thường, không chấm cuối. Thân commit cũng viết tiếng Anh. - Thân và chân cách dòng đầu một dòng trống; xuống dòng ở khoảng 72 ký tự.
Closes #12(từ khóa tiếng Anh) để GitHub tự đóng issue khi merge vào nhánh mặc địnhdevelop;Refs #12để chỉ liên kết.
Từ 23/10/2026 (sau v1.0.0), commit, tiêu đề và mô tả PR viết tiếng Anh: lịch sử Git là thứ người ngoài đội —
cộng đồng nguồn mở, người muốn đóng góp — đọc đầu tiên, và mô tả PR thành thân commit khi Squash and merge.
Commit trước mốc này giữ nguyên tiếng Việt, không viết lại lịch sử. Issue viết tiếng Việt, tiêu đề kèm bản tiếng
Anh trong ngoặc; tài liệu, CHANGELOG.md và giao diện vẫn tiếng Việt vì người dùng là cơ quan, trường học Việt Nam.
3.2. Loại commit
| Loại | Dùng khi | Ghi vào CHANGELOG mục |
|---|---|---|
feat | Thêm tính năng người dùng thấy được | Đã thêm |
fix | Sửa lỗi | Đã sửa lỗi (lỗi bảo mật → Bảo mật) |
perf | Tăng hiệu năng, không đổi hành vi | Đã sửa đổi |
refactor | Đổi cấu trúc mã, không đổi hành vi | — |
docs | Chỉ sửa tài liệu | — |
test | Thêm hoặc sửa test | — |
build | Dockerfile, docker-compose.yml, Makefile, phiên bản gói | Đã sửa đổi (nếu ảnh hưởng người cài) |
ci | .github/workflows/ | — |
chore | Việc vặt khác: dọn dẹp, phát hành | — |
revert | Hoàn tác một commit trước | — |
Mỗi thay đổi ghi một dòng trong nhóm tương ứng của CHANGELOG (tối đa 120 ký tự): nói thay đổi gì với người dùng,
kèm số issue hoặc PR, ví dụ - Trang tài liệu Docusaurus đăng lên GitHub Pages (#342). Chi tiết cài đặt, số test,
biến môi trường để ở PR và tài liệu của tính năng.
3.3. Phạm vi
| Phạm vi | Bao gồm | Mảng chính |
|---|---|---|
gateway | APISIX: route, plugin | Backend |
auth | Keycloak: realm, vai trò, mapper, trang đăng nhập | Frontend · Backend |
db | Schema, migration, metadata Hasura, ràng buộc, sổ kiểm toán | AI |
workflow | Flowable, svc-workflow, services/svc-workflow/processes/*.bpmn | Backend |
files | svc-files, SeaweedFS, ClamAV, tìm kiếm | AI |
realtime | Centrifugo | Backend |
office | apps/office — giao diện web | Frontend |
ca-nhan | apps/ca-nhan — Không gian cá nhân P.A.R.A, ứng dụng thứ hai trên lõi | Frontend |
bang-tin | apps/bang-tin — Bảng tin đơn vị, ứng dụng thứ ba, cắm bằng moc app install | Frontend · Backend |
nghi-phep | apps/nghi-phep — Đơn nghỉ phép, ứng dụng thứ tư, quy trình chạy trên lõi (#301) | Frontend · Backend |
lich-giang | apps/lich-giang — Đổi, bù lịch giảng, ứng dụng ngành (Tầng 3), hai cấp duyệt trên lõi | Frontend · Backend |
sinh | apps/sinh — Ứng dụng sinh từ bảng: menu, danh sách, biểu mẫu dựng từ /rest/v1 của PostgREST (#560) | Frontend |
ai | Trợ lý hỏi đáp, đọc văn bản đến, OCR trong svc-files (assist/, docread/, ocr.py); Ollama | AI |
data | Nạp lô và nguồn công khai (imports/, open_data.py), nộp lưu nóng → lạnh (archive.py), phả hệ (lineage.py) | AI |
ops | Compose, reconciler, sao lưu, giám sát, OpenBao | Backend |
cli | Lệnh scripts/moc.py | Backend |
scripts | Các kịch bản khác trong scripts/: seed, smoke, demo, phát hành | Ai cũng được |
e2e | Kiểm thử trình duyệt Playwright, tests/e2e/ | Frontend |
ci | CI/CD | Backend |
deps | Nâng hoặc ghim phiên bản, DEPENDENCIES.md | Ai cũng được |
docs | Tài liệu chung | Ai cũng được |
release | Phát hành | Backend |
Thay đổi chạm nhiều phạm vi → chọn phạm vi chính, hoặc bỏ trống: feat: …. CI kiểm tiêu đề PR theo
đúng hai bảng trên (scripts/check_pr.py): thêm loại hay phạm vi thì sửa cả hai nơi.
3.4. Ví dụ
Tốt:
feat(workflow): add a director approval step above the amount threshold
Read the threshold from DXOS_APPROVAL_THRESHOLD instead of hard-coding
it in the BPMN, so that each organization sets it by its own rules.
Closes #27
fix(files): block files when ClamAV times out instead of storing them
A scan that timed out still stored the file, against the zero-trust
principle. Treat such a file as unsafe and return 503.
Closes #41
docs(readme): add the problem statement and the feature table
Chưa tốt:
| Commit | Vấn đề | Nên viết |
|---|---|---|
update | Không nói gì | fix(office): show the Approve button on small screens |
fix bug | Lỗi nào, ở đâu? | fix(auth): stop the redirect loop when the token expires |
WIP | Không dùng cho commit đã merge | Gộp lại khi squash merge (mục 5) |
feat: finish phase 1 | Quá to, không review được | Tách thành nhiều PR nhỏ |
3.5. Mẫu commit cho trình soạn thảo
Lưu khối dưới đây thành tệp ~/.gitmessage-moc, rồi chạy
git config commit.template ~/.gitmessage-moc trong thư mục repo. Khi gõ git commit (không -m),
trình soạn thảo mở sẵn mẫu; các dòng bắt đầu bằng # tự bị bỏ.
# <loại>(<phạm vi>): <mô tả ngắn bằng tiếng Anh, không chấm cuối; cả dòng ≤ 72 ký tự>
#
# Loại: feat fix perf refactor docs test build ci chore revert
# Phạm vi: gateway auth db workflow files realtime office ca-nhan bang-tin nghi-phep lich-giang sinh ai data ops cli
# scripts e2e ci deps docs release
#
# Thân (tùy chọn, tiếng Anh): VÌ SAO thay đổi. Xuống dòng ở 72 ký tự.
#
# Chân (tùy chọn):
# Closes #<số-issue> tự đóng issue khi merge vào develop
# Refs #<số-issue> chỉ liên kết
# Co-authored-by: Tên <email> khi làm cặp
# BREAKING CHANGE: <mô tả> khi phá vỡ API hoặc cấu hình cũ
4. Commit thế nào cho đúng
4.1. Cấu hình Git một lần cho mỗi người
git config --global user.name "Họ Tên Thật"
git config --global user.email "email-đã-xác-minh-trên-GitHub" # để commit được tính cho đúng người
git config --global pull.rebase true
git config --global rebase.autoStash true
git config --global core.autocrlf input # luôn lưu LF, kể cả trên Windows
4.2. Trước mỗi commit
make license-fix # tự chèn header Apache-2.0 vào tệp mã mới
make license-check # PoF #2 — CI cũng chạy bước này
make check # môi trường + cú pháp Compose
git diff --staged # đọc lại đúng những gì sắp commit
Có test cho phần mình sửa thì chạy test trước khi commit.
make license-check in CẢNH BÁO loại tệp chưa hỗ trợ → bổ sung loại tệp đó vào
scripts/license-header.sh trong cùng PR, đừng bỏ qua. Sửa tệp .bpmn bằng trình vẽ quy trình
xong thì chạy lại make license-fix, vì trình vẽ có thể xóa chú thích header.
4.3. Nguyên tắc
- Một commit = một thay đổi logic. Không trộn sửa định dạng với sửa logic.
- Sau mỗi commit, mã vẫn dựng được.
- Push nhánh ít nhất mỗi ngày khi đang làm — vừa sao lưu, vừa để cả đội thấy tiến độ.
- Làm cặp thì thêm
Co-authored-by: Tên <email>vào chân commit để cả hai cùng được ghi nhận. - Thêm phần mềm thượng nguồn mới → cùng PR phải cập nhật
DEPENDENCIES.md. - Thêm tham số cấu hình → cùng PR phải cập nhật
.env.example.
4.4. Không bao giờ commit
| Thứ | Lý do |
|---|---|
.env, khóa, mật khẩu, token | Lộ bí mật — repo công khai |
node_modules/, .venv/, dist/ | Vi phạm PoF #5 (bundle) — CI chặn node_modules, vendor, third_party |
data/, volumes/, bản dump CSDL thật | Dữ liệu cá nhân của đơn vị chạy thử |
| Trọng số mô hình AI, CSDL chữ ký ClamAV | Tải lúc chạy, không đóng gói vào repo hay release |
| Tệp nhị phân lớn (video, ảnh demo nặng) | Làm phình repo; để ở nơi khác và dẫn link |
5. Pull Request
5.1. Các bước
git switch develop && git pull # 1. lấy develop mới nhất
git switch -c feat/12-keycloak-realm # 2. tạo nhánh từ issue #12
# ... sửa, commit nhỏ ...
git fetch origin && git rebase origin/develop # 3. cập nhật theo develop trước khi mở PR
git push -u origin feat/12-keycloak-realm # 4. đẩy nhánh
gh pr create --base develop --fill # 5. mở PR vào develop (nhánh mặc định của repo)
5.2. Yêu cầu với PR
- Tiêu đề PR = commit message dòng đầu, theo mục 3 — vì khi squash merge, tiêu đề PR trở thành
commit duy nhất trên
develop. CI kiểm tiêu đề mỗi lần mở hay sửa PR. - Mô tả điền theo mẫu
.github/pull_request_template.mdvà có dòngCloses #<issue>. - Gắn nhãn mảng (
backend/ai/frontend) và milestone của phase hiện tại. - PR nhỏ: lý tưởng dưới ~400 dòng thay đổi. To hơn thì tách.
- Còn dở dang nhưng muốn xin ý kiến sớm → mở Draft PR.
5.3. Review
| PR của | Người review | Lý do |
|---|---|---|
| Backend (@Vanvuong2005827) | AI (@HoangTechCS-AIE) | AI dùng trực tiếp API và dữ liệu do Backend dựng |
| AI (@HoangTechCS-AIE) | Frontend (@nguyenlehoainam) | Frontend hiển thị dữ liệu, tìm kiếm, trợ lý do AI làm |
| Frontend (@nguyenlehoainam) | Backend (@Vanvuong2005827) | Backend nắm Keycloak, gateway, luồng gọi API |
- Review trong vòng 24 giờ. Không kịp thì nhắn người khác review thay.
- Người review chạy thử theo mục "Cách kiểm chứng" trong mô tả PR, không chỉ đọc mã.
- Góp ý nhỏ, không chặn → ghi "nit:" và vẫn duyệt.
5.4. Merge
- Điều kiện: CI xanh (8 job: 7 job của workflow CI — Giấy phép, Dựng từ mã nguồn, Tài liệu, Test đơn vị dịch vụ,
Schema và quyền trên CSDL thật, Chạy thử toàn hệ thống, Quét bí mật và lỗ hổng phụ thuộc — và job "Quy trình nhánh và
tiêu đề PR" của workflow PR) + 1 duyệt. Ruleset của repo chặn nút merge tới khi đủ. Job "Chạy thử toàn hệ thống"
gom sáu phần chạy song song trên sáu máy (
Chạy thử: luong-chinh,e2e,ai,sao-luu,cai-dat,https; phần nào cũng tự dựng và nạp mẫu), mất khoảng 11 phút; PR chỉ đổi tài liệu (*.md, ảnh trongdocs/, trừdocs/api/) hay không đổi tệp nào không dựng hệ thống, job này đạt ngay. Lỗi thì tảistack-logs-<phần>ở trang lượt chạy để xem nhật ký từng dịch vụ và vết Playwright. - Cách merge vào
develop: Squash and merge — mọi commit nhỏ của nhánh gộp thành một commit sạch. Riêng PR gộp ngược (mục 6) và PR chỉ ghi lịch sử một nhánh vàodevelop(commit gộpgit merge -s ours, không đổi tệp nào, như #294) dùng Create a merge commit: squash bỏ mất đúng phần lịch sử cần giữ. - Cách merge vào
main(PR phát hành, mục 6): Create a merge commit, giữ nguyên các commit củadevelop. - Người mở PR tự merge sau khi được duyệt; nhánh làm việc tự xóa sau khi gộp.
6. Phát hành
Lịch: v0.1.0 18/10 · v1.0.0 23/10 · v1.1.0 25/10 · v1.2.0 27/10 · v1.2.1 28/10, bản vá · v1.3.0 30/10, bản
cho vòng trường · v1.4.0 31/10, bản online tự nâng theo bản phát hành (bảy bản đã phát hành) · v1.5.0 03/12, bản
nộp chấm (mục 8). Các lệnh dưới đây lấy 1.1.0 làm ví dụ. Gói phát hành tên signet-X.Y.Z.tar.gz từ v1.1.0; gói của
v0.1.0, v1.0.0 giữ tên cũ moc-X.Y.Z.tar.gz (không gắn lại thẻ đã đẩy).
Đây là lúc duy nhất main nhận thay đổi.
# 1. Chốt ghi chép thay đổi trên develop: PR (nhánh chore/<issue>-chot-1-1-0) chuyển các dòng của mục
# "[Chưa phát hành]" trong CHANGELOG.md sang "## [1.1.0] - <ngày phát hành>", viết 3–8 dòng tóm tắt ngay dưới
# tiêu đề đó (trang Release hiện phần này, phần còn lại gập lại), để lại tiêu đề
# "## [Chưa phát hành]" trống (CI đòi có mục này)
# tiêu đề PR: chore(release): finalize the changelog for v1.1.0 → review → squash merge vào develop
# 2. PR phát hành develop → main, tiêu đề đúng "chore(release): v1.1.0"
gh pr create --base main --head develop --title "chore(release): v1.1.0" --body "Release v1.1.0"
# CI kiểm: nguồn là develop, CHANGELOG có mục [1.1.0] có dòng thay đổi, thẻ v1.1.0 chưa có
# → review → Create a merge commit
# 3. Gắn thẻ trên main mới nhất, cây làm việc sạch:
git switch main && git pull
# Ký thẻ (máy chủ đặt MOC_GITOPS_ALLOWED_SIGNERS chỉ nhận thẻ ký SSH, van-hanh.md mục GitOps, #492) — một lần:
# git config --global gpg.format ssh && git config --global user.signingkey ~/.ssh/id_ed25519.pub
make release VERSION=1.1.0 # tạo thẻ v1.1.0 (có khóa thì git tag -s); thẻ không ký SSH (không khóa, khóa GPG)
# thì cảnh báo trước dòng "Bước tiếp theo": máy chủ sẽ không nâng — gỡ thẻ, đặt khóa SSH như trên, chạy lại (#514)
# + gói .tar.gz + .sha256 trong dist/ để kiểm tra
# rồi kiểm chính gói vừa đóng (scripts/kiem_goi.py, xem dưới): không đạt thì gỡ thẻ v1.1.0, xóa gói, in từng lỗi —
# sửa qua PR như thường rồi chạy lại
# 4. Đẩy thẻ — GitHub Actions kiểm thẻ nằm trên main, chưa có Release, đóng gói và kiểm lại gói bằng cùng
# scripts/kiem_goi.py, rồi mới tạo Release kèm .tar.gz, SBOM .cdx.json (make sbom) và .sha256 của cả hai
git push origin v1.1.0
# 5. Gộp ngược main vào develop để thẻ nằm trong lịch sử develop. Không làm thì bản clone develop (nhánh mặc định)
# có `git describe` ra thẻ cũ (v0.1.0-…), và scripts/install.sh lấy chuỗi đó làm phiên bản khi cài từ bản clone.
# main chỉ thêm commit gộp phát hành, cây giống hệt develop lúc phát hành, nên không xung đột
gh pr create --base develop --head main --title "chore(release): merge v1.1.0 back into develop" --body "Refs #N"
# → review → Create a merge commit (không squash: squash không đưa thẻ vào lịch sử develop)
Bản vá nóng — lỗi của bản đã phát hành cần sửa ngay, không chờ phase sau:
git fetch origin && git switch -c hotfix/1.1.1 origin/main
# sửa, commit "fix(…): …"; thêm mục "## [1.1.1] - <ngày>" ngay dưới "## [Chưa phát hành]" của CHANGELOG.md,
# ghi dòng sửa vào đó
git push -u origin hotfix/1.1.1
gh pr create --base main --title "chore(release): v1.1.1" --body "Urgent fix …" # → review → merge commit
git switch main && git pull && make release VERSION=1.1.1 && git push origin v1.1.1
# gộp ngược vào develop qua một nhánh riêng (không sửa thẳng develop hay main được):
git switch -c chore/gop-nguoc-1-1-1 origin/develop && git merge --no-ff origin/main
# CHANGELOG.md xung đột ở đầu tệp thì giữ cả mục [Chưa phát hành] của develop lẫn mục [1.1.1]
git push -u origin chore/gop-nguoc-1-1-1
gh pr create --base develop --title "chore(release): merge v1.1.1 back into develop" --body ""
# → review → Create a merge commit (không squash: giữ quan hệ cha con để lần phát hành sau không xung đột)
- Phiên bản theo Semantic Versioning: số giữa tăng khi có tính năng mới, số cuối cho bản vá nóng, số đầu chỉ tăng khi bản cài cũ không nâng cấp tại chỗ được.
release.ymldựng trang Release bằngscripts/release_body.py: dòng đầu ghi tệp chính thức làsignet-X.Y.Z.tar.gz(GitHub luôn tự gắn thêm "Source code (zip)" mà PoF #3 trừ điểm định dạng không mở), rồi phần tóm tắt, toàn bộ ghi chép thay đổi gập lại.- Kiểm gói trước khi phát hành (
scripts/kiem_goi.py,make releaseở máy và bước "Kiểm gói trước khi phát hành" củarelease.yml, ngay sau bước đóng gói): kiểm chính tệpsignet-X.Y.Z.tar.gz, không phải cây git. Giải nén vào thư mục tạm (mục nào là symlink, đường tuyệt đối,..thì dừng trước khi giải nén) rồi chặn: tệp môi trường thật (chỉ phát hành tệp mẫu*.example), khóa riêng (id_rsa,*.key,*.p12…, và mọi tệp có khối PEMPRIVATE KEY), tài liệu nội bộ*.local*,COMMIT.md,publics/,node_modules/,__pycache__/, dữ liệu chạy ở gốc (backups/,data/…), tệp CSDL, tệp lớn hơn 5 MiB (tệp lớn nhất lúc viết 0,44 MB). Giấy phép: cóLICENSE,NOTICE;scripts/license-header.sh check <thư mục gói>; mọi tệp cóSPDX-License-Identifiervà bản quyền trong tệp, hoặc đượcREUSE.tomlkhai. Cuối cùng.sha256khớp và ghi đúng tên gói, thư mục gốc làsignet-X.Y.Z/, tệpVERSIONcủa gói ghivX.Y.Z. Tệp phải theo dõi trong git nhưng không được vào gói: thêm dòng<đường dẫn> export-ignorevào.gitattributes. - Phiên bản trong gói:
git archiveđiền thẻ vào dòng cuối củaVERSION(.gitattributes:VERSION export-subst),make installtừ gói giải nén đọc dòng đó, không cần đặt tayMOC_VERSION; bản clone git vẫn lấygit describe. Gói cũ chưa có tệpVERSION(đếnv1.1.0) thì vẫn đặt tay. - Không xóa hay gắn lại thẻ đã đẩy — ruleset thẻ
v*chặn hẳn,release.ymlvàrelease.shcũng từ chối phát hành lại. Phát hành nhầm thì ra bản vávX.Y.Z+1.
7. Đưa lên server (nhánh deploy)
Có ba cách, cách nào cũng đi qua scripts/nang_cap.sh nang hoặc install.sh của đúng thẻ:
- Theo thẻ phát hành — bản online của đội (signet.htechcs.com) dùng cách này từ 31/10 (#344). Reconciler chạy mỗi
phút bằng cron của người dùng
moc(máy Windows qua WSL2 không có systemd,trien-khai.mdmục 7), đặtMOC_GITOPS_BRANCH=main,MOC_GITOPS_FOLLOW=release,MOC_GITOPS_MODE=nang-cap. ThẻvX.Y.Zmới trênmainlà bản online tự nâng: sao lưu, nâng, kiểm (có smoke), lỗi sau khi sao lưu thì tự lùi về bản trước (van-hanh.mdmục GitOps). Người phát hành không làm gì thêm sau khi đẩy thẻ; job "Kiểm bản online qua HTTPS" sau Release kiểm lại từ bên ngoài. Smoke tạo văn bản thử trên bản online ở mỗi lần nâng. Bảo trì tay thì tắt dòng cron trước (crontab -e, thêm#). - Nâng tay — tải gói của thẻ trên trang Release rồi chạy
scripts/nang_cap.sh nang(trien-khai.mdmục 7): sao lưu trước, kiểm sau khi nâng, lùi về được. Bản online dùng cách này tớiv1.4.0. - Nhánh
deploy— cho máy chủ bật reconciler GitOps theo nhánh (van-hanh.mdmục GitOps): reconciler kéo nhánhdeploymỗi 60 giây. Chỉ Backend (@Vanvuong2005827) đẩy nhánh này. Repo chưa có nhánhdeploy.
git fetch origin
git switch deploy && git pull --ff-only
git merge --ff-only v1.1.0 # thẻ phát hành trên main
git push origin deploy
Máy chủ bật kiểm chữ ký (MOC_GITOPS_ALLOWED_SIGNERS, van-hanh.md mục GitOps) thì đầu nhánh phải là commit do người
trong danh sách ký bằng khóa SSH — thay dòng --ff-only bằng:
git merge --no-ff -S -m "deploy: v1.1.0" v1.1.0 # -S: ký bằng user.signingkey (gpg.format=ssh)
git verify-commit HEAD # thấy Good "git" signature trước khi đẩy
- Chỉ fast-forward (bật kiểm chữ ký: chỉ commit gộp có ký như trên), không commit thẳng lên
deploy. - Lỗi trên server → bản vá nóng (mục 6), rồi đưa
deploylên thẻ mới. - Tạo nhánh lần đầu:
git push origin v1.1.0^{commit}:refs/heads/deploy(bật kiểm chữ ký thì máy chủ chỉ áp từ lần gộp có ký đầu tiên).
8. Các mốc đóng băng
| Từ | Quy tắc |
|---|---|
| 27/10 | Phát hành v1.2.0, bản cho vòng trường 01/11, sớm hơn lịch cũ 30/10 (#354); không đóng băng 28–29/10 nữa, develop đi tiếp tới v1.3.0 |
| 28/10 | Bản vá v1.2.1: đúng bản đang chạy online (giao diện Lịch hạn), không thêm tính năng (#370) |
| 30/10 | Phát hành v1.3.0, bản cho vòng trường 01/11 (#419); bản nộp chấm 03/12 đổi thành v1.4.0 |
| 31/10 | Phát hành v1.4.0: vá pypdf, bản online tự nâng theo bản phát hành (#471); bản nộp chấm 03/12 đổi thành v1.5.0 |
| 26/11 | Đóng băng tính năng: chỉ nhận PR fix, docs, test, chore(release) |
| 03/12 | Phát hành v1.5.0, bản nộp chấm. Sau đó mỗi PR sửa lỗi gộp vào develop đi kèm một bản vá v1.5.x trước 17:00 06/12: lúc chấm, develop (nhánh mặc định, giám khảo clone) và main cùng nội dung |
| 05/12 → 10/12 | Không đẩy deploy trừ khi cả đội đồng ý (sự cố chặn demo) |
| 17:00 06/12 | Không push bất cứ gì lên repo này — kể cả nhánh mới. Slide, video làm ở repo riêng |
9. Xử lý tình huống thường gặp
| Tình huống | Cách xử lý |
|---|---|
Nhánh bị tụt so với develop | git fetch origin && git rebase origin/develop, sửa xung đột, git push --force-with-lease |
Lỡ mở PR vào main | Bấm Edit cạnh tiêu đề PR, đổi nhánh đích sang develop — CI chặn PR không phải phát hành vào main |
Xung đột ở tệp khóa (package-lock.json…) | Lấy bản của develop, chạy lại trình cài gói để sinh lại tệp khóa, commit |
| Viết sai commit message, chưa push | git commit --amend |
| Viết sai commit message, đã push nhánh của mình | git commit --amend rồi git push --force-with-lease (không bao giờ làm trên develop, main) |
| Lỡ commit bí mật | Đổi bí mật đó ngay (coi như đã lộ), báo cả đội, rồi mới gỡ khỏi mã |
| PR đã merge gây lỗi | Mở PR revert vào develop: git revert <sha> — không sửa lịch sử develop, main. Lỗi đã nằm trong bản phát hành → bản vá nóng (mục 6) |
| Muốn bỏ hết thay đổi chưa commit | git restore . (kiểm tra kỹ trước — không lấy lại được) |
10. Thiết lập GitHub một lần (người quản trị repo)
- Nhánh mặc định là
develop(Settings → General → Default branch, haygh repo edit --default-branch develop): PR mới tự trỏ vàodevelop, và nhánh mặc định không bao giờ bị tự xóa sau khi gộp PR phát hành. Không đểmainlàm mặc định: GitHub chỉ đọcCloses #Ntrong PR nhắm vào nhánh mặc định, PR vào nhánh khác không nối, không đóng được issue. Giám khảo clone nhánh mặc định: ruleset giữdevelopluôn xanh CI, và lúc chấmdevelopcùng nội dung vớimain(mục 8). - Ruleset (Settings → Rules → Rulesets), khai sẵn trong
.github/rulesets/, tạo bằnggh api -X POST repos/HIT-Champion/Signet/rulesets --input .github/rulesets/<tệp>.json:develop.json— nhánhdevelop: bắt buộc PR, 1 duyệt, 8 job CI xanh (mục 5.4); chỉ squash hoặc merge commit; cấm force push, cấm xóa;main.json— nhánhmain: như trên nhưng chỉ merge commit; job "Quy trình nhánh và tiêu đề PR" chỉ cho PR phát hành vào;tags.json— thẻv*: không ai sửa hay xóa được, kể cả quản trị;tag-creation.json— thẻv*: chỉ quản trị repo được tạo.
- Bảo vệ nhánh
deploy: cấm force push; chỉ Backend được push. - Merge (Settings → General → Pull Requests):
- bật Allow merge commits và Allow squash merging, tắt Allow rebase merging;
- nội dung commit mặc định của cả hai lấy tiêu đề + mô tả PR;
- bật Automatically delete head branches;
- bằng lệnh:
gh repo edit --enable-merge-commit --enable-squash-merge --enable-rebase-merge=false --delete-branch-on-merge, rồigh api -X PATCH repos/HIT-Champion/Signet -f merge_commit_title=PR_TITLE -f merge_commit_message=PR_BODY -f squash_merge_commit_title=PR_TITLE -f squash_merge_commit_message=PR_BODY.
- Code security (Settings → Code security): bật Secret scanning và Push protection (miễn phí với repo
công khai), chặn từ lúc push thay vì đợi job gitleaks của CI; bật Private vulnerability reporting để người ngoài
báo lỗ hổng riêng tư theo
SECURITY.md— bằng lệnh:gh api -X PUT repos/HIT-Champion/Signet/private-vulnerability-reporting; kiểm:gh api repos/HIT-Champion/Signet/private-vulnerability-reportingtrả{"enabled": true}. - Issues:
- bật Issues;
- tạo nhãn
backend,ai,frontend,bug,feature,docs,good first issue; - tạo milestone
Phase 0…Phase 5theo kế hoạch.
11. Quy trình qua các giai đoạn
| Giai đoạn | Cách làm thực tế |
|---|---|
Đến v1.0.0 (23/10/2026) | Mã được gộp tại máy rồi đẩy lên theo đợt, không qua pull request, không có review trên GitHub. Một đợt đẩy nhiều commit sát nhau thì CI tự hủy các lượt đang chờ (Cancelled), mỗi đợt chỉ lượt cuối chạy đủ; các lượt Cancelled này cùng nhiều lượt chạy khác của giai đoạn này đã bị xóa khỏi trang Actions ngày 23/10/2026. Thẻ v0.1.0 từng bị gắn lại; mỗi lần như vậy workflow Release chạy lại và thay gói .tar.gz cùng .sha256 đã đăng. Commit chuyển sang Git Flow và bản phát hành v1.0.0 cũng đẩy thẳng lên develop, main, trước khi bật ruleset. |
Sau v1.0.0 | Git Flow như các mục trên: mọi thay đổi qua PR vào develop, CI và một người duyệt là bắt buộc (ruleset); main chỉ nhận PR phát hành; thẻ v* không sửa, không xóa được. |
Vì sao đổi: gộp tại máy không để lại dấu vết review nào trên GitHub; commit không nối được với issue
(không có Closes #); main có lúc đỏ CI mà không có gì chặn; và gắn lại thẻ làm hỏng tổng kiểm của
người đã tải bản phát hành. Các luật ở mục 1, 5, 6 và ruleset ở mục 10 chặn đúng những chỗ đó.
Phụ lục — Lệnh hay dùng
git status # đang ở đâu, đã sửa gì
git log --oneline --graph -20 # lịch sử gần đây
git diff / git diff --staged # xem thay đổi chưa / đã stage
git add -p # chọn từng đoạn để commit
git switch -c <nhánh> # tạo và chuyển nhánh
git fetch origin && git rebase origin/develop
git push --force-with-lease # đẩy lại nhánh của mình sau rebase/amend
gh pr create --base develop --fill / gh pr checks / gh pr merge --squash --delete-branch