つばくろぐ @takamii228

知は力なり

jqのyaml版コマンド yq は2種類ある

以前flutterのバージョン切り替えをCI環境で動的にやる記事の中で、yamlの指定したキーを取り出すのに yqコマンドを使っていたのですが、このyqの利用に関してハマったことをメモとして残しておきます。

takamii.hatenablog.com

yqとは

yqとはjqのyaml版でyamlに対してクエリを発行して部分的な文字列を出力するコマンドです。

jqはこちら。

stedolan.github.io

"yq" でググると2つのyqがヒットする

Googleyq を検索すると、なんと全く異なる2種類がヒットします。

https://www.google.com/search?q=yq

Python製のyq

1つ目はpythonベースの yq コマンドです。

github.com

kislyuk.github.io

pypi.org

yq takes YAML input, converts it to JSON, and pipes it to jq:

とあるので、yamljsonに変換して引数のコマンドを処理しているように見えるのでjqでできることは基本できるようです。jqがないと動きません。

$ cat pubspec.yaml
environment:
  sdk: ">=2.1.0 <3.0.0"
  flutter: 1.12.13+hotfix.8
...

$ cat pubspec.yaml | yq -r .environment.flutter
1.12.13+hotfix.8

Go製のyq

もうひとつはGoでかかれたyqです。こちらはjqが入ってなくても動きます。

github.com

mikefarah.gitbook.io

Pythonのypと比べて、diffやマージ等ができるようです。参照のコマンドも微妙に違う。

$ cat pubspec.yaml 
environment:
  sdk: ">=2.1.0 <3.0.0"
  flutter: 1.12.13+hotfix.8
...

$ yq r pubspec.yaml environment.flutter
1.12.13+hotfix.8

brew install yqで入るのはどっち?

brew install yq で入るyqは、Go製のyqです。

$ brew install yq
==> Downloading https://homebrew.bintray.com/bottles/yq-3.2.1.catalina.bottle.tar.gz
==> Downloading from https://akamai.bintray.com/8c/8cbb0eda1f9d8c20342c41979e2cca5440e6215e85c36a3f2
######################################################################## 100.0%
==> Pouring yq-3.2.1.catalina.bottle.tar.gz
🍺  /usr/local/Cellar/yq/3.2.1: 5 files, 5.9MB

Python製のyqを入れるにはpip経由で入れるか、brewpython-yqと指定するようです。

$ brew install python-yq
==> Downloading https://homebrew.bintray.com/bottles/python-yq-2.10.0_1.catalina.bottle.tar.gz
Already downloaded: /Users/takami228/Library/Caches/Homebrew/downloads/4488cbac19c6771a781cfdfe3532dc49d77b45b388fc700a12154ac416867ade--python-yq-2.10.0_1.catalina.bottle.tar.gz
==> Pouring python-yq-2.10.0_1.catalina.bottle.tar.gz
🍺  /usr/local/Cellar/python-yq/2.10.0_1: 99 files, 617.1KB

どっちがいいの?

どちらもそれなりのスターがついていて、利用記事も出てきます。

ドキュメントを読む限りGoのyqのほうがコマンドがリッチそうに見えますが、指定したキーを取り出すだけの用途であればどちらでもよさそうです。

CIやスクリプトに組み込んでチームとして利用していく場合は、どちらのyqコマンドを使うのかをきちんと明記して、統一しておくのが良いでしょう。

※私のチームでは一時期両方のコマンドが混在していて混乱しました。

ライブラリやプラグインを使うときに気にしていること

アプリケーション開発において実装したい機能を実現するために、世の中に公開されているライブラリやプラグインを使ってフルスクラッチでの実装の手間を省くことがあります。その一方で、プラグインを理由にプラットフォームのアップデートを断念したり塩漬けにしているシーンをよく見かけました。

これまでJenkins、WordPress、Flutterとプラグインを選定する場面が多くあったなーと思っていて、知らず知らずのうちに自分の中での選び方の基準みたいなものができつつあったのでここで言語化しておこうと思います。

なお機能的な要件を満たすことは大前提となるのでここでは省きます。

1. 公式のプラットフォームで公開されていること

野良プラグインは怪しさ満点です。公式のマーケットプレイスで公開されているもの、さらに言えば公式が認めているもの(公式バッチがついている、メンテナーが企業になっている等)を使いましょう。

また実際にどう動いているのかソースが追えるようにGitHubでソース公開されているものを利用しましょう。

2. 評価が高く、利用実績が豊富であること

他の人も利用していて、かつ評価が高ければ、信頼度もあります。

3. ドキュメントが充実していること

