hitode909の日記

以前はプログラミング日記でしたが、今は子育て日記です

新風館ビール空間

新風館(オフィス近くの商業施設、数年間閉まっていてホテルになるべく改装中だった)がオープンしたので、目当てだったビール屋さんに行った。朝から整理券をもらい、昼休みに行ってボトルをおすすめされるがままに買って帰り、夜に飲んで、これはよいとまた飲みに行った。ビールのラインナップはよく分からなくて、フルーツが混ざっているやつとか酸っぱいビールが多かった。
その場で缶に詰めてくれるマシンがあっておもしろかった。缶詰工場っぽい。
200ml1200円くらいの高級ビールがあったので飲んでみたら自然発酵でウイスキーの樽で作ったやつ?でおいしかった。


www.fashion-press.net

自律

こないだ出てた10Xのバリューの記事で、自律する、が挙げられているのが興味深かった。
うちのチームは、リモートとは言っても、時差があるわけでもないので、皆の働く時間をあわせて業務時間中はリアルタイムに同期しながらやりましょう、という感じでやっていて、なんでも聞いてくださいって感じでやっている。
自律に寄せていくとタスクの切り分け方とかも変わってきそうで、ペアプロしまくって進めましょうというバディみたいな働き方というよりは、大きく切り出して渡しておくような形になりそう。
10x.co.jp

コアタイムを設けて残りは各自で、とか、自分の集中する時間で難しいことをする、とかをやろうとすると、自律や自走することは求められてくると思って、そうするとアルバイトや新卒を育てていくようなマインドとどう折り合いをつけられるものか考えている。教育係のようなロールだとわりきって、オンボーディングや新人向けのメンタリングをやっていくことになる?
Netflixの最強人事の本とかを読むと最強の社員しか雇わないので新人を育てているような様子はなかった。

追記

みんな大好き組織パターンには託児所というパターンがありましたね、という話をした。パターンとして存在するのはわかっていても、託児所が具体的にどのように運営されているのかは気になる。

炊飯

とりあえずティファールの鍋で炊いている。フタは密閉できてないのだけど、なんとなく炊けてはいる、という状況。もともと浸水とかやったことないので米に対する要求や期待感があまりないのかもしれない。

炊飯器壊れたのでおすすめ炊飯器情報ください

そろそろ炊けたかなと見に行ったら炊飯器が止まっていて、スイッチを押しても反応なく、ぬるま湯に米が浸かっていて晩ごはん延期になっている。

2015年に一人暮らしを始めたときに買って、

f:id:hitode909:20200608210328p:plain

スタイリッシュ炊飯器で炊いた米がうますぎる - hitode909の日記

2017年にも一度壊れていたようで、

去年は炊飯器が壊れ,カメラが壊れ

■ - hitode909の日記

そのときはヨドバシで修理してもらい、そのさい長期保証を使い切った。
価格コムで人気の炊飯器を見ても今使っている炊飯器のほうがかっこいい。
同シリーズで5.5合炊きがあったけどこんなでかいのは要らないと思う。

シャープ IH おひつ炊飯器 5.5合 KS-WM10B-T

シャープ IH おひつ炊飯器 5.5合 KS-WM10B-T

  • 発売日: 2019/07/12
  • メディア: ホーム&キッチン


二人で二合炊きだと少ないかというと全然そんなことはなくて、三食米を食べることはなくて、二合炊いて、余ったら冷凍している。炊き込みご飯を山ほど作りたいときにはホットクックで炊飯している。

追記

ガスコンロで炊飯できるのを思い出したのでティファールの鍋に移して、コンロで炊いてみた。炊けたようには見えるけど、途中で失敗してリカバリしたためかうまみは低い気がする。毎日炊くわけではないのだから土鍋とか買ってみればよいのかもしれない。

さらに追記

いろいろ教えてもらえた。

BackstopJSの落ちたテストをリトライしてレポートを1つにまとめてくれるコマンドラインツールを作った

