Go Wiki: ウォッチフレーク

ウォッチフレークは、build.golang.org ダッシュボード上の明白なテストフレークをトリアージするプログラムです。

明白なテストフレークとは、以下のいずれかに当てはまる失敗です。

  • 完全に失敗しているビルダー上ではない。
  • 除外されたビルダー上ではない。
  • 4つ以上のビルダーで失敗したコミットを実行していない。
  • そのビルダー上で4つ以上の失敗コミットの実行の一部ではない。

ウォッチフレークは、すべての明白なテストフレークをTest Flakes プロジェクトのイシューに投稿します。

Test Flakes プロジェクトの各イシューの説明は、そのイシューに関連する失敗のパターンで始まります。たとえば、#55260 の説明のマークダウンは次のように始まります。

```
#!watchflakes
post <- pkg == "cmd/go" && test == "" && `unexpected files left in tmpdir`
```

ウォッチフレークは、すべての明白なテストフレークをイシュー内のパターンと照合します。

  • フレークがイシュー内のパターンに一致する場合、そのイシューに投稿されます。
  • フレークが複数のイシュー内のパターンに一致する場合、最も番号の小さいイシューに投稿されます。
  • フレークがどのイシュー内のパターンにも一致しない場合、ウォッチフレークは、失敗したパッケージとテストケースに一致するパターンを持つ新しいイシューを作成します。

新しく作成されたイシューのパターンはしばしば広すぎることがあり、実際の失敗に対してより具体的にするために編集されるべきです。失敗を最も番号の小さい一致するイシューに送ることで、新しい失敗に対する広範なデフォルトパターンを作成しても、以前のイシューから失敗が「奪われる」こともなく、すでに個別に追跡されている同じテストの無関係な失敗で新しいイシューをスパムすることもありません。

ウォッチフレークは、新しく作成されたイシューを Test Flakes プロジェクトに配置し、「NeedsInvestigation」ラベルを追加します。これらのイシューは、最初はステータスがありません(ActiveでもDoneでもない)。ステータスのないイシューは、通常、失敗に関する顕著な情報を捉えるようにパターンを修正する人が検査する必要があります。チェックされたイシューは、Activeに移動できます。GitHubは、イシューが閉じられると自動的にActiveからDoneに移動します。

ウォッチフレークは、新しい失敗を照合する際に、どのステータスのイシューも考慮します。クローズされたイシューに新しい失敗が見つかった場合、その失敗を投稿してイシューを再度開きます。したがって、修正が適用されたときにイシューをクローズしても問題ありません。失敗が本当になくなったかどうかを数週間待つ必要はありません。新しい失敗が発生した場合、イシューは自動的に再度開かれます。

ウォッチフレークは、独自のステートを維持しません。すべてのステートは GitHub のイシューにあります。実行するたびに、過去 60 日間のビルドダッシュボードの失敗を考慮し、すべての明白なフレークが Test Flakes プロジェクトに記録されていることを確認します。イシューに一致する失敗がすでにそのイシューに投稿されている場合、ウォッチフレークは当然ながら再度投稿しません。また、特定の失敗を除外するようにパターンを更新するためにイシューが編集された場合、ウォッチフレークは古い投稿を削除しませんが、それらの失敗に対して別の適切なイシューを探します(新しいイシューを作成する可能性もあります)。

構文

