くりにっき

フルスタックキュアエンジニアです

naturalsorterでバージョンをいい感じにソートする

そういう需要があったので調べていました

現状

手元のgemで git tag した結果です(長くてすみません。。。)

$ git tag
v0.0.1
v0.0.2
v0.0.3
v0.0.4
v0.0.5
v0.0.6
v0.0.7
v0.0.8
v0.0.9
v0.1.0
v0.1.1
v0.1.2
v0.1.3
v0.1.4
v0.1.5
v0.1.6
v0.1.7
v0.1.8
v0.1.9
v0.2.0
v0.2.1
v0.3.0
v0.3.1
v0.3.2
v1.0.0
v1.0.1
v1.0.2
v1.0.3
v1.0.4
v1.0.5
v1.0.6
v1.0.7
v1.0.8
v1.1.0
v1.1.1
v1.1.2
v1.1.3
v1.1.4
v1.1.5
v1.1.6
v1.1.7
v2.0.0
v2.0.0.beta1
v2.0.0.beta2
v2.0.1
v2.0.2
v2.0.3
v2.0.4
v2.0.5
v2.0.5.4
v2.0.6
v2.0.7
v2.0.8
v2.1.0
v2.1.1
v2.1.2
v2.1.3
v2.1.4
v2.1.5
v2.1.5.beta
v2.1.6
v2.1.7
v2.1.8
v2.1.9
v2.2.0
v2.2.1
v2.2.2
v2.2.3
v2.2.4
v2.2.5
v2.2.6
v2.2.7
v2.2.8
v2.2.9
v2.3.0
v2.3.1
v2.3.10
v2.3.2
v2.3.3
v2.3.3.beta1
v2.3.3.beta2
v2.3.3.beta3
v2.3.4
v2.3.4.beta1
v2.3.5
v2.3.6
v2.3.7
v2.3.8
v2.3.9
v2.3.9.beta1

Rubyからgit tagの結果をparseする

# get versions from `git tag` response
# ex) ["0.3.1", "0.3.2", "0.3.3", "0.3.4", "0.3.5", "0.3.6", "0.3.7", "0.3.8", "0.3.8.beta1", "0.3.9", "1.0.0", "1.0.0.beta1"]
def tag_versions
  `git tag`.each_line.find_all{ |tag| tag.start_with?("v") }.map{ |tag| tag.gsub("v", "").strip }
end
 
pp tag_versions
=begin
["0.0.1",
 "0.0.2",
 "0.0.3",
 "0.0.4",
 "0.0.5",
 "0.0.6",
 "0.0.7",
 "0.0.8",
 "0.0.9",
 "0.1.0",
 "0.1.1",
 "0.1.2",
 "0.1.3",
 "0.1.4",
 "0.1.5",
 "0.1.6",
 "0.1.7",
 "0.1.8",
 "0.1.9",
 "0.2.0",
 "0.2.1",
 "0.3.0",
 "0.3.1",
 "0.3.2",
 "1.0.0",
 "1.0.1",
 "1.0.2",
 "1.0.3",
 "1.0.4",
 "1.0.5",
 "1.0.6",
 "1.0.7",
 "1.0.8",
 "1.1.0",
 "1.1.1",
 "1.1.2",
 "1.1.3",
 "1.1.4",
 "1.1.5",
 "1.1.6",
 "1.1.7",
 "2.0.0",
 "2.0.0.beta1",
 "2.0.0.beta2",
 "2.0.1",
 "2.0.2",
 "2.0.3",
 "2.0.4",
 "2.0.5",
 "2.0.5.4",
 "2.0.6",
 "2.0.7",
 "2.0.8",
 "2.1.0",
 "2.1.1",
 "2.1.2",
 "2.1.3",
 "2.1.4",
 "2.1.5",
 "2.1.5.beta",
 "2.1.6",
 "2.1.7",
 "2.1.8",
 "2.1.9",
 "2.2.0",
 "2.2.1",
 "2.2.2",
 "2.2.3",
 "2.2.4",
 "2.2.5",
 "2.2.6",
 "2.2.7",
 "2.2.8",
 "2.2.9",
 "2.3.0",
 "2.3.1",
 "2.3.10",
 "2.3.2",
 "2.3.3",
 "2.3.3.beta1",
 "2.3.3.beta2",
 "2.3.3.beta3",
 "2.3.4",
 "2.3.4.beta1",
 "2.3.5",
 "2.3.6",
 "2.3.7",
 "2.3.8",
 "2.3.9",
 "2.3.9.beta1"]
=end

普通にソートする

