cli チュートリアル
このチュートリアルでは、cli 実行ファイルを使って MoonBit の外からスコアスナップショットを計算する方法を、シェル、インデックスビルダーと同じ Python、Node.js の順に示します。このリポジトリのチェックアウト、MoonBit ツールチェーン、Node.js 20.16、22.3 以降が必要です。
クイックスタート
リポジトリのルートで実行ファイルをビルドします。
moon build src/cli --target js
1 つのパッケージのシグナルをファイルに書きます。
cat > payload.json <<'JSON'
{"dependents": 20, "recent_dependents": 4, "downloads": 300,
"days_since_release": 40, "historical_dependents": 10,
"historical_recent_dependents": 2, "historical_downloads": 0,
"historical_days_since_release": 10}
JSON
node _build/js/debug/build/cli/cli.js score-snapshot --input payload.json
コマンドはスナップショットを 1 行で出力します。
{"score":301.7852882193072,"score_30d_ago":135.2764584196223,"score_growth_30d":166.5088297996849,"score_growth_ratio_30d":1.2308780976744749,"rank_label":"S","momentum_label":"Rising","activity_multiplier":1.06}
これは MoonBit での @score.compute_score_snapshot(20, 4, 300, 40, 10, 2, 0, 10) と同じ数値です。
よくある作業
わからない値は省く
どのキーも省略可能で、既定値は 0 です。5 日前にリリースされ、被依存数が 3 で、ほかは不明なパッケージの場合:
echo '{"dependents": 3, "days_since_release": 5}' > new.json
node _build/js/debug/build/cli/cli.js score-snapshot --input new.json
{"score":59.00068800926255,"score_30d_ago":0,"score_growth_30d":59.00068800926255,"score_growth_ratio_30d":1,"rank_label":"C","momentum_label":"Stable","activity_multiplier":1.12}
過去のシグナルがすべて 0 なので、score_30d_ago は 0 で、成長率は 1 と報告されます。
Python から呼び出す
インデックスビルダーはパッケージごとに 1 回このコマンドを実行します。そのヘルパーの最小版は次のとおりです。
import json, os, subprocess, tempfile
CLI = "_build/js/debug/build/cli/cli.js"
def score_snapshot(signals: dict) -> dict:
with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False) as f:
json.dump(signals, f)
path = f.name
try:
done = subprocess.run(
["node", CLI, "score-snapshot", "--input", path],
check=True, capture_output=True, text=True,
)
finally:
os.unlink(path)
return json.loads(done.stdout)
print(score_snapshot({"dependents": 20, "recent_dependents": 4,
"downloads": 300, "days_since_release": 40})["rank_label"])
S
check=True は 0 以外の終了ステータスをすべて例外にするので、JSON のエラーメッセージと入力ファイルの欠落の両方に対応できます。
Node.js から呼び出す
import { execFileSync } from "node:child_process";
import { writeFileSync } from "node:fs";
writeFileSync("p.json", JSON.stringify({ dependents: 936, days_since_release: 120 }));
const out = execFileSync("node", [
"_build/js/debug/build/cli/cli.js", "score-snapshot", "--input", "p.json",
]);
console.log(JSON.parse(out).rank_label);
S
乗数が なら、被依存数 936 だけでランク S に届きます。理由は score の設計 で導いています。
エラーを扱う
エラーでは error キーを持つ JSON オブジェクトを出力し、ステータス 1 で終了します。
echo 'not json' > bad.json
node _build/js/debug/build/cli/cli.js score-snapshot --input bad.json; echo "status $?"
{"error":"Failed to parse JSON input"}
status 1
出力を解析する前に終了ステータスを確認してください。ファイルがない場合も 1 で終了しますが、JSON ではなく Node.js のスタックトレースを標準エラーに出力します。
さらに進んで
- まとめて処理する。 呼び出しのたびに Node.js のプロセスが起動し、そのコストはスコアの計算よりはるかに大きくなります。1 つの MoonBit プログラムで多数のパッケージを扱うなら、score チュートリアル のように
@score.compute_score_snapshotを直接呼び出してください。 - シグナルの出どころ。 インデックスビルダーがこのコマンドを呼ぶ前に被依存数、最近の被依存数、過去の値をどう数えるかは アーキテクチャガイド で説明しています。
- 入力を厳密にする。 コマンドはいい加減な入力も受け付けます。文字列と
nullは0になり、小数は切り捨てられます。黙って0になることでバグが隠れるおそれがあるなら、呼び出し側でペイロードを検証してください。
よくある落とし穴
- 数値を文字列で書く。
{"dependents": "20"}は被依存数 0 として評価されます。"20"ではなく20と書いてください。 - 引数の順序の誤り。
score-snapshotはスクリプトの直後の引数でなければなりません。--input file.json score-snapshotでは使い方のエラーが出力されます。 - 古いビルド。 インデックスビルダーは既存の
cli.jsを再利用します。src/scoreを変更したらビルドし直してください。そうしないとデータベースは古い規則で評価されます。 - 大きすぎるカウント。
2147483647以上の数(変換はIntの最大値で飽和します)はスコアをNaNにします。JSON にはNaNがないので、出力では数値が入るべき場所に文字列"NaN"が入ります:{"score":"NaN",...}。