JSON レポート
wacz-validator-validate は JSON オブジェクトを 1 つ標準出力に書きます。これが唯一の
出力形式で、切り替えるフラグはありません(同じ report を対話的に読むなら、別
バイナリの wacz-validator — @wacz-validator/tui — を使います)。CI スクリプトや wacz-validator の
下流にとってのインターフェースなので、形式は偶然ではなく意図的に決めてあります。
wacz-validator-validate samples/wikipedia.wacz | jq '.summary'トップレベル
Section titled “トップレベル”export interface Report { /** * この report を作った build の版。CLI と daemon は、build したときに git のタグから * 決めた版を渡す —— タグの上なら `0.31.0`、その先なら `0.31.0+3.gabcdef1`、未コミットの * 変更があれば `dirty` が付き、タグが見えなければ `unknown`。 */ validatorVersion: string; /** report を評価した profile。{@link ReportProfile} を参照。 */ profile: ReportProfile; /** validate された WACZ の identity。{@link ReportSource} を参照。 */ source: ReportSource; summary: ReportSummary; issues: Issue[]; /** WACZ 内のファイル一覧 + 検証の紐付け。{@link ReportEntry} を参照。 */ entries: ReportEntry[]; /** best-effort な metadata — {@link ReportStats} を参照。 */ stats?: ReportStats; /** * producer のバージョンが合わず走らせなかった rule。**該当が無ければ key ごと * 出ない** — {@link SkippedRule} を参照。 */ skipped?: readonly SkippedRule[];}「この archive は使えるか」は summary.failed === 0 です。派生値を別フィールド
として持たせていた時期がありますが、summary とずれる余地を作るだけなので
やめました。
wacz-validator-validate a.wacz | jq '.summary.failed == 0'issues は rule の登録順で、構造的な check が先に来るため、最も可能性の高い
producer バグが上の方に上がります。
source — どこの archive か
Section titled “source — どこの archive か”3 つの形があります。
kind |
欄 | 例 |
|---|---|---|
file |
path |
/var/archives/a.wacz(絶対パス) |
s3 |
uri |
s3://bucket/key.wacz |
http |
url |
https://example.com/a.wacz |
http の url からは query を落としてあります。 署名付き URL
(?X-Amz-Signature=…)で検証したとき、その署名が報告に残ると、報告を見られる
相手が期限内は archive を読めてしまいます。署名は資格情報であって、
「どこの何か」ではありません。s3 の uri に接続設定(forcePathStyle)を
載せないのと同じ判断です。
Summary
Section titled “Summary”export interface ReportSummary { passed: number; failed: number; warnings: number; info: number; durationMs: number;}
/** * report を評価した profile。engine の `ProfileSelector` を wire 向けに写す。 * * **バージョンは文字列で出す。** engine 内部の `SemVer` は `{ major, minor, patch }` * の三つ組だが、あれは parse の中間結果であって、報告を読む人が欲しいのは * `"2.1.0"` のほう。同じ report にある {@link SkippedRule} の `range` * (`">=1.11.0"`) とも並びが揃う。 * * `version` は**操作者が名乗った producer のバージョン**で、archive と照合した * ものではない (`datapackage.json` の `software` は読んでいない)。 */export interface ReportProfile { name: RuleProfile; /** 未指定なら **key ごと出ない**。「バージョンを問わない」の意。 */ version?: string;}
/** * producer のバージョンが合わずに走らせなかった rule。 * * `Report.skipped` は該当が 1 件も無ければ **key ごと出力されない** * (`stats` と同じ条件付き spread)。だからバージョンを指定しない実行の JSON は * 従来と 1 バイトも変わらない。 */export interface SkippedRule { rule: string; reason: "profile-version"; /** rule が要求した範囲 (例 `">=1.11.0"`)。 */ range: string; // 名乗られたバージョンはここには書かない。全エントリで同じ値になる複製で、 // `Report.profile.version` から機械的に取れる。}durationMs は validation の実行時間で、プロセス起動時間は含みません。
各 issue はそれを上げた rule を名乗るので、prose を解析しなくても rule 単位で 絞り込み・差分が取れます。
rule— 安定した<area>/<short-name>識別子。Rules の 表と同じ文字列です。localise せず、version 間で書式を変えません。severity— profile による組み替え後の値。つまり「その実行が実際にどう判断したか」です。messageKey+params— メッセージは事前にレンダリングされた prose では ありません。core は i18n キーとランタイム値だけを持ち、renderer が locale カタログで解決します。TUI と将来の browser frontend が同じ report を別の言語で 提示できるのはこのためです。location— rule が言える場合の、archive 内の位置。details— rule 固有の付加情報(hash の差分、hex dump など)。意図的に型を 持たせておらず、renderer が rule ごとに整形します。docs— 出典となる仕様の該当箇所へのリンク。指摘から根拠の本文までたどれます。conformance— 仕様がどれだけ強く要求しているか(MUST/MUST NOT/SHOULD/SHOULD NOT/MAY)。rule が書くのではなく rule 名から解決するので、 rule 定義が唯一の情報源のままになります。
severity と conformance は別の問いに答えます
Section titled “severity と conformance は別の問いに答えます”severity は違反に対して wacz-validator がどうするか、conformance は仕様が何を
要求しているかです。片方で絞っても、もう片方の代わりにはなりません。
corpus の 30 標本での実測:
| conformance | error | warning | info |
|---|---|---|---|
MUST |
20 | 10 | 0 |
SHOULD |
1 | 8 | 0 |
MAY / MUST NOT |
0 | 4 | 1 |
太字の 2 つが、片方から他方を予想できないことを示しています。
datapackage/resources-complete は MUST ですが warning —— ZIP に未宣言の
ファイルがあっても replay は止まりません。datapackage/digest は逆向きで、
仕様は SHOULD に留めていますが、hash が合わないのはアーカイブが変更された
可能性があるため wacz-validator は error にします。
なお summary.failed は severity だけを数えます —— 適合レベルは見ません。
MUST に違反していても failed が 0 になりえます。仕様準拠の観点が要るなら、
issue から自分で読み取ってください。
# wacz-validator が「壊れている」と判断したものwacz-validator archive.wacz | jq '[.issues[] | select(.severity == "error")]'
# 仕様が要求しているものwacz-validator archive.wacz | jq '[.issues[] | select(.conformance == "MUST" or .conformance == "MUST NOT")]'
# 両者が食い違っている箇所 —— たいていここがいちばん情報量がありますwacz-validator archive.wacz | jq '[.issues[] | select((.conformance == "MUST" and .severity != "error") or (.conformance != "MUST" and .severity == "error"))]'これを行う CLI フラグはまだありません。report は 2 軸とも持っており、絞り込みは 呼び出し側に委ねています。
安定性の約束
Section titled “安定性の約束”validatorVersion は、consumer が schema の drift を推測ではなく検出できるように
するために存在します。値は、その build がどのリリースから来たかで、build したときに
git のタグから読みます。
validatorVersion |
その build は |
|---|---|
0.31.0 |
タグの commit そのもので、未コミットの変更も無い —— リリースそのもの |
0.31.0+3.gabcdef1 |
v0.31.0 より 3 commit 先(abcdef1) |
0.31.0+dirty、0.31.0+3.gabcdef1.dirty |
上と同じで、未コミットの変更がある |
unknown |
タグが見えない所で build した(浅い clone) |
リリースなのは素の X.Y.Z だけです。それ以外は SemVer の build metadata(+…)で
書きます。prerelease(-…)で書くと、そのリリースより前と読めてしまうためです。
0.31.0 より前の build の report は、何から build したかに関係なく 0.0.0 と書いています。
当時この値は package.json#version を映していて、この repo の package.json は動かない
(版はタグにしか無い)ためです。
同一 major version の中では:
- 既存フィールドの意味と型は変わりません。
rule識別子は安定です — key にすべきはここです。- optional なフィールドが増えることはあります。consumer は知らないフィールドを 拒否せず無視してください。
stats が best-effort かつ optional なのは設計です。統計を取れないほど壊れた
WARC があったとしても、それを報告する report 自体を止めてはいけないからです。
profile — どの条件で判定したか
Section titled “profile — どの条件で判定したか”profile は名前とバージョンを持つオブジェクトです。バージョンを添えなければ
version の key は出ません(「バージョンを問わない」の意)。
"profile": { "name": "browserhive", "version": "2.1.0" }"profile": { "name": "spec" }文字列ではなくオブジェクトにしているのは、name だけで絞り込めるようにする
ためです。"browserhive@2.1.0" のような 1 本の文字列だと、バージョンを添えた
瞬間に consumer 側の一致判定が外れます。
jq -r '.profile.name' # pin の有無に関わらず "browserhive"jq -r '.profile.version // "unpinned"' # バージョンだけが要るときversion は --profile で名乗られた値で、archive と照合したものではありません
(プロファイルを参照)。
skipped — 見なかったものを書く
Section titled “skipped — 見なかったものを書く”--profile に producer のバージョンを添えると(browserhive@1.10.0)、その
バージョンでは正しく判定できない rule が走りません。走らせなかった事実は skipped に書きます。
"skipped": [ { "rule": "warc/recording-complete", "reason": "profile-version", "range": ">=1.11.0" }]stats と同じく該当が無ければ key ごと出ません。だからバージョンを添えない
実行の JSON は従来と 1 バイトも変わりません。
省略ではなく明示にしているのは、issues が空であることの意味が 2 通りあるから
です —— 「見て、問題が無かった」と「そもそも見ていない」。consumer が前者だと
思い込むと、報告が嘘に近づきます。
exit code
Section titled “exit code”exit code は @wacz-validator/protocol の exitCodeFor が report から導出します。
CLI と TUI が共有しているので両者がずれることはありません。よくある用途なら
自分のスクリプトで summary.failed を見ても等価です。failure の種類を区別したい
ときに共有ヘルパを使ってください。