Chuyện đặt tên phiên bản: Nỗi ám ảnh mỗi mùa Release
Hồi mới vào nghề, mình từng tham gia một dự án mà mỗi lần release là một lần cả team nháo nhào: “Bản build này là version bao nhiêu rồi anh em?”. Lúc đó, quy trình quản lý phiên bản cực kỳ “chạy bằng cơm”: mở file cấu hình, sửa tay từ 1.0.1 thành 1.0.2, rồi mới bắt đầu build.
Sai sót là chuyện khó tránh khỏi. Một lần nọ, đồng nghiệp quên cập nhật file nhưng vẫn gắn tag Git là v1.1.0. Kết quả là file chạy báo một đằng, nhãn Git báo một nẻo. Khi khách hàng báo lỗi trên bản v1.1.0, mình hì hục kiểm tra code của tag đó nhưng thực chất bản build đang chạy lại chứa code từ… hai tuần trước. Bài học rút ra: Việc tách rời Git Tag và thông tin phiên bản trong code là một sai lầm cực kỳ tai hại.
Tại sao việc gán nhãn thủ công lại dễ “toang”?
Vấn đề lớn nhất của việc nhập tay version string là sự lệch pha giữa mã nguồn và thực tế. Trong quy trình hiện đại, mã nguồn luôn biến động qua các trạng thái:
- Commit đã được gắn Tag (Bản Release chính thức).
- Commit nằm giữa hai Tag (Đang phát triển, chưa ổn định).
- Mã nguồn đang sửa dở tại local (Chưa commit).
Dùng con số tĩnh như 1.2.0 trong file package.json rất rủi ro. Bạn sẽ không thể biết chính xác bản build đó đang đứng ở đâu trong lịch sử Git nếu không đối chiếu thủ công. Đặc biệt trong môi trường DevOps, khi việc build diễn ra tự động trên Jenkins hay GitHub Actions, chúng ta cần một cơ chế để máy tính tự “đọc” lịch sử Git và đặt tên phiên bản một cách logic.
Từ thủ công đến tự động hóa: Đi tìm giải pháp thực tế
Nhiều team chọn dùng Commit Hash (ví dụ: 7a3b1c2) làm version. Cách này cực kỳ chính xác vì mỗi commit là duy nhất. Tuy nhiên, mã băm lại không thân thiện với con người. Nhìn vào một chuỗi ký tự ngẫu nhiên, bạn chẳng thể biết nó là bản mới hay cũ, hay cách bản release gần nhất bao xa.
Đây là lúc git describe trở thành “vũ khí bí mật”. Thay vì dùng con số vô hồn, lệnh này kết hợp Tag gần nhất, số lượng commit phát sinh và mã băm của commit hiện tại. Kết quả là một chuỗi ký tự vừa dễ đọc, vừa chính xác tuyệt đối.
Tuyệt chiêu git describe: Biến lịch sử commit thành Version String
Thử gõ lệnh này trong repository của bạn xem sao:
bash
git describe
Nếu bạn đang ở đúng commit đã gắn tag (ví dụ v1.0.0), Git sẽ trả về đúng v1.0.0. Mọi thứ bắt đầu thú vị khi bạn tạo thêm các commit mới sau tag đó.
Giải mã cấu trúc của git describe
Giả sử bạn nhận được kết quả: v1.0.0-5-g7a3b1c2. Cùng mổ xẻ ý nghĩa của nó:
- v1.0.0: Tên của Tag gần nhất mà Git tìm thấy khi đi ngược dòng lịch sử.
- 5: Số lượng commit mới được tạo ra kể từ sau tag
v1.0.0. - g7a3b1c2: Mã băm rút gọn của commit hiện tại (chữ
glà viết tắt của Git).
Nhìn vào đây, mình biết ngay: “Bản build này dựa trên v1.0.0, đã có thêm 5 thay đổi và mã commit là 7a3b1c2”. Rất minh bạch!
Các tùy chọn (options) thực dụng nhất
Để đạt hiệu quả tối đa, mình thường kết hợp các tham số sau:
bash
git describe --tags --always --dirty
Giải thích nhanh cho anh em:
--tags: Mặc địnhgit describechỉ tìm các “annotated tags”. Option này giúp quét cả các “lightweight tags” (tag thường).--always: Nếu repository chưa có tag nào, lệnh sẽ trả về commit hash thay vì báo lỗi. Điều này giúp script build không bị dừng đột ngột.--dirty: Option yêu thích của mình. Nếu code tại máy chưa commit, nó sẽ thêm hậu tố-dirty. Đây là cơ chế bảo vệ, ngăn bạn mang code “rác” lên staging hoặc production.
Áp dụng vào quy trình CI/CD thực tế
Trong một dự án Python trước đây, mình đã tích hợp lệnh này vào Makefile để tự động hóa hoàn toàn. Thay vì sửa version bằng tay, script sẽ tự lấy thông tin từ Git và ném vào bản build. Cách này giúp team tiết kiệm khoảng 15-20 phút kiểm tra mỗi lần release.
Ví dụ, tạo file _version.py tự động cực đơn giản:
bash
# Lấy version string từ git
VERSION=$(git describe --tags --always --dirty)
# Ghi vào file version của project
echo "__version__ = '$VERSION'" > src/myapp/_version.py
Khi build Docker image, mình cũng dùng chính chuỗi này để gắn tag:
bash
IMAGE_TAG=$(git describe --tags --always)
docker build -t myapp:$IMAGE_TAG .
Kể từ khi áp dụng, tình trạng lệch phiên bản biến mất hoàn toàn. Khi có bug, mình chỉ cần check version ứng dụng, sau đó git checkout đúng mã hash đó là có thể tái hiện lỗi ngay lập tức.
Lưu ý nhỏ từ kinh nghiệm thực chiến
Dù rất mạnh mẽ, nhưng để git describe hoạt động trơn tru, bạn nên nhớ:
- Chăm chỉ gắn Tag: Hãy tuân thủ chuẩn Semantic Versioning (v1.0.0, v1.1.0…). Nếu không có tag, lệnh này sẽ chỉ trả về mã hash, mất đi ý nghĩa chính của nó.
- Cẩn thận với Merge: Khi merge nhánh cũ vào nhánh chính, Git có thể chọn nhầm tag của nhánh cũ nếu nó có commit gần hơn. Hãy kiểm tra lại đồ thị commit nếu thấy version hiển thị lạ.
- Fetch đầy đủ lịch sử: Các hệ thống như GitHub Actions thường chỉ fetch commit cuối cùng (shallow clone). Bạn phải cấu hình
fetch-depth: 0để lấy đầy đủ tag, nếu không lệnh sẽ báo lỗi hoặc hoạt động sai.
Áp dụng git describe chỉ tốn 15 phút thiết lập nhưng giá trị mang lại là cực lớn. Nếu team bạn vẫn đang sửa version thủ công, hãy thử thay đổi ngay hôm nay. Anh em DevOps chắc chắn sẽ thầm cảm ơn bạn!