社のプロダクトではビジュアルリグレッションテストのためのツールであるBackstopJSをここ半年くらい使っている。referenceという正解データの画像と、testとして開いてキャプチャした画像を見比べて、差分があれば失敗、というテストを書ける。テストケースの設定がJSONを書くだけなのでテストを書くコストが低いのと、結果的に画像に出る形でテストすることを強制される、というシンプルさが気に入っている。過去にはE2Eテストをいろいろ試したけどどれも最終的に滅びてしまい、BackstopJSはそんななか生き残っている希望のテクノロジー。
リリース前の確認フローにも使っているし、書いてる途中のコードの動作確認にも使えて、数ピクセルの意図しないズレを検知できたりしている。

便利だけど、運用上苦労している点もあって、テストの書き方が悪かったり、ネットワークの調子が悪かったり、で確率的に落ちるテストがあるので、リトライする仕組みを用意していた。

既存研究

落ちたテストをリトライしたいというissueで紹介されていた技がこれで、HTMLレポートが呼び出しているreportという関数を上書きして、その中で落ちたテストを順番に実行するというもの。
実際に動かしてみたわけではないけど、backstop testを複数回実行しているので、全ケースが載ったHTMLのレポートを作れない点が惜しいと思う。機械的にfilterをつけて再実行するというアイデアは参考になった。

#!/usr/bin/env node
# Name: backstop_retry.js
# Usage example: backstopjs --config=backstop.json || backstop_retry.js backstop.json

const { execSync } = require('child_process')
const fs = require('fs')
const path = require('path')

const configJsonPath = process.argv[2]
const configJson = JSON.parse(fs.readFileSync(configJsonPath, 'utf8'))
const reportConfigPath = path.join(process.cwd(), configJson.paths.html_report, 'config.js')

global.report = function (config) {
  // Fail fast if no timeout failure found
  config.tests.forEach((test) => {
    if (test.status === 'fail' && !(test.pair.engineErrorMsg && test.pair.engineErrorMsg.match(/Navigation Timeout Exceeded: \d+ms exceeded/))) {
      process.exit(1)
    }
  })
  // Retry each timeout
  config.tests.forEach((test) => {
    if (test.status === 'fail') {
      const label = test.pair.label
      console.log(`\nRetrying a failed scenario label="${label}"...\n`)
      const cmd = `backstop test --config=${configJsonPath} --filter="${label}"`
      try {
        execSync(cmd, { stdio: 'inherit' })
      } catch (err) {
        process.exit(1)
      }
    }
  })
}

require(reportConfigPath)
[New Feature Request] Retry Function · Issue #761 · garris/BackstopJS · GitHub

Jenkins上で落ちたテストを抜き出してリトライする

我々はJenkins上でBackstopJSを動かしていたので、Jenkinsでやってみよう、と最初に用意したのがこれで、Jenkinsfileのretryを使う手法。見どころは、jqを使ってjson_reportから失敗しているものを抜き出してfilter用の正規表現を作っているところ。

retry(5) {
  // レポートが上書きされないように、前回の結果を別ディレクトリに移しておく
  sh "cp -r backstop_data/ report/backstop_data_`date +%s`/; true"
  // --filter で (落ちたテスト名1|落ちたテスト名2) のような正規表現を渡す
  def filter = sh(
      script: "cat backstop_data/json_report/jsonReport.json | jq -r '[.tests[] | select(.status == \"fail\") | .pair.label] | join(\"|\")'",
      returnStdout: true,
  )
  def filterOption = filter ? "--filter '^(${filter.trim()})\$'" : ""
  // 何度かリトライするので、referenceは失敗してもジョブの失敗にしない
  stage 'Reference'
  sh "docker run --net=host --user=`id -u` --rm -v `pwd`:/src backstopjs/backstopjs reference --config backstop.js ${filterOption} || true"
  stage 'Test'
  sh "docker run --net=host --user=`id -u` --rm -v `pwd`:/src backstopjs/backstopjs test --config backstop.js ${filterOption}"
}

初回は--filterなしですべて実行→次回は落ちたところだけfilterを指定して実行、を繰り返して、テストが通れば完了。安定して動くようにはなったものの、いまいちな点がいくつかあった。

結果のレポートが複数に分かれてしまう

backstop testを一度実行するたびにHTMLのレポートができていく。5回実行しているので、最大5つのレポートを見て回ることになる。

f:id:hitode909:20200607173419p:plain
初回実行では133件成功、1件失敗
f:id:hitode909:20200607173425p:plain
二度目の実行では初回で失敗した1件が成功

