Open140

runn開発者会議スレッド

katzumikatzumi

ファイルの存在有無などで実行するステップを変えたい

現状の機能ではifセクションでステップの実行可否を制御できる
exec runnerで test -f でファイルの存在チェックして、ifセクションで参照すればできるがステップが分断されるので可読性が良くない。
また現状ではファイル関係のbuilt-in関数はなし
ファイルの存在チェックの機能があると良さそう

想定しているユースケース

  • シナリオ実行の事前条件を実現する
    OpenAPIのSpec出力とか
    認証情報(Tokenを別途取得しておく)
  • テスト条件をファイル作成
    テストのseedデータを事前取得しておく
  • APIのレスポンスをキャッシュ化して高速化する
    マスターデータ等は、ローカルで保持しておくのも良いかもしれない

実装idea

  • makeコマンドのbuildの様に依存しているファイルの関係を定義してステップを変える
    targetが存在しないもしくはsourceファイルのほうが、targetファイルよりも新しい場合はステップを実行する
  • Fileランナーを作っても良さそう
    ビルトイン関数よりも筋は良さそう
katzumikatzumi

Property based testingをしたい

OpenAPIのSpecからテストケースを自動生成できないか?
→ ライブラリは幾つかありそう

脆弱性診断を目的とするのであればfuzzing test?

katzumikatzumi

Golangで使えそうなライブラリはあるだろうか?

こちらが見つかったが。。
https://github.com/flyingmutant/rapid

OpenAPIやgRPCやGraphQLとかのスキーマ定義に対して、汎用的にテストデータを作成できるか?

もし実装するとして、どういうI/Fにするか?

katzumikatzumi

https://okiyama.dev/posts/2022-10-19-golang-package-testing-quick/

leanovate/gopter の方が有名っぽい

既存の(http|grpc|graphql) runnerのオプショナルなパラメータを追加して、特定のステップでプロパティベースドテストを実行できるようにする

APIチェーン的に引き継ぐべきパラメータとAPI仕様書のプロパティベースドで生成したパラメータをいい感じにmergeして実行できると良さそう。
使い勝手を考えると、通常通り指定されたパラメータの内、生成されたパラメータで上書きしないパラメータ名を指定できると良さそう? 🤔

katzumikatzumi

シナリオテスト自体をドキュメント化したい

現状ではlistコマンドでシナリオ一覧までを出力はできるが、シナリオのステップ及びステップ内のテスト項目も含めてドキュメントすることはできない。
ステップの内容についてはverboseオプション時の出力内容でカバー出来るかもしれないが、実行しないと全体のステップを把握することはできない。
yqコマンドで静的にrunbookを解析してドキュメントを生成することは出来るが、runn側のparse機能をベースにドキュメント出力のサポートができると良さそう(runnをパッケージ利用してドキュメントツール自体は別ツールとして提供するか?)

テスト内容についてはkarateの様にGherkin記法みたいに可読性を担保したテスト式を書けると良さそうであるが、Gherkin記法自体は馴染まなさそう。

jestのdescribeやitとかテストの条件を装飾できると良さそう。

Hidden comment
Hidden comment
katzumikatzumi

ゴールデンテストのファイルとレスポンスを効率よく比較したい

  • 想定するユースケース
    CSVファイルをレスポンス出力するようなシナリオで、レスポンス内容をファイルとして保存してゴールデンテストとする

  • 現状のシナリオのフロー

    1. CSVファイルをダウンロードするAPIを呼び出す
      ステータスコードのみチェック
    2. 期待値となるファイル名と同じパスに拡張子.actualのファイルとしてdumpする
      期待値ファイル: path/to/expected.csv
      検証対象ファイル: path/to/expected.csv.actual
    3. それぞれのファイルを外部コマンドのdiffで比較する
    4. diff実行のステータスが0以外(=差分がある)の場合は内容を出力し、testで失敗もさせる
      ※このステップで failセクション が欲しかった

上記の2〜4までのステップが共通っぽいので、共通のシナリオとしてincludeして実行させている
include時のvarsとして、期待値ファイルのパスと、レスポンスの内容を渡す

  • ゴールデンテストの入れ替え作業
    テスト結果が失敗したら、actualファイルを手動でcpして期待値ファイルを上書きしている