各イシューのウォッチフレークスタンザは、イシュー説明の先頭に表示されなければなりません。コードブロック(``` で囲むか、インデントされているか)である必要があり、無関係なコードブロックがウォッチフレークによって誤解釈されるのを防ぐために、最初の行は #!watchflakes でなければなりません。

ブロックの残りの部分は、小さなウォッチフレークスクリプトです。行末までのコメントは # で導入されます。スクリプトはルールのシーケンスであり、各ルールは action <- pattern の形式です(パターンに一致するものをアクションに送信します)。

アクション

アクションは次のとおりです。

  • post は、スクリプトが表示されているイシューに失敗を投稿します。
  • skip は、失敗を無視し、破棄します。このアクションはめったに使用すべきではありません(たとえば、#55166 のようにポリシーを設定する場合など)。
  • default は、post の優先度の低いバージョンです。イシューに失敗に一致する post または skip がある場合、ウォッチフレークはそちらを実行します。ただし、他に一致するものがない場合、ウォッチフレークは default パターンの一致を考慮します。(そして、default の一致がない場合、ウォッチフレークは新しいイシューを作成します。)

レコード

パターンへの入力は、名前付きフィールドを持つレコードであり、各フィールドには文字列値があります。

  • pkg は、ビルドに失敗した、またはテストに失敗したパッケージの完全なインポートパスです。

  • test は、失敗したパッケージ内のテスト関数の名前です。

  • mode は、ビルド失敗かテスト失敗かに応じて build または test です。

  • output は、失敗したテストからの出力です。この出力は、テストバイナリが終了するときに表示される最後の FAIL 行の直前で停止します。同じ実行で失敗した他のテストケースからの出力や、テストが開始される前に all.bash または buildlet によって表示されたコンテキストは含まれません。

  • log は、失敗したビルドログ全体です。

  • snippet は、イシュー自体に投稿される output の短縮形式です。一致はほとんど常に output を使用すべきです。

  • builder は、テストを実行したビルダーの名前です(dragonfly-amd64-622 など)。

  • repo は、テストされているリポジトリの名前です(go, net, tools, ...)。

  • goos は GOOS の値です(linux, windows, ...)。

  • goarch は GOARCH の値です(amd64, mips64le, ...)。

  • date は、テストされているコミットの日付で、2006-01-02T15:04:05 の形式です。日付比較ロジックはありません。代わりに文字列比較を使用してください。日付の比較はめったに使用すべきではありません。

  • section は、失敗が発生したビルドログのセクションです。all.bash の出力では、セクションは ##### で導入され、ブートストラップ中の Building 行もそれぞれ独自のセクションと見なされます。サブリポジトリでは、:: Running 行はそれぞれ、実行されている go コマンドにちなんだ名前のセクションを導入します(たとえば go test golang.org/x/tools/...)。

    ほとんどのパターンでは section を使用する必要はありません。これは、代替の実行環境でテストを再実行するメインリポジトリのテストで最も役立ちます。

パターン

パターンは、Go のような構文で記述されたブール式であり、||、&&、!、(、) を使用して複雑な式を構築できます。==、!=、<、<=、>、>= を使用してフィールドを文字列リテラルと比較できます。~ および !~ を使用して正規表現と照合できます。

すべての文字列比較は、左側にフィールド名、右側に二重引用符で囲まれた文字列リテラルを持つ必要があります(builder == "linux-amd64-alpine"goos == " のように)。

すべての正規表現一致は、左側にフィールド名、右側にバッククォートで囲まれた文字列リテラルを持つ必要があります(builder ~ `corellium` のように)。

バッククォートで囲まれた文字列リテラル自体は、output フィールドとの比較と見なされます。これは、パターンの正規表現の大部分に適切です。

これらすべてをまとめると、いくつかのスクリプトの例を以下に示します。

#!watchflakes
post <- pkg == "net/http" && test == "TestHandlerAbortRacesBodyRead"

#55277 のこのスクリプトは、http.TestHandlerAbortRacesBodyRead で失敗したビルド実行に応答して、ウォッチフレークによって自動的に作成されました。イシュー作成の原因となった特定の失敗はタイムアウトでした。そのテストで異なる根本原因の失敗がさらに見つかった場合、&& `panic: test timed out` を追加したり、パターンを修正したりすることが適切になるかもしれません。

#!watchflakes
post <- goos == "openbsd" && `unlinkat .*: operation not permitted`

#49751 のこのスクリプトは、os.Remove が unlinkat を呼び出すことによる予期せぬ EPERM エラーによって OpenBSD で発生する失敗を収集します。これらの失敗はさまざまなテストで問題を引き起こすため、pkgtest に条件はありません。

#!watchflakes
post <- pkg ~ `^cmd/go` && `appspot.com.*: 503`

#54608 のこのスクリプトは、cmd/go 自体だけでなく、cmd/go/... パッケージ階層内のすべてのテストで appspot.com からの 503 応答を伴うネットワーク問題を追跡します。

#!watchflakes
post <- goos == "windows" &&
        (`dnsquery: DNS server failure` || `getaddrinfow: This is usually a temporary error`)

#55165 のこのスクリプトは、Windows を実行しているビルダー上のあらゆるテストで特定の DNS 失敗に一致します。

#!watchflakes
post <- builder == "darwin-arm64-12" && pkg == "" && test == ""

#55312 のこのスクリプトは、特定のパッケージテストが実行される前に発生する darwin-arm64-12 ビルダーでの失敗を追跡するために、ウォッチフレークによって自動的に作成されました。

#!watchflakes
# note: sometimes the URL is printed with one /
default <- `(Get|read) "https://?(goproxy.io|proxy.golang.com.cn|goproxy.cn)`

#55163 のこのスクリプトは、特定の非標準 Go プロキシを使用するエラーに一致します。default を使用して、これらのプロキシによって引き起こされるより具体的な失敗の所有権を他のイシューが取得できるようにします。他のイシューに一致しない失敗は、新しいイシューを作成する代わりに #55163 に送られます。

#!watchflakes
default <- `: internal compiler error:`

#55257 のこのスクリプトは、テストされているパッケージやリポジトリに関係なく、あらゆるビルドでのコンパイラの失敗に一致します。前の例と同じ理由で default を使用しています。つまり、特定のコンパイラエラーに一致するイシューをまだ提出できるようにするためですが、他のイシューに一致しない失敗は、問題を引き起こした特定のテストに割り当てられた新しいイシューを作成する代わりに、#55257 にグループ化されます。


このコンテンツはGo Wikiの一部です。