2の裏返しかもしれませんが、ドキュメントがあると使いやすいですよね。

4. ライセンスの自由度が高いこと

MITやApache2.0など、再利用に関して自由度が高いものが法的リスクが低いです。

5. 更新が活発であること

プラグインが動作するプラットフォームや依存するソフトウェアに追従できていないと、あっという間に負債になってしまいます。

GitHubを見に行ってcommit数が順調に伸びていたり、Issueに対してきちんと対応されているかを見ましょう。

6. プラグインの内部のライブラリの依存リスクが明確になっていること

利用しているライブラリやプラグインが依存するものが大きいと、結局そこがボトルネックになってしまいます。

プラグインの中で依存するライブラリの中に、枯れたライブラリがないか目を通しておきましょう。

7. いざとなったら自分たちでメンテナンスできること

最終的に利用を続けるとなったときに、自分たちでパッチを充てたり修正していける内容なのかを見ましょう。

仮にそうだったとして、そのプラグインを使い続けるのか、余裕のあるときに自分で独自実装してしまうのかは再度立ち返って考えてみるといいと思います。


結局はソフトウェアのバージョンアップライフサイクルを考えたときに、それに依存するものを切り分けて、メンテナンス対象としてどう扱っていくかをきちんと考えよう ということなのだと思います。最近は言語やFWのバージョンアップライフサイクルは早く、1年も経てばあっという間に古くなってしまいます。

三者の成果物を利用することは実装の手間を省くことができる一方で、時間が経ってから自分たちが取り扱えなくなったり依存関係によって更新できない負債と化すリスクもあるので十分注意しながら選定したいものです。

App StoreとGoogle Playの評価を日時で通知する

自分たちが関わっているサービスに関する世の中からのフィードバックを確認することは、そのアプリのマーケットでの立ち位置の把握や開発チームのモチベーションを維持する上で重要です。

iOSであればAppStore、AndroidであればGoogle Play上でユーザから5段階評価とコメントが載せられています。

ここの情報をチャット上に日時で通知する仕組みを作ってみたのでまとめます。

Nodeのモジュールでスクレイピング結果を取得する

いろいろ検索してみたら、ちょうどよさそうなnpmモジュールがあったので、Node.jsで書くことにしました。

www.npmjs.com

www.npmjs.com

こんな感じでアプリケーションのIDと言語を指定するだけでスクレイピング結果をJSONオブジェクトとして取得できます。今回はサンプルとしてChromeアプリを使います。

const store = require('app-store-scraper');

const iosAppId = '535886823';
const iosReview = store.app({id: iosAppId, country: 'jp'});

iosReview.then(function(value){
    console.log(value);
});

Google Playの場合

const gplay = require('google-play-scraper');

const androidAppId = 'com.android.chrome';
const androidReview = gplay.app({appId: androidAppId, lang: 'ja', country: 'jp'})

androidReview.then(function(value){
    console.log(value);
});

Androidの方はscoreGoogle Playで表示されている値がとれそうですが、iOSの方はappではscoreの値は0.5刻みで丸め込まれているようなので、ratingsから自分で平均点を計算します。

const store = require('app-store-scraper');

const iosAppId = '535886823';
const iosRatings = store.ratings({id: iosAppId, country: 'jp'});

iosRatings.then(function(value){
    console.log(value);
   // 自分で平均点を計算
    var rate = (1*value['histogram']['1'] + 2*value['histogram']['2']
    + 3*value['histogram']['3'] + 4*value['histogram']['4'] + 5*value['histogram']['5']) / value['ratings'];
    console.log(rate);
});

結果

{
  ratings: 8103,
  histogram: { '1': 1998, '2': 918, '3': 1269, '4': 1426, '5': 2492 }
}
3.184622979143527

チャットへPOSTする

あとはこの結果をSlackやMattermostへ連携するだけです。それぞれWebhookのURLを発行し、POSTのリクエストを組めばOKです。

slack.com

docs.mattermost.com

今回はMattermost用に作っていて、Slackでも使えるように拡張しようと調べたときに気づいたのですが、Slackの投稿領域のMarkdownって結構制約があるんですね。表が使えない。

www.markdownguide.org

コード一式はGitHubにまとめました。よかったら参考にしてみてください。

github.com

実行するときはnodeコマンドでスクリプトを実行すればOKです。

$ npm install
$ node app.js

f:id:takamii228:20200223163229p:plain

f:id:takamii228:20200225123453p:plain

無事表示されました。

これをAWS LambdaやGitLab CI/JenkinsのScheduledジョブに設定しておくことで、毎日のストア評価の変化を追うことができるようになります。