cli の設計

設計目標

他のパッケージはすべて純粋であるか、副作用を計測ループに閉じ込めています。cli はファイル、標準ストリーム、プロセス実行、終了コードを扱う場所です。このアダプタを薄く保ち、唯一の危険な操作であるアーティファクトに記録されたコマンドの実行を明示的なものにします。

数学的背景

コマンドラインは、引数ベクタからリクエストへの小さな全域関数と、それに続く副作用を持つインタプリタです:

argv→ parse_args CliRequest→ main effects×{0,1,2}.\text{argv} \xrightarrow{\ \texttt{parse\_args}\ } \text{CliRequest} \xrightarrow{\ \texttt{main}\ } \text{effects} \times \{0, 1, 2\}.

parse_args は決して失敗しません。どのベクタもリクエストに対応付けられ、エラーはフィールドに記録されます。これにより、パーサはどのターゲットでもテスト可能になり、「何が求められたか」と「何が起きたか」が分離されます。終了コードは結果を成功(00)、実行時の失敗(11: IO、不正なデータ、リプレイの失敗)、使い方の誤り(22)に分けるため、スクリプトは壊れた入力と壊れた呼び出しを区別できます。

設計上の決定

純粋なヘルパーと native の副作用

問題。 プロセス実行と非同期の標準ストリームは native ターゲットにしか存在しません。選択。 解析、ヘルプテキスト、JSONL からのリプレイ情報の抽出、レポートの描画は cli.mbt の通常の関数です。native の main.mbt は async で、stdin、stdout、プロセスに moonbitlang/async を使います。その他のターゲットは main_unimplemented.mbt を使い、ファイルからファイルへの report をサポートし、replay を拒否します。理由。 同じパッケージがすべてのターゲットでビルドでき、ロジックは何も起動せずにテストできます。

保護されたリプレイ

問題。 リプレイのアーティファクトにはコマンドラインが含まれます。issue で見つけたファイルからそれを無条件に実行すれば、任意のコードが実行されてしまいます。選択。 replay はファイルを必須とし(stdin は不可)、--dry-run でコマンドを表示し、--yes があるときだけ実行し、記録されたタイムアウトを適用し、非ゼロの終了で失敗します。理由。 安全な既定は確認することであり、実行は明示的な第 2 段階です。

必要なものだけを読む

replay_spec_from_jsonl は最初の validation_failure で止まり、それより前の各行のバージョンを検査した後、3 つのリプレイ用フィールドだけを読みます。多数のイベントと 1 つの失敗を含むストリームでも動作し、未知のバージョンのアーティファクトは何かが実行される前に拒否されます。

ポータブルなレポート

report は入力全体を読み込み、ターゲットラベル native でドキュメントを構築し、1 つの HTML ファイルを書き出します。入力や出力に - を指定するとパイプにつながるため、他のツールと組み合わせられます。

正しさと不変条件

  • parse_args は全域関数であり、環境に触れません。
  • replay は --yes なしでは決して実行せず、native 以外のターゲットでも決して実行しません。
  • 使い方の誤りは 2 で、実行時の失敗は 1 で終了します。
  • イベントの解析に失敗した場合、report は何も書き出しません。

採用しなかった代替案

  • フラグライブラリ。 2 つのコマンドと 4 つのフラグでは依存関係を正当化できません。
  • 既定でリプレイを実行する。 他所から来たアーティファクトに対して安全ではありません。
  • CLI のロジックを report に埋め込む。 純粋なパッケージにファイル IO を持ち込むことになります。

境界

  • run コマンドはありません。ベンチマークは MoonBit のコードであり、自分のパッケージの moon test または moon run で実行します。
  • --open は macOS の open コマンドを使います。
  • replay はファイルだけを読み、最初の失敗だけを実行し、コマンドを(シェルを介さず)直接プロセス API に渡します。
  • 進捗行は、ms とラベル付けされたマイクロ秒の値を表示します。
  • バージョン文字列はソースに固定されています。