ascii_sorted_versions = tag_versions.sort
pp ascii_sorted_versions
=begin
["0.0.1",
 "0.0.2",
 "0.0.3",
 "0.0.4",
 "0.0.5",
 "0.0.6",
 "0.0.7",
 "0.0.8",
 "0.0.9",
 "0.1.0",
 "0.1.1",
 "0.1.2",
 "0.1.3",
 "0.1.4",
 "0.1.5",
 "0.1.6",
 "0.1.7",
 "0.1.8",
 "0.1.9",
 "0.2.0",
 "0.2.1",
 "0.3.0",
 "0.3.1",
 "0.3.2",
 "1.0.0",
 "1.0.1",
 "1.0.2",
 "1.0.3",
 "1.0.4",
 "1.0.5",
 "1.0.6",
 "1.0.7",
 "1.0.8",
 "1.1.0",
 "1.1.1",
 "1.1.2",
 "1.1.3",
 "1.1.4",
 "1.1.5",
 "1.1.6",
 "1.1.7",
 "2.0.0",
 "2.0.0.beta1",
 "2.0.0.beta2",
 "2.0.1",
 "2.0.2",
 "2.0.3",
 "2.0.4",
 "2.0.5",
 "2.0.5.4",
 "2.0.6",
 "2.0.7",
 "2.0.8",
 "2.1.0",
 "2.1.1",
 "2.1.2",
 "2.1.3",
 "2.1.4",
 "2.1.5",
 "2.1.5.beta",
 "2.1.6",
 "2.1.7",
 "2.1.8",
 "2.1.9",
 "2.2.0",
 "2.2.1",
 "2.2.2",
 "2.2.3",
 "2.2.4",
 "2.2.5",
 "2.2.6",
 "2.2.7",
 "2.2.8",
 "2.2.9",
 "2.3.0",
 "2.3.1",
 "2.3.10",
 "2.3.2",
 "2.3.3",
 "2.3.3.beta1",
 "2.3.3.beta2",
 "2.3.3.beta3",
 "2.3.4",
 "2.3.4.beta1",
 "2.3.5",
 "2.3.6",
 "2.3.7",
 "2.3.8",
 "2.3.9",
 "2.3.9.beta1"]
=end

問題点

  • 2.0.0.beta1 や 2.0.0.beta2 は 2.0.0よりも前になってほしい
  • 2.3.10 は 2.3.9 よりも後になってほしい

いい感じにソートするgemを探したらあった

自分で作ってもよかったんだけど汎用的だしきっと誰か作ってるだろうと思って探したら naturalsorter という素晴らしいgemを見つけました!!

github.com

naturalsorterでソートした

natural_sorted_versions = Naturalsorter::Sorter.sort_version(tag_versions)
pp natural_sorted_versions
=begin
["0.0.1",
 "0.0.2",
 "0.0.3",
 "0.0.4",
 "0.0.5",
 "0.0.6",
 "0.0.7",
 "0.0.8",
 "0.0.9",
 "0.1.0",
 "0.1.1",
 "0.1.2",
 "0.1.3",
 "0.1.4",
 "0.1.5",
 "0.1.6",
 "0.1.7",
 "0.1.8",
 "0.1.9",
 "0.2.0",
 "0.2.1",
 "0.3.0",
 "0.3.1",
 "0.3.2",
 "1.0.0",
 "1.0.1",
 "1.0.2",
 "1.0.3",
 "1.0.4",
 "1.0.5",
 "1.0.6",
 "1.0.7",
 "1.0.8",
 "1.1.0",
 "1.1.1",
 "1.1.2",
 "1.1.3",
 "1.1.4",
 "1.1.5",
 "1.1.6",
 "1.1.7",
 "2.0.0.beta1",
 "2.0.0.beta2",
 "2.0.0",
 "2.0.1",
 "2.0.2",
 "2.0.3",
 "2.0.4",
 "2.0.5",
 "2.0.5.4",
 "2.0.6",
 "2.0.7",
 "2.0.8",
 "2.1.0",
 "2.1.1",
 "2.1.2",
 "2.1.3",
 "2.1.4",
 "2.1.5.beta",
 "2.1.5",
 "2.1.6",
 "2.1.7",
 "2.1.8",
 "2.1.9",
 "2.2.0",
 "2.2.1",
 "2.2.2",
 "2.2.3",
 "2.2.4",
 "2.2.5",
 "2.2.6",
 "2.2.7",
 "2.2.8",
 "2.2.9",
 "2.3.0",
 "2.3.1",
 "2.3.2",
 "2.3.3.beta1",
 "2.3.3.beta2",
 "2.3.3.beta3",
 "2.3.3",
 "2.3.4.beta1",
 "2.3.4",
 "2.3.5",
 "2.3.6",
 "2.3.7",
 "2.3.8",
 "2.3.9.beta1",
 "2.3.9",
 "2.3.10"]
=end 

latest_version = natural_sorted_versions.last
# => 2.3.10

LGTM!!!!

LGTM

今回のコードはこちらです

https://gist.github.com/sue445/5fafb7b6bf8cdac97ff0

近況

【追記】2015/7/3 17:40

gemのバージョン比較には Gem::Version を使った方がいいと社内からツッコミあったので追記

a = Gem::Version.create("2.3.3.beta1")
b = Gem::Version.create("2.3.3")
a < b
# => true
latest_version = tag_versions.max_by { |tag| Gem::Version.create(tag) }