JenkinsのHTML Publisherとの相性の悪さ

結果はHTML Publisherを使ってブラウザから見えるようにしていたのだけど、HTML Publisherの仕様で、HTMLが複数あるときには、こういう画面がJenkinsに現れる。素朴な画面で、どこからレポートを見れるのか初見ではわからない。

f:id:hitode909:20200607173355p:plain
この画面でindex.htmlをクリックするのが正解

  • Jenkinsfileでjqを呼び出したりしてすごい処理をやっていることへの違和感
    • Jenkinsfileはスクリプトを実行するだけ、くらいにとどめたい
    • GitHub Actionsからテストしたい場合はGitHub Actions界で使える技を探して再実装する、という無駄感

リトライし、レポートを1つに合体するグッズを作った

もろもろの困りごとを解消するためのコマンドラインツールを作ってみることにした。

  • レポート見づらい問題
    • → レポートは1つにまとめる処理を実装する。各回のレポートを見比べて、落ちたところがのちに通っていたら上書きしていけばよい
  • Jenkinsfileにロジックを書きたくない
    • → リトライ処理を持ったコマンドラインツールを作る。どのコマンドを何回実行するかを起動時に受け取る
    • そのさい、上でやっていたようなfilterの合成もおこなう
    • BackstopJSとセットで使いたいだろうからNode.jsで実装し、npm installで入手できるようにする

何度かリトライしたときの結果をつなげたアニメーションGIFがこういう調子で、上のテストは先に緑になる、下のテストはリトライされ続ける、全部通ったら終わり、というような形。

f:id:hitode909:20200607174221g:plain
テストがだんだん通る様子

これによって、リトライ機能はそのままに、レポートが1つに統一されて見やすい、という形を作れるようになった。

f:id:hitode909:20200607174319p:plain
134件のテストが通るというレポート

どのテストがリトライしているかは、成果物であるレポートでは見れないけど、実行時のログを眺めていると分かるようになっている。

#  Running(2/5) backstop test --filter '^(random_face)$'
#  BackstopJS v5.0.1
#  #  Loading config:  /Users/hitode909/co/github.com/hitode909/backstop-retry-failed-scenarios/examples/retry/backstop.json




backstop-retry-failed-scenariosコマンドが今回作ったもので、こういう形で実行する。リトライ回数、実行するためのコマンド、実行時に使うconfigファイルをそれぞれ指定する形。

$ backstop-retry-failed-scenarios \
    --retry 5 \
    --command 'backstop test' \
    --config backstop.js

BackstopJSをDockerで動かしてたらこんな感じで、commandにdocker runを書けば良い。

$ backstop-retry-failed-scenarios \
    --retry 5 \
    --command 'docker run --rm -v $(pwd):/src backstopjs/backstopjs test'




イマイチな点としては、BackstopJSが生成するJSONの構造に勝手に依存していることで、公開されたインターフェイスではなくて、HTMLを描画するアプリケーションが読んでいるJSのファイルを直接書き換えたりしていて、行儀は悪い。型定義が公開されていれば最新版と統合して動くか試したりとか、E2Eテストのようなものを用意して、実際にテスト中でBackstopと統合して動くか見ておけるとより良いかもしれない。

もう一つは、実装しきってから気づいたけど、社のプロダクトではNodeとBackstopJSを別のDockerイメージで動かしていたので、backstop-retry-failed-scenariosをDockerで動かし、そこに渡すcommandでもdocker runしようとすると、DockerからDockerを呼び出す形になってしまう。これを避けるためには、Nodeが入っていて、BackstopJSやGoogle Chromeが入っていて、今回実装したbackstop-retry-failed-scenariosもインストールした全部入りイメージを作る必要が出てきて、全部入りの巨大イメージができてしまって悲しかった。
Nodeが動いてるイメージはFROM nodeしてチョロっと必要なものを入れる、くらいにとどめたいし、BackstopJSのイメージはGoogle Chromeのダウンロードとかが必要なので自前で面倒見たくない、というあたりがイマイチ。


BackstopJS使ってる人と知見交換したいけどあまり出会いがない、友だちになってください、よろしくお願いします。
github.com
www.npmjs.com