やりたいこと(解決したいこと)

  • ファイルの比較をdiffコマンドをやめたい
    オンメモリで比較したい。パフォーマンスの問題と地味にscopeの権限付与が面倒
  • ゴールデンテストとなる期待値のファイルの差し替えを自動化したい
    イメージとしてはUPDATE_GOLDEN環境変数みたいにオプション指定したら期待値のファイルを書き換えたい
Ken’ichiro OyamaKen’ichiro Oyama

2024年中にv1にしたい

もうマイナーバージョンが3桁になる状況なので2024年中にv1にしたいです。

が、何をトリガーにv1にするべきか...ということに悩んでいます

katzumikatzumi

永遠のベータは最近は流行らないですかねー? 🤔
注目度が上がり、他のツール等の連携も増えてきているので、ちゃんとメジャーリリースした方が対外面では良さそうという判断でしょうか?
一つ確認しておきたいのですが、バージョンのナンバリングには意味を持たせますか(持たせたいですか)?
v1はLTSバージョンでv2は開発バージョン(比較的I/F変更が活発)とか。。
tagpr使っているので、2つのメインストリームを持たせるのは難しいかもと思っていますが。

考えられるトリガーは以下の感じですかね。

  • Breaking Changeがある機能が反映されてから(反映してから)
  • 初期構想の機能がおおよそ実装が出来た場合
  • 新しい機能追加の方向性が決まった段階
  • 周年(ファーストコミットからの経過日数?)
  • 依存しているライブラリがメジャーバージョンアップした場合(Golangのバージョンも?)
Ken’ichiro OyamaKen’ichiro Oyama

注目度が上がり、他のツール等の連携も増えてきているので、ちゃんとメジャーリリースした方が対外面では良さそうという判断でしょうか?

はい。主に「オペレーションツールもしくはテスティングツールとして機能を枯らしたい」というのが理由です。
いつまでたってもBreaking Changeがガンガンあるようなツールだと使う側(runnの上に資産を積み上げる側)としては安心できないと思っています。
v1にしたあかつきには機能の追加はあっても挙動の変更はしないようにしたいと考えています。

バージョンのナンバリングには意味を持たせますか(持たせたいですか)?

はい。なの「でできるかぎりSemVer」を採用したいと思っています。
幸い、Goは後方互換性を保ちつつバージョンアップをしています。Goがv1の間は何も考える必要はないと思っています。runnもそのようにしたいと考えています。

とはいえ開発体制から考えてもLTSなどはサポートできないので、単純にSemVerで開発すればいいのではないかと思っています。

tagpr使っているので、2つのメインストリームを持たせるのは難しいかもと思っていますが。

これは必要になったらtagprに機能追加を提案すればいけそうだと考えています。

考えられるトリガーは以下の感じですかね。

良いと考えているのは「初期構想の機能がおおよそ実装が出来た場合」なんですけど、すでに私の想定を超えているので、どうしたもんかなーと。

まあ、「SemVerにする(v1にして挙動の変更はバージョンを上げる)」というだけなので記念的な何かでもいいとは思っています。

ちなみに、ガンガン開発した結果、v1がv100に到達してもそれは別に問題ないと思っています。なので開発に影響はないかなと...(たぶん気分の問題になりそう)

Hidden comment
Hidden comment
Hidden comment
Hidden comment
Ken’ichiro OyamaKen’ichiro Oyama

runn lint

ある程度「こうあった方がいい」というようなルールを提供したいなと思っています。

  • ランブックのdescがない
  • ステップのdescがない
  • ランブックのdescが重複している
  • ランブックの書き方が List と Map で混在している

などがあるだけで便利かなあと。

katzumikatzumi

同一step内で結果確認する場合はcurrentを使っているか?とかですかねー

katzumikatzumi

includeでのファイル名の大文字・小文字違いで環境依存でテスト失敗するケースがあるのでlintで気づきたいです。

Ken’ichiro OyamaKen’ichiro Oyama

includeでのファイル名の大文字・小文字違いで環境依存で

お、これはどう判定する(何を正しいとする)といいんですかねー

Ken’ichiro OyamaKen’ichiro Oyama

ランブック単位でのクリーンアップ処理

afterHookとかtearDownとかCleanupとか言われるアノ機能です。

まず、個人的には(beforeなんとか)前処理はいらないと思っています。steps: に書けば良い。

その上でクリーンアップ処理は後処理というか後始末処理としてあるのは良さそうな気はしています。

