Skip to content

Tags, Releases & Secrets

Từ góc nhìn production: tag giúp team trả lời chính xác "ta đã ship cái gì?", còn secret policy giúp team tránh câu hỏi đau đớn hơn: "ai đã làm lộ credential vào history?"

Nhiều junior xem git tag là một cái sticker đẹp, còn .gitignore là tấm khiên thần kỳ. Đó là cách suy nghĩ rất dễ dẫn tới incident. Trong production, release là cam kết truy vết được, còn secret handling là cam kết bảo vệ hệ thống và người dùng.


Mục tiêu bài này

Sau bài này, bạn cần nắm được:

  • Tag giải quyết vấn đề gì trong vòng đời phát hành
  • Vì sao annotated tag là lựa chọn mặc định cho release
  • Trực giác versioning cơ bản: khi nào nên nghĩ theo MAJOR.MINOR.PATCH
  • Vì sao .env, credentials, dump dữ liệu, và nhiều file generated không bao giờ được commit
  • Khi làm sai, cần rotate, revoke, communicate, và quyết định giữa cleanup history hay containment như thế nào

1) Vấn đề tags, releases và secret policy thực sự giải quyết

Tags và releases giải quyết gì?

Khi hệ thống gặp lỗi ở production, team thường phải trả lời rất nhanh:

  • Release nào đang chạy ở production?
  • Commit nào tương ứng với bản phát hành đó?
  • Release note của bản đó là gì?
  • Nếu rollback, rollback về đâu?

Nếu không có tag rõ ràng, bạn sẽ rơi vào trạng thái:

  • "Chắc là commit hôm thứ Ba"
  • "Hình như deploy sau khi merge PR thanh toán"
  • "Version trong Slack không khớp với code đang chạy"

Tag tốt biến lịch sử Git thành timeline phát hành có thể kiểm toán được.

Secret policy giải quyết gì?

Secret policy tồn tại để tránh 4 loại thảm họa rất thực tế:

  1. Credential leak: API key, password, private key bị bot quét và lạm dụng
  2. Data leak: dump production, file export, snapshot DB bị commit lên remote
  3. False confidence: nghĩ rằng thêm vào .gitignore là đã an toàn, dù secret đã lọt vào history
  4. Incident kéo dài: team không biết phải rotate, revoke, thông báo, hay cleanup ra sao

Nguyên tắc production-first: Git không chỉ lưu code. Git còn lưu bằng chứng về việc bạn đã ship gì và đã làm lộ gì.


2) Mental model: từ commit đến release

text
Working tree
   -> reviewed commit(s)
   -> tested main / release branch
   -> annotated tag (v1.4.0)
   -> release notes
   -> deployment
   -> monitoring / rollback reference

Tag không thay thế kiểm thử. Tag chỉ đóng dấu: "đây là commit mà team chấp nhận đại diện cho một release cụ thể".


3) Tag là gì và vì sao release nên dùng annotated tag

Lightweight tag vs annotated tag

Loại tagBản chấtDùng khi nàoCó nên dùng cho release?
LightweightChỉ là pointer đến commitMốc cá nhân, tạm thờiKhông nên
AnnotatedLà object riêng trong Git, có message, tagger, thời gianRelease, checkpoint quan trọng

Vì sao annotated tag quan trọng?

Vì release không chỉ là "trỏ vào commit". Release cần metadata:

  • Ai tạo tag
  • Tạo lúc nào
  • Tag này đại diện cho điều gì
  • Có thể được ký số trong quy trình chặt chẽ hơn
bash
# Tạo annotated tag cho release
 git tag -a v1.4.0 -m "Release v1.4.0"

# Push tag lên remote
 git push origin v1.4.0

Khi nào không nên dùng shortcut sai?

Đừng tin những shortcut này:

  • git tag v1.4.0 rồi gọi đó là release chính thức
  • "Cứ tag tạm, mai sửa lại cũng được"
  • "Tag chỉ để nhìn đẹp trên GitHub"

Không nên dùng tag như sticker có thể di chuyển tùy hứng. Một khi tag release đã được dùng trong pipeline, tài liệu, hay triển khai, nó trở thành mốc trách nhiệm.


4) Release thinking: đừng hỏi "đã tag chưa?", hãy hỏi "đã đủ điều kiện release chưa?"

Một release tốt trả lời được ba câu hỏi:

  1. Ship cái gì?
  2. Vì sao ship bây giờ?
  3. Nếu có sự cố, rollback hoặc hotfix thế nào?

