単なる jq
のメモ。
やりたいこと
github から release の一覧を取得して、 RC 以外のバージョンの最新バージョンの値を取得する
GitHub API
例えば Gradle のリリースは、 GitHub の ドキュメント によると次の URL で 40
件ほど取得できる
https://api.github.com/repos/gradle/gradle/releases?per_page=40
すると、このコマンドができる
curl \ https://api.github.com/repos/gradle/gradle/releases?per_page=40 \ -H 'accept:application/vnd.github.v3+json' \ -H "authorization:token ${GITHUB_TOKEN}"
なお、レスポンスの内容は、次のような json(一部省略) の配列であり、ほしい値は name
である
{ "url": "https://api.github.com/repos/gradle/gradle/releases/22797617", "html_url": "https://github.com/gradle/gradle/releases/tag/v6.1.0-RC3", "tag_name": "v6.1.0-RC3", "target_commitish": "release", "name": "6.1 RC3", }
お題1 jq
で json の配列から json の一部の項目だけを抜き出した配列を作る
配列を イテレータに変換 して、 プロパティを指定して 取り出す(Object Identifier-Index というらしい)
.[] | .name
したがって、現在のコマンドはこんな感じ
curl \ https://api.github.com/repos/gradle/gradle/releases?per_page=40 \ -H 'accept:application/vnd.github.v3+json' \ -H "authorization:token ${GITHUB_TOKEN}" \ jq ' .[] | .name '
出力はこうなる
6.6 6.6 RC6 6.6 RC5 6.6 RC4 6.6 RC3 6.6 RC2 6.6 RC1 6.5.1 6.5 6.5 RC1 6.4.1 6.4 6.4 RC4 6.4 RC3 6.4 RC2 6.4 RC1 6.3 6.3 RC4
このとき、 RC のバージョンはいらないので、これを除外したい
お題2 jq
で特定の文字列を含む項目を取り除く
特定の文字列を含むかテストして、含んでいたら true
、含んでいない場合は false
を返す contains
という関数がある。これを boolean
を受け取って、出力有無を決定する select
関数に渡して絞り込む。
select(contains("RC") == false)
現在のコマンドは次の通り
curl \ https://api.github.com/repos/gradle/gradle/releases?per_page=40 \ -H 'accept:application/vnd.github.v3+json' \ -H "authorization:token ${GITHUB_TOKEN}" \ jq ' .[] | .name | select(contains("RC")) '
出力は次のようになる
6.6 6.5.1 6.5 6.4.1 6.4 6.3
このとき、 6.4.1
はほしいけど、 6.4
は必要がなく、また同じフィルターで 6.3
や 6.6
も取れるようにしたい
お題3 マイナーバージョン番号を取得する
[:3]
の形でマイナーバージョンの値を取得できるといえば取得できるが、当然マイナーバージョンが 10
以上にもなりうるので(例 : 1.x
2.x
4.x
は実際にあった)できれば動的に取得したい。 indices
を使うと、指定した部分文字列のインデックス値が取得できる。これと、 Object construction を用いて現在の値も保持する
{.value: ., indices: indices(".")}
ここで値は次のようになる。
{ "value": "6.6", "indices": [1] } { "value": "6.5.1", "indices": [1, 3] } { "value": "4.10.3", "indices": [1, 4] } { "value": "4.10", "indices": [1] }
次に indices を indices の二番目の要素または文字列の長さにマッピングしたい。そこで、 Alternative Operator を使って indices の二番目(インデックスは 1
)の要素または 文字列の長さ を取得する。
{value: .value, index: (.indices[1] // (.value | utf8bytelength))}
ここで値は次のようになる。
{ "value": "6.6", "index": 3 } { "value": "6.5.1", "index": 3 } { "value": "4.10.3", "index": 4 } { "value": "4.10", "index": 4 }
最後に index の値をマイナーバージョンにマッピングする
{value: .value, minor: .value[:.index]}
以上を組み合わせると次のようなコマンドと出力になる。
コマンド
curl \ https://api.github.com/repos/gradle/gradle/releases?per_page=40 \ -H 'accept:application/vnd.github.v3+json' \ -H "authorization:token ${GITHUB_TOKEN}" \ jq ' .[] | .name | select(contains("RC")) | {.value: ., indices: indices(".")} | {value: .value, index: (.indices[1] // (.value | utf8bytelength))} | {value: .value, minor: .value[:.index]} '
出力
{ "value": "6.6", "minor": "6.6"} { "value": "6.5.1", "minor": "6.5" } { "value": "6.5", "minor": "6.5" } { "value": "6.4.1", "minor": "6.4" } { "value": "6.4", "minor": "6.4" } { "value": "6.3", "minor": "6.3" }
お題4 マイナーバージョンごとにグループ化する
グルーピングは group_by
を使って、 .minor
ごとにグループを組めばよいのだが、この関数は配列を入力に取るものの、現在の状態はイテレーターによるオブジェクトが入力になってしまうため型があわない。そこで、ここまでの内容を配列に変換するため、ここまでのクエリに Array construction を適用する。
コマンド
curl \ https://api.github.com/repos/gradle/gradle/releases?per_page=40 \ -H 'accept:application/vnd.github.v3+json' \ -H "authorization:token ${GITHUB_TOKEN}" \ jq ' [ .[] | .name | select(contains("RC")) | {.value: ., indices: indices(".")} | {value: .value, index: (.indices[1] // (.value | utf8bytelength))} | {value: .value, minor: .value[:.index]} ] '
出力
[ { "value": "6.6", "minor": "6.6"}, { "value": "6.5.1", "minor": "6.5" }, { "value": "6.5", "minor": "6.5" }, { "value": "6.4.1", "minor": "6.4" }, { "value": "6.4", "minor": "6.4" }, { "value": "6.3", "minor": "6.3" } ]
そして、改めて group_by
を適用する。
コマンド
curl \ https://api.github.com/repos/gradle/gradle/releases?per_page=40 \ -H 'accept:application/vnd.github.v3+json' \ -H "authorization:token ${GITHUB_TOKEN}" \ jq ' [ .[] | .name | select(contains("RC")) | {.value: ., indices: indices(".")} | {value: .value, index: (.indices[1] // (.value | utf8bytelength))} | {value: .value, minor: .value[:.index]} ] | group_by(.minor) '
出力
[ [ { "value": "6.3", "minor": "6.3" } ], [ { "value": "6.4.1", "minor": "6.4" }, { "value": "6.4", "minor": "6.4" } ], [ { "value": "6.5.1", "minor": "6.5" }, { "value": "6.5", "minor": "6.5" } ], [ { "value": "6.6", "minor": "6.6"} ] ]
group_by
を使うと API で取得できる配列の順番が逆になるので、 reverse
を最後に適用しておく
[ .[] | .name | select(contains("RC")) | {.value: ., indices: indices(".")} | {value: .value, index: (.indices[1] // (.value | utf8bytelength))} | {value: .value, minor: .value[:.index]} ] | group_by(.minor) | reverse
お題5 個々の配列からマイナーバージョンの値を取得する
配列の個々の値を変換していくので、まずイテレーターに変える。 jq
では 6.4
より 6.4.1
の方が大きい値なので、 max_by
を用いて最終的にほしい値の入ったオブジェクトを取り出す。最後にほしい値のプロパティを指定して取り出す。
.[] | max_by(.value) | .value
または次でも同じものが取れる
.[] | [.[] | .value] | max
コマンド
curl \ https://api.github.com/repos/gradle/gradle/releases?per_page=40 \ -H 'accept:application/vnd.github.v3+json' \ -H "authorization:token ${GITHUB_TOKEN}" \ jq ' [ .[] | .name | select(contains("RC")) | {.value: ., indices: indices(".")} | {value: .value, index: (.indices[1] // (.value | utf8bytelength))} | {value: .value, minor: .value[:.index]} ] | group_by(.minor) | .[] | max_by(.value) | .value '
出力
"6.6" "6.5.1" "6.4.1" "6.3" "6.2.2" "6.1.1" "6.0.1" "5.6.4"
ほしい値が取れたが、ダブルクォーテーションがついていると次のプロセスで使いづらい
お題6 出力された文字列のイテレーターからダブルクォーテーションを取り除く
オプション -r
/ --raw-output
を使うと出力が文字列の際に json に則って出力するのではなく、そのままの値を出力する
よって、最終的に次のコマンドと出力を得る
コマンド
curl \ https://api.github.com/repos/gradle/gradle/releases?per_page=40 \ -H 'accept:application/vnd.github.v3+json' \ -H "authorization:token ${GITHUB_TOKEN}" \ jq -r ' [ .[] | .name | select(contains("RC")) | {.value: ., indices: indices(".")} | {value: .value, index: (.indices[1] // (.value | utf8bytelength))} | {value: .value, minor: .value[:.index]} ] | group_by(.minor) | .[] | max_by(.value) | .value '
出力
6.6 6.5.1 6.4.1 6.3 6.2.2 6.1.1 6.0.1 5.6.4