runnをGoのパッケージとして使う場合の実装はすでに runn.BeforeFunc と runn.AfterFunc があるので、ランブックで実現するクリーンアップ処理について考えたいです。

関連Issue

https://github.com/k1LoW/runn/issues/397
https://github.com/k1LoW/runn/issues/612

Ken’ichiro OyamaKen’ichiro Oyama

今のところ良いと思っているアイデアは「ステップへの defer: の導入です」
Go言語における defer ステートメントとほぼ同じ挙動をするイメージです。

メリット

  • ステップの実行の途中で順々に後処理を追加できるという柔軟性がある
  • ステップをそのまま後始末処理に回せるのでランブックのYAMLのネストが増えないし新しいセクションも増えない

デメリット

  • defer の考え方が Gopher 以外にはわかりにくい( defer: でなくてもいいのかも)
  • step[*] 構文や current previous 構文と相性が悪い
    • しっかり検討してデザインをする必要がある
katzumikatzumi

ステップへの defer: の導入です

runBookに対してdeferなのか?stepに対してdeferなのかによって結構I/Fが変わりそう
runBookだったら、後処理のイメージがつきやすい?

step[*] 構文や current previous 構文と相性が悪い

last という構文を用意して、どのstepまで処理されたのか?確認しながら

ステップの実行の途中で順々に後処理を追加できる

runBookでもincludeさせればそれほど制約にはならなさそう。

Ken’ichiro OyamaKen’ichiro Oyama

runBookに対してdeferなのか?stepに対してdeferなのかによって結構I/Fが変わりそう

ランブックに対してです。
ランブックをGoにおける関数ブロック、各ステップを関数ブロック内の処理だとイメージしてもらえると良いかと。

last という構文を用意して、どのstepまで処理されたのか?確認しながら

どちらかというと defer とマークされたstepは実行されずにランブックの終了まで待つイメージです。

次のようなランブックがあったとき

steps:
  -
    test: true
  -
    defer: true
    test: true
  -
    test: false

実行順は0->2->1になります。
かつ、2が失敗しても1はdeferとマークされているので実行されます。

そうすると、「step[*] 構文や current previous 構文と相性が悪い」というのがわかるかと思います。

具体的には3つめのstep(つまり2)で previous を使っていた場合、それは0にかかるのか1にかかってしまうのか。みたいな感じで悩ましいのです。

Ken’ichiro OyamaKen’ichiro Oyama

ステップの実行の途中で順々に後処理を追加できる

runBookでもincludeさせればそれほど制約にはならなさそう。

steps:
  -
    test: true
  -
    defer: true
    test: true
  -
    test: false
  -
    defer: true
    test: true
  -
    test: true

このとき、実行順は0->2->1です。

もし2が test: true なら 0->2->4->3->1 です。

このように段階的に後処理(1と3)を追加できるのが便利です。

katzumikatzumi

goconでディスカッションした内容をベースに決めたいことをまとめました

論点 案1 案2 案3
クリーンアップ処理単位 runBook step単位 runner単位
stepリストカウントアップ方式 枝番あり 実行順番で単純カウントアップ。枝番なし カウントアップなし
前処理結果判定方法 専用別名(last) 既存方式(previous) deferred-objectベース
done, fail, always
実行タイミング runBook終了後 step終了後 -
同期 クリーンアップ処理終了までwait 非同期 -
クリーンアップ処理中のエラーハンドリング なし
前処理の結果次第で停止と継続
あり
testセクションサポートあり
test結果次第で停止
非同期でハンドリングNG
クリーンアップ処理の後方参照 枝番で参照可能 クリーンアップ処理は通常のstepと同様に結果保持 非同期で結果保持なし
katzumikatzumi

どの論点を軸に優先させたいか?によって案の組み合わせはいくつかのパターンで表せるハズ。
パターンをABCでまとめて判断してみる。

Ken’ichiro OyamaKen’ichiro Oyama

私個人としては「runbook単位」のみかなあと思っています。
理由は「外部APIの実行に失敗したらDBの処理を戻す」「アップロードに失敗したら一時ファイルを削除する」など、後始末処理が複雑なユースケースがあるため、ステップ単位やランナー単位では対応できないと考えているためです。

Ken’ichiro OyamaKen’ichiro Oyama