Checklist suy nghĩ trước khi tag

text
[ ] CI xanh hoặc kiểm thử cốt lõi đã pass
[ ] Commit được chọn là commit đã review
[ ] Không còn thay đổi local chưa commit
[ ] Release note / changelog đủ rõ để team khác hiểu
[ ] Biết rõ rollback về tag nào nếu có sự cố
[ ] Không có secrets hoặc file nhạy cảm bị stage nhầm

Ví dụ production

Team chuẩn bị phát hành v1.4.0 cho hệ thống thanh toán:

  • Có thêm retry logic cho webhook
  • Có fix race condition ở xử lý callback
  • QA đã test luồng thành công và luồng timeout
  • Ops cần biết release này map tới commit nào để rollback nếu cổng thanh toán lỗi

Khi đó, tag v1.4.0điểm neo giữa code, tài liệu vận hành, và trách nhiệm triển khai.


5) Trực giác versioning cơ bản

Bạn chưa cần thuộc mọi chi tiết SemVer, nhưng cần đúng trực giác:

Kiểu thay đổiTrực giác version
Sửa bug, không đổi contractPATCH
Thêm tính năng tương thích ngượcMINOR
Phá vỡ API / contract cũMAJOR

Gợi ý nhớ nhanh

text
1.4.2 -> 1.4.3   Sửa bug an toàn hơn
1.4.2 -> 1.5.0   Có thêm capability mới
1.4.2 -> 2.0.0   Người dùng/đội khác phải thay đổi cách tích hợp

Khi không nên tin shortcut versioning sai?

  • Đừng tăng PATCH nếu bạn vừa làm breaking change
  • Đừng gắn MAJOR/MINOR/PATCH một cách hình thức nếu team chưa hiểu impact thật sự
  • Đừng coi version là việc của riêng CI/CD; version là tín hiệu giao tiếp với người dùng và đội vận hành

6) Wrong shortcut: những thứ tuyệt đối không được tin

Sai lầm 1: "Sai tag thì xóa và tag lại cùng tên"

Đây là shortcut nguy hiểm. Vì có thể:

  • Người khác đã fetch tag cũ
  • Pipeline đã build artifact từ tag cũ
  • Tài liệu release đã tham chiếu tag đó

Cách an toàn hơn:

  • Nếu tag chưa ai dùng và vẫn còn trong vùng kiểm soát nội bộ, có thể xóa và tạo lại sau khi thông báo rõ
  • Nếu tag đã được dùng rộng rãi, ưu tiên tạo release mới (v1.4.1) thay vì giả vờ lịch sử chưa từng sai

Sai lầm 2: "Chỉ cần thêm .env vào .gitignore là xong"

Sai. .gitignore chỉ ngăn Git theo dõi file trong tương lai. Nếu secret đã vào commit rồi, .gitignore không xóa được history.

Sai lầm 3: "Commit file dump nội bộ chắc không ai để ý"

Sai tiếp. Các file sau đây có thể chứa dữ liệu cực nhạy cảm dù nhìn rất "vô hại":

  • .env
  • config.local.*
  • credentials.json
  • service-account.json
  • *.pem, *.key, *.p12
  • dump SQL / NoSQL
  • file SQLite local
  • file export CSV chứa email, token, số điện thoại
  • generated file nhúng config runtime hoặc token tạm thời

7) Secret hygiene: danh sách "do not commit" bắt buộc nhớ

TUYỆT ĐỐI KHÔNG COMMIT

  • .env, .env.*
  • API keys, access tokens, refresh tokens
  • Username/password thật của DB, Redis, SMTP, S3...
  • Private keys, certificate bundles, signing keys
  • Production dump, backup, snapshot, export CSV/XLSX chứa dữ liệu thật
  • File SQLite cục bộ hoặc generated artifacts chứa dữ liệu người dùng
  • File build/generated có nhúng secret từ môi trường runtime

Vì sao generated files cũng nguy hiểm?

Nhiều người chỉ cảnh giác với file cấu hình nguồn. Nhưng generated files cũng có thể leak:

  • bundle frontend chứa endpoint/token hard-code
  • file debug export từ job CI
  • snapshot test vô tình chụp dữ liệu thật
  • script sinh config ra JSON rồi commit luôn

Bài học: đừng tin file chỉ vì nó "được tạo tự động".

Một policy cơ bản nên có

gitignore
.env
.env.*
*.pem
*.key
*.p12
*.pfx
*.sqlite
*.db
*.sql
*.dump
backups/
exports/

