つばくろぐ @takamii228

知は力なり

GitLabでtagとtagの間に含まれるMRリストを取得する

GitLabであるタグに含まれるMRのリストをトレースする必要があったので、GitLabのAPIとjqを使ってtagとtagに間含まれるMRリストを出力して、Markdown形式のリリースノートを出力するシェルを組みました。

#!/bin/sh

BASE_URL="GITLAB_HOSTNAME"
API_TOKEN="GITLAB_APITOKEN"
PROJECT_ID="xx"
PROJECT_NAME="yyy"
REPOSITOEY_NAME="zzz"

TAG_FROM_AUTHORD_DATE=`curl -X GET -H "PRIVATE-TOKEN: ${API_TOKEN}" "${BASE_URL}/api/v4/projects/${PROJECT_ID}/repository/tags/${1}" | jq -r .commit.authored_date`
TAG_TO_AUTHORD_DATE=`curl -X GET -H "PRIVATE-TOKEN: ${API_TOKEN}" "${BASE_URL}/api/v4/projects/${PROJECT_ID}/repository/tags/${2}" | jq -r .commit.authored_date`

# tagを切ったコミットハッシュのMRが含まれない可能性があるため1分ずらす
TAG_FROM_AUTHORD_DATE=`date -v+1M -j -f "%Y-%m-%dT%T.000Z" ${TAG_FROM_AUTHORD_DATE} +%Y-%m-%dT%T.000Z`
TAG_TO_AUTHORD_DATE=`date -v+1M -j -f "%Y-%m-%dT%T.000Z" ${TAG_TO_AUTHORD_DATE} +%Y-%m-%dT%T.000Z`

curl -X GET -H "PRIVATE-TOKEN: ${API_TOKEN}" "${BASE_URL}/api/v4/projects/${PROJECT_ID}/merge_requests?state=merged&updated_after=${TAG_FROM_AUTHORD_DATE}&updated_before=${TAG_TO_AUTHORD_DATE}&target_branch=master&view=simple&per_page=100" | jq -r 'sort_by(.iid)| .[] | "- [" + (.title|tostring) + "](${BASE_URL}/${PROJECT_NAME}/${REPOSITOEY_NAME}/merge_requests/" + (.iid|tostring) + ")"'

使い方は引数に2つのタグを入れるだけです。

# 実行方法と出力サンプル
$ ./generate-releasenote.sh tag1 tag2
- [MR Title1](${BASE_URL}/${PROJECT_NAME}/${REPOSITOEY_NAME}/merge_requests/mr_id1)
- [MR Title2](${BASE_URL}/${PROJECT_NAME}/${REPOSITOEY_NAME}/merge_requests/mr_id2)
- [MR Title3](${BASE_URL}/${PROJECT_NAME}/${REPOSITOEY_NAME}/merge_requests/mr_id3)

中でやってるのは以下の通りです。

  1. tag1、tag2が打たれたタイムスタンプを取得する
  2. 1で取得したタイムスタンプをdateコマンドで1分後ろにずらす
  3. 2を使ってMRリストを取得するAPIリクエストを投げてタイトルとIDのリストを取得する
  4. jqで加工してMarkdown形式で出力する

tag1、tag2のタイムスタンプをそのまま使ってしまうとtag1・tag2がマージコミットだった場合に出力されるMRリストに含まれない可能性があるのでdateコマンドで1分後ろにずらしています。

注意点としてはマージ先のブランチを考慮にいれてないので、もし複数のブランチへのMRがある場合はそれらは区別されずに表示されます。jqのクエリにmerge先のブランチも指定するようにすれば対応できると思います。

またクエリにヒットするのはタイムスタンプの間に更新があったMRリストなので、もしマージされたあとにコメント書いたりして更新されたものがあるとヒットしてしまいます。このへんは運用で対処しました。

あとはMR一覧を取得するAPIは一度に取得できる数がデフォルトだと20件なので、per_pageで最大値である100を指定しています。

これを日々行うことで、リリース前後でどのbugfixがどのタグに含まれているか追跡するのが楽になりました。

https://docs.gitlab.com/ce/api/merge_requests.html

AtCoderの色が緑で停滞している

AtCoderを再開した話を以前書いた。

takamii.hatenablog.com

あれからもぼちぼち続けて晴れて緑になったのだけど、その後は800くらいで停滞している。

f:id:takamii228:20191002005624p:plain

水色になりたいと↑では語っていたのだけど、水色になるには今のABCでE問題まで常に解けるくらいにならないといけなさそうだ。

実力的にはEよりもまずABCDを確実に早く解けるようになるのが先だと思い、とりあえず過去問を埋めることにした。

f:id:takamii228:20191002005910p:plain

https://kenkoooo.com/atcoder/#/user/takamii228

8月中にA、B、Cは埋められそうな勢いだったけど仕事が忙しくなったので停滞気味。

A、B、Cを埋めるとsortとか簡単な全探索はパッと書けるようになったので、グラフ問題とかDPの式構築がサクッと解けるようになるのが次のステップかなと思った。

ゆるーく楽しみながら続けたいと思いましたまる。

flutter driverでassetsが原因で画像が表示されなくなった件 #flutter