クリーンアップ処理単位「runbook単位」より「1実行単位」が良い気がしています。

Include先で書いた defer はそのIncludeした側のrootランブックの実行終了時に実行されて欲しいことが多い...(今欲しいやつ)

Ken’ichiro OyamaKen’ichiro Oyama

JSON Schemaの導入

具体的に何に使いたいかというと、各ステップのフォーマットベースのバリデーションです。
書きやすさを重視していることからネストが1つずれるだけで意図した形で動かなくなります。

なのでJSON Schemaによるバリデーションを内蔵したいなあと考えています。各ランナーごとに。

その先には、カスタムランナーのカスタムバリデーションをJSON Schemaで追加できるようになると良いなあと思っています。

katzumikatzumi

各ランナーのValidatorというとHttpRunnerのOpenAPI Specと、gRPCRunnerのprotoがありますが、それとは別ということでしょうか?
runBookの構文チェック的にJSON Schemaを定義する感じでしょうか?
それとも、どこのyamlの一部分に対してJSON Schameを使って構文をチェック感じでしょうか?

Ken’ichiro OyamaKen’ichiro Oyama

CEL(Common Expression Language) 対応

個人的には、expr-langで全く困ってないのですが、新規ユーザはCELのほうがいいのかも?などと思っています。

できれば「両方対応」が実現できたらいいなあと思っています。

Hidden comment
katzumikatzumi

ステップの時間計測

ステップ毎にかかった時間を計測したい
定点的に観測して遅くなったステップの変化を捉えたい
オプションをつけたらかかったステップの時間が表示されるでも良いかも知れない

Ken’ichiro OyamaKen’ichiro Oyama

ステップ毎にかかった時間を計測したい

profileで取得できますね。逆にいうと実行時間はprofileをonにするのと同じことになります。

Ken’ichiro OyamaKen’ichiro Oyama

needs: セクションの追加

依存するランブックを指定でき、そのランブックの実行結果(bindした値)を使用したランブックが書けるようにする。

Includeランナーと違うのは依存先ランブックに対して何も干渉ができない代わりに、
ランブックAの値を、(2度とランブックAを実行せずに)ランブックBとCで再利用して使うということが可能になる。

[...]
needs:
  init: path/to/init.yml
  apiinit: path/to/apiinit.yml
steps:
  -
    exec:
      command: echo '{{ needs.init.secret }}'
Ken’ichiro OyamaKen’ichiro Oyama

needsセクションを作ることができたら、いわゆる前処理の書き方に選択肢が生まれる

katzumikatzumi

凄い未来を感じます。
妄想ですが、複数シナリオ実行時に

  1. needsで指定されたシナリオが既に実行済みの場合はその結果を踏まえて処理する
  2. needsで指定されたシナリオが未実行の場合は、ランブック実行前にシナリオ実行される

となったら、処理時間の短縮にも繋がりそう。
bind変数を保持し続けないといけないのでメモリが凄いことになりそうですが 😅

Ken’ichiro OyamaKen’ichiro Oyama

妄想ですが、複数シナリオ実行時に

求める挙動が完全に同じです!

処理時間の短縮にも繋がりそう。

まさにこれがしたい感じですね。

bind変数を保持し続けないといけないので

現状、実行後にstoreの値は消していないんですよね。

https://github.com/k1LoW/runn/blob/b607f602729f286fd8f747c4bb76fd2e790c2f49/operator.go#L115-L118 で実行後に値を取れたりするので。

なのでメモリ使用量は変わらない可能性があります(調整もしないとな)。

katzumikatzumi

APIシナリオテストをMockサーバー(テストダブル)化

APIシナリオテストからシナリオ内容からMockサーバー化できないか?という妄想。
単純にMockサーバーにするという話ではなくテストダブルにできないか?という内容です。

ゴールのイメージとしては以下の様になります。

SPAなシステムのバックエンドのAPIのテストをrunnで書いたら、フロントエンドのテスト用にモックサーバーを立ち上げることができ、フロントからシナリオどおりのレスポンスを返却することができる。
シナリオテストの想定以外のリクエストが送信された場合に、エラーにするという内容です。

これはマイクロサービス間のAPIテストで環境を整備するのが辛い問題を解決する一つのアイデアになります。

鴨川でお話をしたテストダブルの件と同じでしょうか?