Nhưng nhớ kỹ: .gitignore là hàng rào phòng ngừa, không phải xe cứu hỏa sau incident.


8) Khi nào không nên release hoặc không nên gắn tag?

Không nên tag/release khi:

  • Build chưa reproducible hoặc test còn đỏ
  • Bạn chưa chắc commit đó đã được review đúng
  • Có hotfix dang dở chưa tách bạch
  • Changelog chưa đủ để người khác hiểu impact
  • Vừa phát hiện file nhạy cảm đang nằm trong staging area

Dấu hiệu nguy hiểm

bash
git status
git diff --cached

Nếu trong staging có .env, dump dữ liệu, file key, hoặc generated file lạ, dừng lại ngay. Release lúc này không phải tốc độ; release lúc này là tai nạn có hẹn giờ.


9) Recovery nếu dùng sai tag hoặc release sai commit

Tình huống A: tạo nhầm tag ở commit sai

Nếu tag chưa được public rộng rãi

Bạn có thể sửa, nhưng phải làm có kỷ luật:

  1. Dừng pipeline/phát hành liên quan
  2. Thông báo team rằng tag đang bị sửa
  3. Xóa tag local và remote
  4. Tạo lại annotated tag đúng commit
  5. Push lại và xác nhận mọi người dùng đúng mốc mới
bash
# Xóa local tag
 git tag -d v1.4.0

# Xóa remote tag
 git push origin --delete v1.4.0

# Tạo lại đúng commit
 git tag -a v1.4.0 <correct-sha> -m "Release v1.4.0"
 git push origin v1.4.0

Nếu tag đã được dùng công khai hoặc có artifact phát hành

Đừng cố làm lịch sử "trông đẹp" bằng cách retag cùng tên. Cách chuyên nghiệp hơn là:

  • Giữ nguyên sự thật lịch sử
  • Tạo bản sửa kế tiếp, ví dụ v1.4.1
  • Ghi release note rõ ràng rằng v1.4.0 có vấn đề gì
  • Thông báo stakeholder, QA, ops, support

Production accountability: đôi khi lịch sử xấu nhưng trung thực vẫn tốt hơn lịch sử đẹp mà giả.


10) Recovery nếu commit nhầm secret

Bước 1: Coi secret như đã bị lộ

Không tranh luận kiểu: "repo private mà", "mới push 2 phút", hay "chưa ai xem đâu". Với secret đã commit, giả định an toàn nhất là đã compromise.

Bước 2: Rotate và revoke ngay

Đây là thứ phải làm trước cleanup history.

  • Rotate password / token / access key
  • Revoke hoặc disable credential cũ
  • Cập nhật secret manager, CI/CD, production config
  • Kiểm tra log truy cập nếu credential có khả năng bị lạm dụng

Bước 3: Communicate rõ ràng

Thông báo tối thiểu cho:

  • Team trực tiếp phát triển
  • Ops / DevOps / SRE nếu credential liên quan triển khai
  • Security hoặc người chịu trách nhiệm incident
  • Stakeholder phù hợp nếu có ảnh hưởng dữ liệu hoặc downtime

Thông điệp cần rõ:

  • Secret nào bị lộ
  • Hệ thống nào bị ảnh hưởng
  • Secret đã rotate/revoke chưa
  • Có cần re-clone, reset branch, hay pause deploy không

Bước 4: Quyết định cleanup hay containment

Không phải incident nào cũng cần rewrite toàn bộ history ngay lập tức. Hãy quyết định theo mức độ nghiêm trọng.

Chọn containment khi:

  • Secret đã được rotate/revoke nhanh chóng
  • Repo có nhiều clone/fork và rewrite sẽ gây gián đoạn lớn
  • Mức rủi ro còn lại thấp sau khi thu hồi credential
  • Bạn cần ưu tiên khống chế sự cố trước, cleanup sau

Containment thường bao gồm:

  • Xóa file khỏi HEAD hiện tại
  • Chặn sử dụng credential cũ
  • Tăng giám sát truy cập
  • Tạo incident note và follow-up cleanup nếu cần

Chọn history cleanup khi:

  • Secret vẫn có giá trị hoặc chưa chắc đã thu hồi hết
  • Dữ liệu nhạy cảm nằm trong file/history mà chỉ rotate credential là chưa đủ
  • Có yêu cầu compliance hoặc bảo mật buộc phải purge
  • Team chấp nhận chi phí force-push, re-clone, và đồng bộ lại lịch sử

Bước 5: Cleanup history nếu cần