f:id:takamii228:20190829204538j:plain

flutter driverでE2Eテストをゴリゴリ書こうとした時にハマった内容と暫定的な対処方法を共有します。

ある日突然flutter driverで画像が表示されなくなった

flutterではflutter driverというIntegration test用のFWが用意されています。

flutter.dev

公式を参考にそれっぽいシナリオを実行していたのですが、ある日突然今まで成功していたシナリオが失敗するようになりました。

あれれと思ってエミュレータを見てみると、画像が一切表示されていませんでした。

flutter doctorはこんな感じ。

$ flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[] Flutter (Channel unknown, v1.5.4-hotfix.2, on Mac OS X 10.14.6 18G87, locale ja-JP)
[] Android toolchain - develop for Android devices (Android SDK version 29.0.0)
[] iOS toolchain - develop for iOS devices (Xcode 10.2.1)
[] Android Studio (version 3.4)
[!] VS Code (version 1.37.1)
    ✗ Flutter extension not installed; install from
      https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter
[] Connected device (2 available)

assetsの数が多すぎるのが原因っぽい

おかしいなと思ってコミットの差分を追ってみると、どうやら画像を大量に追加した前後で挙動がおかしくなっていた。

でも普通にflutter runすると正しく画像は表示されていた。おかしいなぁと思っていろいろググってみるとgithubのIssueを発見。

github.com

After days of investigation, I've found that it may have the same cause with #24703. When the AssetManifest.json file size larger than 10 * 1024, it will call compute, then Isolate.spawn will be called which won't work for flutter drive. So images can NOT be displayed. However when AssetManifest.json file size smaller than 10*1024, then It works well. That's why image assets can be displayed for some certain apps.

なんと、AssetManifest.jsonのファイルサイズが 10 * 1024を超えると新しくIsolate.spawnが呼ばれて、画像が読めなくなってflutter driverが動かなくなる模様。

AssetManifest.jsonはassetsのファイル名とパスをkey / valueでまとめたjsonファイルで、iosはApp.Frameworkの中に、androidはbuild配下にビルド時に自動生成されるファイルのようです。

assetsはpubspec.ymlでフォルダごと読み込むようにしていました。

...
flutter:
  uses-material-design: true

  assets:
    - assets/
...

ファイルのサイズを確認すると、確かに10 * 1024は超えていてflutterのObservatory URLを見に行ってみるとspawnのisolateが起動されていました。

$ ls -la AssetManifest.json 
-rw-r--r--  1 takamii228  staff  10905  8 28 22:11 AssetManifest.json

f:id:takamii228:20190829201850p:plain

対象方法

えーどうしよう...と思ってflutterのソースを眺めてたらそこの制御をしているコードを発見。

github.com

  Future<String> loadString(String key, { bool cache = true }) async {
    final ByteData data = await load(key);
    if (data == null)
      throw FlutterError('Unable to load asset: $key');
    if (data.lengthInBytes < 10 * 1024) {
      // 10KB takes about 3ms to parse on a Pixel 2 XL.
      // See: https://github.com/dart-lang/sdk/issues/31954
      return utf8.decode(data.buffer.asUint8List());
    }
    return compute(_utf8decode, data, debugLabel: 'UTF8 decode for "$key"');
  }

Pixel 2XLだと、10 * 1024を超えるとparseに3msかかるらしい。3msくらいええやん...。

github.com

最新のmasterでもこのソースのままだったので、アドホックですがflutter driverの実行前後でこの上限を緩和するpatchを当てるようなシェルを組みました。

asset_bundle.patch

--- a/development/flutter/packages/flutter/lib/src/services/asset_bundle.dart
+++ b/development/flutter/packages/flutter/lib/src/services/asset_bundle.dart
@@ -67,7 +67,7 @@ abstract class AssetBundle {
     final ByteData data = await load(key);
     if (data == null)
       throw FlutterError('Unable to load asset: $key');
-    if (data.lengthInBytes < 10 * 1024) {
+    if (data.lengthInBytes < 1000 * 1024) {
       // 10KB takes about 3ms to parse on a Pixel 2 XL.
       // See: https://github.com/dart-lang/sdk/issues/31954
       return utf8.decode(data.buffer.asUint8List());

flutter-driver.sh

#!/bin/sh

set -x

# 実行前にパッチを当てる
CURRENT_DIR=`pwd`
cd $HOME
patch -p1 -N < ${CURRENT_DIR}/asset_bundle.patch || true
cd ${CURRENT_DIR}

# flutter driverを実行
flutter driver --debug --flavor develop --target test_driver/app.dart  -d xxxxxx

# 実行後は元に戻す
cd $HOME
patch -p1 -N -R < ${CURRENT_DIR}/asset_bundle.patch || true
cd ${CURRENT_DIR}

パッチを当ててから実行するようにしたら無事spawnのIsolateが消えてflutter driverでも画像が表示され、E2Eテストが再び通るようになりました🎉🎉🎉

f:id:takamii228:20190829203547p:plain

flutter driverは公式の情報がまだまだ少なく、この後もいろいろ踏みそうですがなんとか食らいついて頑張っていこうと思います。