以下の記事が参考になると思います。Karate vs Karateのイメージが素敵です。
https://qiita.com/takanorig/items/5f63806219ae30a8f09a

Ken’ichiro OyamaKen’ichiro Oyama

なるほど!すっきり理解できたかもです。

frontend -> backend な構成において

  1. runn -> backend でシナリオテストを実行する
  • この時runnはリクエストとレスポンスを何かしらの形でファイル等に書き出しておく(仮に「カセット」とする)
  1. runn(もしくは別のなにか)はカセットを使ってモックサーバとして起動する
  2. frontend -> runnモックサーバでテストが実行できる

こんなイメージでしょうか?

katzumikatzumi

ですですー。
カセットという用語は良さそうですね。
VCRを連想させますね。
VCRのカセットと同様の挙動でいいのか?シナリオならではの+αがあるのか?は議論はほしそうですねー
カセットがAPIとの通信のキャプチャ&リプレイさせる為だけの情報にするか?
カセットの情報にシナリオの可変値を識別して埋め込んでおくか?

varsを可変値として柔軟なMockサーバーになりそう(実装は大変そうですが 😅

Ken’ichiro OyamaKen’ichiro Oyama

個人的にはrunnとモックサーバとの間に共通のフォーマット(カセット的なもの)を作る(もしくは既存のものがあればそれに乗っかる)のが良いかなあと思います。

その共通フォーマットにプログラマブルな要素を含めるのか、それとも、そのカセットをいい感じにいじる機能をモックサーバ側に作る かなあと思っています。

Ken’ichiro OyamaKen’ichiro Oyama

なので

カセットの情報にシナリオの可変値を識別して埋め込んでおくか?

上記ではない感じです。runnのランブックとは切り離したフォーマットで良いかなと(当然近いフォーマットでもいいわけですが)。

まあ実現できそうなのはHTTPランナーとgRPCランナーのシナリオだけですね。

Ken’ichiro OyamaKen’ichiro Oyama

複数のランブックの依存グラフの出力

Includeランナーや needs: セクションの導入の結果、ランブックの依存関係の把握はシナリオの整理の上で必要になってきたかもしれない。

依存グラフを何かしらの形で出力できれば良さそう。

(なお、今回は go-graphviz の導入には慎重です。Cgoが必要になってしまうため)

katzumikatzumi

今ならGithub ReadyなMermaid形式がいいんですかね?
tblsでERD出す時に使っていませんでしたっけ?

Ken’ichiro OyamaKen’ichiro Oyama

Mermaidもいいと思いますー DOTまでならテキスト出力ですし PlantUML も依存が書けるならありかもです。

あとは「依存グラフ」の先にある機能を想像しながらのコマンドの設計ですかねえ。

コマンド名も悩む

Ken’ichiro OyamaKen’ichiro Oyama

ランブックから依存の制御をしたい

具体的には

  • includeされることを禁止
  • needsされることを禁止

を設定したい。

大きな括りで見ると「シナリオの意図」の設定なんだろうなあ、と思います。

katzumikatzumi

こちら想定しているユースケースとしてはどんな感じでしょうか?

  • 2回実行されることを防ぎたい?
  • セキュリティ的な観点?

具体的なイメージがあるとインターフェースも決めやすいかと考えています。

Ken’ichiro OyamaKen’ichiro Oyama

依存グラフをシンプルにするためですね。
そのランブックが意図せずinclude/needsされてしまうのを防ぐイメージです。

katzumikatzumi

runn.Capturer インターフェースのあるべき姿を考える

katzumikatzumi

runn.Capturerインターフェースの現状整理

各種runnerが実行時の状態をキャプチャーする為のインターフェースが定義されている

https://github.com/k1LoW/runn/blob/main/capturer.go#L10-L49

このインターフェースを通して

これらのキャプチャーする機能を実装して以下の機能を実現している

  • ステップ毎の実行結果やエラーのキャプチャしてstoreへの格納や、verbose出力
  • 各種runnerの実行結果をキャプチャーしてデバック出力

現状のインターフェースの課題

  • ログやデバック観点でキャプチャしたいイベントと、最終結果を格納したいタイミングが異なる
    ※今回はデバック出力文脈でイベントを増やそうとしている
    デバック文脈では、最終結果がFix。。ステップが完了する前の状態を扱ったりする
  • runnerが増えて新しいキャプチャ内容が増えると、拡張ポイントが増えすぎる?
    確定結果を扱うインターフェースと、途中結果を扱うインターフェースが混ざっているかもしれない。(今回のexecの標準出力は1行毎にインベント通知したさがあるが、現状では全ての出力結果を一つの文字列として扱っている)
  • インターフェース定義されている関数によっては、例えばstoreの格納文脈等では実装しづらいものがある
Ken’ichiro OyamaKen’ichiro Oyama

CaptureExec* だけストリーム(io.Writer)が欲しくなっていませんか?(私はそうです)

Ken’ichiro OyamaKen’ichiro Oyama

いただいだ「現状のインターフェースの課題」をみると、もう少し広く考える必要がありそうな気もしますね。。。

katzumikatzumi

CaptureExec* だけストリーム(io.Writer)が欲しくなっていませんか?(私はそうです)

CaptureSSH* もですねー
runner側はStdoutPipeとかでストリーム処理する感じになってしますしね。
ストレームベースにするか?テキストベースで1行づつ処理する感じにするか?どうかはまだ悩んでいます。
 storeにstdoutやstderrを格納する際に、既存のCaptureExecStdoutとCaptureExecStderrを残すのか?
それとも既存のインターフェースを廃止して新しくストリーム処理させる function  側でstoreに格納させるのが良いか?が関わってくると考えています。
それによってテキストベースで処理させるインターフェースの方が望ましいのか?が決まってくると考えています。

Ken’ichiro OyamaKen’ichiro Oyama

storeにstdoutやstderrを格納する際に、既存のCaptureExecStdoutとCaptureExecStderrを残すのか?

なるほど...うおおどうすればいいんだ....

katzumikatzumi

storeにstdoutやstderrを格納する際に、既存のCaptureExecStdoutとCaptureExecStderrを残すのか?

一旦、残した状態で実装を進めてみました。

もう一点課題が出てきました。

標準出力と標準エラー出力のハンドリングを独立して非同期化するか?という課題が発生しました。
出力内容が混ざってしまう(それに伴うテストがFlaky化する)問題が出てきました。
これはdebug時を考慮して標準出力→標準エラー出力で逐次処理の方が、良さそうでしょうか?

Ken’ichiro OyamaKen’ichiro Oyama

正常な出力と考えると混ざるべきだと思っています。

うーんまだ行ごとに出力するようなメソッドを作成する方針に振れない自分がいます。

katzumikatzumi

なるほどー
混ぜるとテストしづらさが出てしまうので悩ましいですね

うーんまだ行ごとに出力するようなメソッドを作成する方針に振れない自分がいます。

インターフェースを変更せずにexec runnerにTeeオプションを追加して
オプションが有効になっている場合にのみTeeReaderを挟む様にして標準出力するようにするにはどうでしょうか?

https://github.com/k1LoW/runn/pull/1056/files#diff-5dbc24f000f25dbb39d02206d7efaac855fe1766eb4970b721f6ac18efdb1cc7R110

debugとの責務とは独立したオプションというイメージです。

Ken’ichiro OyamaKen’ichiro Oyama

exec runnerのみを拡張するというのはミニマムで良いかもですね(オプション名、 tee 以外にいい感じのないですかねー)。これ賛成です。

とはいえ、debugよりもexec runnerのオプションのほうが情報量が多いのはアレですね。

なんとかしたい...ウルトラCがあればいいんですけど。

混ぜるとテストしづらさ

「STDINとSTDOUTの情報量全てが出力されているか」とかで良さそうに思いました。本来コマンドの出力をstreamにしている状態ではSTDINとSTDOUTが混ざっていますし。

今回実現したい機能の特性を考えるとそのほうが自然に思いました。

Ken’ichiro OyamaKen’ichiro Oyama

tee(?)オプションはあくまで「ストリームで出力を見たい」だけという立ち位置で良さそうです。

katzumikatzumi

オプション名、 tee 以外にいい感じのないですかねー

liveoutput ではどうでしょうか?

tee(?)オプションはあくまで「ストリームで出力を見たい」だけという立ち位置で良さそうです。

大分シンプルになりました:+1:

Ken’ichiro OyamaKen’ichiro Oyama

最高です!!!!!!!!!!!!!名前もめちゃいい!!!!!!個人的に気に入りました。

1点だけ、
英語+YAML的には liveOutput になると思うのですがどうしましょう?