Dùng công cụ hiện đại như git filter-repo hoặc giải pháp được team chuẩn hóa. Sau khi rewrite:

  • Force-push có kiểm soát
  • Yêu cầu collaborators re-fetch / reset / re-clone
  • Xác minh secret không còn ở lịch sử dễ truy cập
  • Ghi lại quyết định và phạm vi cleanup

Quan trọng: cleanup history không thay thế rotate/revoke. Nó chỉ giảm bề mặt rò rỉ còn sót lại.


11) Quy trình phản ứng nhanh khi lỡ commit secret

text
1. Stop push/deploy lan rộng nếu còn kịp
2. Xác định secret nào bị lộ
3. Rotate ngay
4. Revoke/disable bản cũ
5. Thông báo team và owner liên quan
6. Đánh giá mức độ ảnh hưởng
7. Chọn containment hoặc cleanup history
8. Xác minh hệ thống đã dùng secret mới
9. Theo dõi log, audit, và ghi postmortem ngắn

Ví dụ thực tế

Một engineer lỡ commit service-account.json vào branch, rồi merge lên main.

Phản ứng đúng:

  • Vô hiệu hóa service account cũ
  • Tạo credential mới và cập nhật vào secret manager
  • Báo DevOps + Security
  • Kiểm tra CI/CD, cron jobs, production runtime đã dùng key mới chưa
  • Quyết định có cần rewrite history hay chỉ containment
  • Nếu rewrite, thông báo toàn team cách reset local repo

Phản ứng sai:

  • Xóa file ở commit mới rồi coi như xong
  • Chỉ thêm vào .gitignore
  • Im lặng vì "repo private"

12) Bộ lệnh tối thiểu cần nhớ

bash
# Xem tag
 git tag

# Xem chi tiết tag
 git show v1.4.0

# Tạo annotated tag
 git tag -a v1.4.0 -m "Release v1.4.0"

# Push tag
 git push origin v1.4.0

# Kiểm tra staging trước khi commit/release
 git status
 git diff --cached

Nếu bạn chỉ nhớ một điều, hãy nhớ điều này:

Release phải truy vết được. Secret phải bị chặn trước khi vào history. Nếu đã vào history, xử lý như incident thật.


13) Tóm tắt quyết định đúng

Với tags/releases

  • Dùng annotated tag cho release
  • Tag sau khi đã đủ điều kiện release, không phải để "đánh dấu cho vui"
  • Không retag cùng tên một cách tùy tiện sau khi đã public
  • Version là tín hiệu trách nhiệm, không phải số trang trí

Với secrets

  • Không commit .env, credentials, dump, file DB local, generated files nhạy cảm
  • Không tin .gitignore nếu secret đã từng được commit
  • Rotate và revoke trước khi cleanup
  • Communicate sớm, rõ, và có owner chịu trách nhiệm

🧠 Quiz

Câu 1: Vấn đề quan trọng nhất mà release tag giải quyết là gì?

  • [ ] A) Làm repository trông chuyên nghiệp hơn
  • [x] B) Tạo mốc phát hành truy vết được giữa commit, release note và deployment
  • [ ] C) Thay thế hoàn toàn changelog
  • [ ] D) Cho phép bỏ qua kiểm thử trước khi ship

💡 Giải thích: Tag release tồn tại để team biết chính xác commit nào đại diện cho bản phát hành nào. Nó là công cụ truy vết và kiểm toán, không phải đồ trang trí.

Câu 2: Nếu đã commit .env chứa API key và push lên remote, bước đúng đầu tiên là gì?

  • [ ] A) Thêm .env vào .gitignore
  • [ ] B) Tạo commit mới xóa .env
  • [x] C) Rotate/revoke credential ngay và xử lý như incident bảo mật
  • [ ] D) Đổi tên file .env để khó bị phát hiện

💡 Giải thích: .gitignore và commit xóa file không làm biến mất secret khỏi history đã push. Việc đầu tiên là giảm rủi ro thật: rotate và revoke.

Câu 3: Khi nào không nên retag cùng tên một release tag?

  • [ ] A) Khi tag là annotated
  • [ ] B) Khi team dùng Semantic Versioning
  • [x] C) Khi tag đã được public, đã có artifact/pipeline/stakeholder sử dụng
  • [ ] D) Khi commit có nhiều file thay đổi

💡 Giải thích: Khi tag đã được dùng rộng rãi, retag cùng tên làm mất tính nhất quán giữa source, artifact, tài liệu và môi trường đã triển khai. Lúc đó thường nên phát hành version mới.