理系学生日記

おまえはいつまで学生気分なのか

シェルスクリプトで、パッチバージョンが最大となるマイナーバージョンのリストを取得する

パッチバージョンが最大となるマイナーバージョンのリストを取得すると言うのは何をいっているのかというと、例えば、以下のようなsemverのリストがあるとします。 マイナーバージョンとしては1.94、1.95、1.96があり、それぞれのマイナーバージョンに対してパッチバージョンが複数存在しています。この時に、1.94.2、1.95.3、1.96.2のようなパッチバージョンが最大となるマイナーバージョンのリストを取得したいんですよね。

1.96.2
1.96.1
1.96.0
1.95.3
1.95.2
1.95.1
1.95.0
1.94.2
1.94.1
1.94.0

ユースケース

なぜそんなことをやりたいのか。

実は、上記のリストはVS Codeのバージョンを示しています。VS Code Extensionを開発していると、ある特定のバージョン範囲までは、Extensionの動作を保証したいんですよね。もちろん、全パッチバージョンでテストをすればいいんですが、テスト対象が増えると、テストの実行時間が増えるので、なるべくテスト対象を絞りたい。そうすると、マイナーバージョン単位でテストをすれば良いのでは?と言うことです。

解法

実はそんなに難しくなくて、次のようにすれば良い。

# VSCodeのバージョン情報を取得する
versions=$(curl -s https://api.github.com/repos/microsoft/vscode/releases | jq -r .[].tag_name)
# 最新3つのminorバージョンを持つ semvar を取得
latest_versions=$(echo "$versions" | sort -rV | awk -F. '!seen[$1"."$2]++' | head -n 3)

curlの行は単にGitHub APIを叩いてVS Codeのリリース情報を取得し、その中にあるバージョン情報を取得しているだけです。先頭5行を表示すると次のようになります。

curl -s https://api.github.com/repos/microsoft/vscode/releases | jq -r .[].tag_name | head -5
1.96.2
1.96.1
1.96.0
1.95.3
1.95.2

コアとなるのはlatest_versionsの行ですね。

まず、sort -rVでバージョンを逆順にソートします。-rはよく知られた逆順ソートで、ここではバージョンを降順(新しい準)に並べています。-Vはバージョンをソートするためのオプション。結果として、このsortではバージョンを降順で並べています。

       -V, --version-sort
              natural sort of (version) numbers within text

次にawk -F. '!seen[$1"."$2]++'についてです。

まず目的を達成するための考え方ですが、前提として、awkは条件にマッチした行のみを標準出力に出力するというフィルタの役割を担うことができます。先のsortによってバージョンは降順に並んでいるので、メジャー・マイナーバージョンの組み合わせに対して最初は出力し、二度目以降の同組み合わせは出力しないようにすれば、パッチバージョンが最大となるマイナーバージョンのリストを取得できます。

!seen[$1"."$2]++は、$1$2をキーとして、そのキーが初めて出現した場合に真となる条件です。-F.でフィールドセパレータを.に設定しており、$1はメジャーバージョン、$2はマイナーバージョン、$3はパッチバージョンが設定されるので、ここでメジャーバージョン・マイナーバージョンの組み合わせをキーとし、そのキーが初めて出現した場合に真となる条件を設定しています。

と言うわけで、全部繋げると次のようになります。

curl -s https://api.github.com/repos/microsoft/vscode/releases | jq -r .[].tag_name | sort -rV | awk -F. '!seen[$1"."$2]++' | head -n 3
1.96.2
1.95.3
1.94.2