Kiểm tra range semver: phiên bản nào khớp, và range đó thật sự cho phép những gì
Nhập một range như dấu mũ, dấu ngã, khoảng lớn hơn nhỏ hơn hoặc chuỗi có dấu hoặc, cùng một danh sách phiên bản, công cụ trả lời bản nào lọt và vì sao bản còn lại bị chặn. Kèm phần so sánh hai phiên bản, bóc tách major, minor, patch, prerelease, build metadata và giải thích thứ tự ưu tiên khi so sánh prerelease.
Tính năng nổi bật
- Đọc đủ các dạng range thông dụng: dấu mũ, dấu ngã, lớn hơn, nhỏ hơn, khoảng có gạch nối, ký hiệu x, dấu sao và chuỗi nối bằng hai gạch đứng
- Quy range về dạng comparator chuẩn rồi diễn giải bằng tiếng Việt cận dưới và cận trên thật sự của nó
- Cảnh báo riêng cho dấu mũ đặt trên phiên bản 0.x, chỗ nhiều người hiểu sai nhất
- Bảng kết quả từng phiên bản kèm lý do bị chặn, chỉ rõ comparator nào loại nó
- So sánh hai phiên bản và nói rõ trường nào quyết định kết quả
- Bóc tách chuỗi phiên bản thành major, minor, patch, prerelease và build metadata, đánh dấu từng định danh prerelease là số hay chữ
- Kiểm tra tính hợp lệ theo đặc tả semver 2.0.0 và chỉ ra lỗi cụ thể như số 0 ở đầu hoặc thiếu nhóm patch
- Công tắc tính bản prerelease như bản thường để đối chiếu với hành vi mặc định của trình quản lý gói
Vì sao cần đọc lại range thay vì tin vào cảm giác
Range là một dòng ngắn nằm trong tệp khai báo phụ thuộc, và hầu hết mọi người chỉ đọc lướt qua nó. Vấn đề là cùng một ký hiệu lại có nghĩa khác nhau tùy con số đứng sau. Dấu mũ trên 1.2.3 nới tới sát 2.0.0, còn dấu mũ trên 0.2.3 chỉ nới tới sát 0.3.0, hẹp hơn rất nhiều. Người đọc lướt sẽ nghĩ hai dòng ấy nới lỏng như nhau, rồi bối rối khi thư viện ra bản 0.3.0 mà máy mình không cập nhật, hoặc ngược lại khi một bản mới lọt vào và làm hỏng bản dựng. Việc nhìn thấy range đã được quy về dạng lớn hơn bằng và nhỏ hơn, kèm câu diễn giải, làm biến mất phần đoán mò đó. Công cụ còn hữu ích ở tình huống ngược lại: bạn sắp phát hành và muốn biết con số mình chọn có tới tay người dùng hay không. Dán danh sách phiên bản kèm range mà họ khai, bạn thấy ngay bản mới có được nhận tự động hay phải chờ họ sửa tay tệp khai báo.
Lợi ích khi sử dụng
- Biết chính xác khoảng phiên bản mà một dòng khai báo phụ thuộc cho phép, không phải đoán
- Tránh được cái bẫy dấu mũ trên dòng 0.x, nguyên nhân của rất nhiều lần cập nhật hụt
- Hiểu vì sao bản thử nghiệm không tự được cài dù nhìn qua thì nằm trong khoảng
- Kiểm tra nhanh một chuỗi phiên bản có hợp lệ trước khi đặt tag hoặc phát hành
- Chạy hoàn toàn trong trình duyệt, không gọi registry nên dùng được với gói nội bộ
Cách kiểm tra một range semver
- 1Ở tab khớp range, dán chuỗi range vào ô đầu tiên, hoặc bấm một trong các nút mẫu để nạp nhanh dạng bạn muốn thử.
- 2Dán danh sách phiên bản vào ô bên dưới, mỗi dòng một chuỗi. Lấy danh sách thật của một gói bằng lệnh npm view kèm tên gói và chữ versions.
- 3Đọc khối diễn giải để biết cận dưới và cận trên thật sự của range, đây là phần trả lời câu hỏi range này cho phép những gì.
- 4Xem bảng kết quả để biết bản nào khớp, bản nào bị chặn và comparator nào chặn nó.
- 5Chuyển sang tab so sánh khi cần biết hai chuỗi bản nào lớn hơn, hoặc tab bóc tách khi cần kiểm tra một chuỗi có hợp lệ không và đọc từng thành phần của nó.
Ba con số là một lời hứa, không phải một con số đếm
Điều làm semver khác với cách đánh số tùy hứng là mỗi vị trí mang một cam kết cụ thể với người dùng. Tăng patch nghĩa là bạn sửa lỗi mà không đổi bất cứ hành vi công khai nào, nên người dùng nâng lên mà không cần đọc gì. Tăng minor nghĩa là bạn thêm thứ mới nhưng mọi mã đang chạy vẫn chạy nguyên như cũ. Tăng major nghĩa là có thứ gì đó không còn tương thích, và người dùng phải đọc hướng dẫn nâng cấp trước khi cài. Cái khó nằm ở chỗ định nghĩa thế nào là phá vỡ tương thích, vì nó rộng hơn nhiều so với việc xóa một hàm. Đổi tên tham số, đổi giá trị mặc định, đổi định dạng chuỗi trả về, siết một điều kiện đầu vào từng được chấp nhận, hay thậm chí sửa một lỗi mà người dùng đã lỡ dựa vào, tất cả đều có thể làm hỏng mã của người khác. Ngược lại, đổi cấu trúc thư mục bên trong hay viết lại một hàm riêng tư thì không, miễn là bề mặt công khai giữ nguyên. Ranh giới ấy chính là phần bạn phải tự vạch ra và ghi rõ trong tài liệu, vì công cụ nào cũng chỉ đọc được con số chứ không đọc được ý định.
Dấu mũ và dấu ngã: khác biệt thật sự, và cái bẫy của 0.x
Cách nói quen thuộc rằng dấu mũ giữ nguyên major còn dấu ngã giữ nguyên minor chỉ đúng một nửa. Quy tắc thật của dấu mũ là khóa số khác không đầu tiên tính từ trái sang. Với 1.2.3, số khác không đầu tiên là major nên dấu mũ cho phép tới sát 2.0.0, tức nhận cả minor mới lẫn patch mới. Với 0.2.3, số khác không đầu tiên lại là minor, nên dấu mũ chỉ cho phép tới sát 0.3.0, tức chỉ nhận patch. Với 0.0.3 thì số khác không đầu tiên là patch, và dấu mũ khóa cứng đúng bản đó, ngay cả 0.0.4 cũng bị chặn. Lý do đứng sau là đặc tả semver nói rõ dòng 0.x dành cho giai đoạn phát triển ban đầu và mọi thứ có thể thay đổi bất cứ lúc nào, nên trình quản lý gói coi số minor của dòng 0.x như số major của dòng 1.x. Dấu ngã thì đơn giản hơn và không có ngoại lệ này: khi bạn ghi đủ ba số, nó luôn chỉ cho phép patch mới. Nhưng khi bạn ghi thiếu, dấu ngã lại nới rộng ra, chẳng hạn dấu ngã trên số 1 tương đương khoảng từ 1.0.0 tới sát 2.0.0.
Prerelease: vì sao 1.0.0-rc.1 nhỏ hơn 1.0.0 và vì sao nó không tự được cài
Phần đứng sau dấu gạch nối là prerelease, và nó luôn kéo phiên bản xuống dưới bản chính thức cùng bộ ba số. Đó là lý do 1.0.0-rc.1 nhỏ hơn 1.0.0 dù chuỗi dài hơn. Khi cả hai bên đều có prerelease, các trường ngăn bằng dấu chấm được so lần lượt từ trái sang phải theo ba quy tắc. Trường toàn chữ số được so theo giá trị số, nên beta.11 lớn hơn beta.2 chứ không nhỏ hơn như khi so chuỗi. Trường có chứa chữ được so theo bảng mã ký tự, nên beta lớn hơn alpha. Khi một bên là số còn bên kia là chữ thì bên chữ luôn lớn hơn. Nếu mọi trường đứng trước đều bằng nhau thì bên nào có nhiều trường hơn sẽ lớn hơn, nên alpha.1 lớn hơn alpha. Ngoài thứ tự, còn một quy tắc riêng về việc cài đặt mà nhiều người bất ngờ: bản có prerelease không lọt vào một range trừ khi chính range đó nhắc tới đúng bộ ba số của nó kèm một prerelease. Nhờ vậy dấu mũ trên 1.2.3 sẽ không kéo về bản 1.9.0-rc.1 dù xét về thứ tự thì bản ấy nằm trong khoảng. Công cụ mặc định theo đúng hành vi này và có công tắc để bạn tắt đi khi cần đối chiếu.
Build metadata: phần bị bỏ qua khi so sánh, và hệ quả thực tế
Phần đứng sau dấu cộng gọi là build metadata, thường dùng để ghi mã băm của commit, số hiệu lần dựng hoặc ngày dựng. Đặc tả quy định rất rõ rằng phần này bị bỏ qua hoàn toàn khi xác định thứ tự ưu tiên. Nghĩa là 1.0.0 và 1.0.0+build1 là hai chuỗi khác nhau nhưng đứng cùng một chỗ trên thang thứ tự, và 1.0.0+build1 cũng bằng 1.0.0+build2. Hệ quả thực tế quan trọng hơn vẻ ngoài của nó. Nếu bạn định dùng build metadata làm số hiệu lần dựng để phân biệt hai gói phát hành khác nhau, hệ thống nào cũng có thể coi chúng là một, và nhiều registry thẳng thừng từ chối cho bạn đăng hai gói chỉ khác phần sau dấu cộng. Muốn phân biệt thật sự thì con số phải nằm ở phần prerelease, tức sau dấu gạch nối, vì phần đó có tham gia so sánh. Cách dùng đúng của build metadata là ghi thông tin truy vết đi kèm một bản đã cố định, chẳng hạn gắn mã commit vào bản đóng gói để sau này biết nó được dựng từ đâu, chứ không phải để tạo ra một phiên bản mới.
Đọc range trong tệp khai báo phụ thuộc: khóa cứng không đồng nghĩa với an toàn
Khi thấy một bản cập nhật làm hỏng dự án, phản xạ thường gặp là bỏ dấu mũ và ghi đúng một con số. Cách đó chặn được bản mới nhưng kèm ba cái giá. Thứ nhất, bản vá bảo mật cũng bị chặn, và những gói khóa cứng thường là gói bị quên lâu nhất. Thứ hai, dự án của bạn khóa cứng không ngăn được thư viện khác trong cây phụ thuộc kéo về một bản khác của cùng gói đó, nên bạn có thể kết thúc với hai bản cùng tồn tại. Thứ ba, khóa cứng ở tệp khai báo là nhầm chỗ, vì việc cố định chính xác cây phụ thuộc đã có tệp khóa lo. Cách làm hợp lý hơn là giữ range hợp lý trong tệp khai báo, đưa tệp khóa vào kho mã để mọi người và máy dựng đều cài y hệt nhau, rồi nâng cấp chủ động theo lịch bằng lệnh cập nhật thay vì để nó xảy ra ngẫu nhiên. Nếu vẫn cần chặn một bản cụ thể vì đang có lỗi, hãy viết khoảng có cận trên rõ ràng và kèm một dòng ghi chú giải thích lý do, để vài tháng sau người khác biết khi nào được gỡ giới hạn đó.
Câu hỏi thường gặp (FAQ)
Vì sao ^0.2.3 không nhận 0.3.0 trong khi ^1.2.3 lại nhận 1.9.0?
Vì dấu mũ khóa số khác không đầu tiên tính từ trái sang, chứ không phải luôn khóa major. Ở 1.2.3 số đó là major nên khoảng nới tới sát 2.0.0. Ở 0.2.3 số đó là minor nên khoảng chỉ tới sát 0.3.0. Đặc tả coi dòng 0.x là giai đoạn API chưa ổn định nên minor ở đó được đối xử như major.
^1.2.3 và ~1.2.3 khác nhau ở đâu?
Dấu mũ cho phép cả minor mới lẫn patch mới, tức từ 1.2.3 tới sát 2.0.0, nên 1.9.0 lọt. Dấu ngã chỉ cho phép patch mới, tức từ 1.2.3 tới sát 1.3.0, nên 1.2.9 lọt còn 1.3.0 bị chặn. Dấu ngã chặt hơn và hợp với dự án muốn kiểm soát từng bước nâng cấp.
Vì sao bản 2.0.0-rc.1 không được cài dù range của tôi là >=1.0.0?
Vì quy tắc riêng dành cho prerelease: bản có đuôi thử nghiệm chỉ lọt khi chính range nhắc tới đúng bộ ba số của nó kèm một prerelease, ví dụ >=2.0.0-rc.0. Quy tắc này tránh việc bản chưa ổn định lọt vào dự án của bạn một cách bất ngờ. Bật công tắc tính cả prerelease trong công cụ để xem kết quả khi bỏ quy tắc đó.
1.0.0-alpha.10 hay 1.0.0-alpha.9 lớn hơn?
1.0.0-alpha.10 lớn hơn, vì trường prerelease toàn chữ số được so theo giá trị số chứ không so theo chuỗi. Nếu so như chuỗi thì 10 sẽ bị coi là nhỏ hơn 9, và đó là lỗi thường gặp khi tự viết hàm so sánh phiên bản.
1.0.0+build1 có lớn hơn 1.0.0 không?
Không, hai chuỗi đó bằng nhau về thứ tự. Build metadata, tức phần sau dấu cộng, bị bỏ qua hoàn toàn khi so sánh. Nếu bạn cần một phiên bản thật sự lớn hơn thì con số phải nằm ở phần prerelease sau dấu gạch nối, hoặc phải tăng một trong ba số chính.
Chuỗi 1.4 có phải phiên bản semver hợp lệ không?
Không, vì semver bắt buộc đủ ba nhóm số ngăn bằng dấu chấm. Tuy nhiên 1.4 vẫn dùng được ở vị trí range, khi đó nó được hiểu là khoảng từ 1.4.0 tới sát 1.5.0. Hai vai trò này khác nhau: một bên là phiên bản cụ thể, một bên là mô tả một tập phiên bản.
Dấu sao và ký hiệu x có khác nhau không?
Trong thực tế chúng tương đương nhau ở cùng vị trí. Một dấu sao đứng một mình hay chữ x đứng một mình đều nghĩa là mọi bản chính thức. Đặt ở vị trí sau, ví dụ 1.x hay 1.2.x, chúng thả tự do phần còn lại, tức tương đương khoảng từ 1.0.0 tới sát 2.0.0 và từ 1.2.0 tới sát 1.3.0.
Khóa cứng một con số thay vì dùng dấu mũ có an toàn hơn không?
Không hẳn. Khóa cứng chặn luôn cả bản vá bảo mật, và nó không ngăn được thư viện khác trong cây phụ thuộc kéo về bản khác của cùng gói. Việc cố định chính xác cây phụ thuộc là nhiệm vụ của tệp khóa. Hãy giữ range hợp lý, đưa tệp khóa vào kho mã, rồi nâng cấp chủ động theo lịch.
Range 1.2.3 || >=2.0.0 nghĩa là gì?
Hai gạch đứng nối hai nhánh theo phép hoặc, một phiên bản chỉ cần lọt qua một nhánh là được nhận. Ở đây nhánh thứ nhất khóa đúng 1.2.3, nhánh thứ hai nhận mọi bản từ 2.0.0 trở lên. Nghĩa là 1.2.4 và 1.9.0 đều bị loại, còn 1.2.3 và 3.1.0 đều được nhận.
Khoảng viết bằng dấu gạch nối có bao gồm hai đầu không?
Cận dưới luôn bao gồm. Cận trên bao gồm khi bạn ghi đủ ba số, ví dụ khoảng 1.2.3 tới 2.3.4 nhận cả 2.3.4. Nhưng nếu cận trên ghi thiếu, ví dụ chỉ tới 2.3, thì nó được hiểu là tới sát 2.4.0, tức nhận mọi bản 2.3.x. Đây là chỗ dễ hiểu nhầm nên bảng diễn giải của công cụ luôn ghi rõ bao gồm hay không.
Công cụ có kiểm tra được phiên bản thật của một gói không?
Không, nó không truy cập registry và không đọc tệp khai báo của dự án bạn. Bạn tự lấy danh sách phiên bản, chẳng hạn bằng lệnh npm view kèm tên gói và chữ versions, rồi dán vào ô danh sách. Nhờ vậy công cụ dùng được cả với gói nội bộ không công khai.
Tôi sắp phát hành, làm sao biết nên tăng lên số mấy?
Câu hỏi đó thuộc về danh sách commit của đợt phát hành chứ không thuộc về range. Hãy chạy công cụ Changelog Generator với đầu ra của lệnh git log, nó đề xuất mức tăng dựa trên các loại commit có mặt. Sau đó quay lại trang này để kiểm tra con số ấy có được các range hiện hành chấp nhận hay không.
Từ khóa liên quan
- semver là gì
- kiểm tra range semver
- dấu mũ trong package.json nghĩa là gì
- khác nhau giữa dấu mũ và dấu ngã npm
- caret 0.x không nhận bản mới
- so sánh hai phiên bản semver
- thứ tự ưu tiên prerelease
- 1.0.0-alpha nhỏ hơn 1.0.0
- build metadata bị bỏ qua khi so sánh
- phiên bản hợp lệ theo semver 2.0.0
- range 1.x nghĩa là gì
- khoảng phiên bản >=1.0.0 <2.0.0
- toán tử hoặc trong version range
- vì sao npm không cài bản rc
- khóa cứng phiên bản dependency
- package lock và range phiên bản
- major minor patch khi nào tăng
- công cụ kiểm tra semver online
- npm view versions danh sách phiên bản
- semantic versioning tiếng việt
