[インデックス 12984] ファイルの概要
このコミットは、Go言語プロジェクトのコードレビュープロセスを支援するための新しいApp Engineアプリケーション「gocodereview」を導入します。このアプリケーションは、既存のコードレビューシステム(Rietveldベース)から変更リスト(CL: Change List)の情報を取得し、レビュー担当者への割り当て、CLのステータス表示、古いCLのガベージコレクションなどの機能を提供するダッシュボードとして機能します。
コミット
misc/dashboard/codereview: new app.
This is live at http://gocodereview.appspot.com/.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/6134043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/0175e3f1e0a1604f2e3c7bd8b67c42d066f36fa1
元コミット内容
commit 0175e3f1e0a1604f2e3c7bd8b67c42d066f36fa1
Author: David Symonds <dsymonds@golang.org>
Date: Fri Apr 27 16:36:02 2012 +1000
misc/dashboard/codereview: new app.
This is live at http://gocodereview.appspot.com/.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/6134043
変更の背景
Go言語プロジェクトでは、コードの品質と整合性を保つために厳格なコードレビュープロセスが採用されています。当時、Googleが開発したRietveldというWebベースのコードレビューツールが広く使われていました。しかし、Rietveldは汎用的なツールであり、Goプロジェクト特有のニーズ(例えば、特定のレビュー担当者への割り当てや、多数のCLの効率的な管理)に完全に最適化されているわけではありませんでした。
このコミットは、Goプロジェクトのコントリビューターやレビュー担当者が、より効率的にコードレビューの状況を把握し、管理できるようにするための専用ダッシュボードアプリケーションを導入することを目的としています。これにより、レビュープロセスの透明性が向上し、ボトルネックの特定や、レビュー担当者の負荷分散が容易になることが期待されます。特に、http://gocodereview.appspot.com/
でライブ稼働していることから、開発チームの生産性向上に直結するツールとして位置づけられていたことが伺えます。
前提知識の解説
このコミットを理解するためには、以下の技術的背景知識が役立ちます。
- Google App Engine (GAE): Googleが提供するPaaS(Platform as a Service)であり、Webアプリケーションやモバイルバックエンドを構築・ホストするためのプラットフォームです。Go言語を含む複数の言語をサポートしており、スケーラビリティ、信頼性、運用管理の容易さが特徴です。このアプリケーションはGAE上で動作するように設計されています。
app.yaml
: App Engineアプリケーションの設定ファイルで、ランタイム、バージョン、URLルーティング、サービス(メール、タスクキューなど)の有効化などを定義します。cron.yaml
: App EngineのCronサービス設定ファイルで、定期的に実行されるジョブ(例: ガベージコレクション)を定義します。index.yaml
: App Engine Datastoreのカスタムインデックスを定義するファイルです。複雑なクエリを実行する際に必要となります。queue.yaml
: App Engine Task Queueの設定ファイルで、非同期タスクの処理キューを定義します。
- Go言語: Googleが開発した静的型付けのコンパイル型言語です。並行処理に強く、シンプルで効率的なコード記述が可能です。このアプリケーションのバックエンドロジックはGoで記述されています。
- Rietveld: Googleが開発したオープンソースのコードレビューツールです。PerforceやGitなどのバージョン管理システムと連携し、変更の差分表示、コメント、承認などの機能を提供します。このGoコードレビューダッシュボードは、RietveldのAPIを利用してCL情報を取得しています。
- App Engine Datastore: Google App Engineが提供するNoSQLデータベースサービスです。スキーマレスで、高いスケーラビリティと可用性を提供します。このアプリケーションでは、CL(Change List)の情報を永続化するために使用されています。
- App Engine Task Queue: App Engineが提供する非同期タスク実行サービスです。時間のかかる処理や、外部サービスへのリクエストなどをバックグラウンドで実行するために使用されます。このアプリケーションでは、CL情報の更新を非同期で行うために利用されています。
- App Engine Mail API: App Engineアプリケーションがメールを受信するためのAPIです。このアプリケーションでは、コードレビューのメール通知を処理し、関連するCLを更新するために使用されています。
- App Engine URL Fetch API: App Engineアプリケーションが外部のHTTPリソースにアクセスするためのAPIです。このアプリケーションでは、RietveldのAPIからCL情報を取得するために使用されています。
技術的詳細
このコミットで導入されたGoコードレビューダッシュボードは、Google App Engineの様々なサービスを組み合わせて構築されています。
-
アプリケーション構造:
misc/dashboard/codereview/
: アプリケーションのルートディレクトリ。app.yaml
: App Engineのメイン設定ファイル。Goランタイムを使用し、メール受信、タスクキュー、管理画面へのアクセス制御、静的ファイルの配信、メインアプリケーションハンドラの設定を行います。cron.yaml
: 定期実行タスク(ガベージコレクション)を設定します。queue.yaml
: タスクキュー(update-cl
)を設定します。index.yaml
: Datastoreのクエリに必要なカスタムインデックスを定義します。dashboard/
: Go言語のアプリケーションロジックが含まれるパッケージ。cl.go
: CL(Change List)エンティティのデータモデル定義と、CLの更新、割り当てに関するハンドラロジック。front.go
: フロントページ(ダッシュボード)の表示ロジックと、CLのフィルタリング、表示に関する処理。gc.go
: 古いCLをDatastoreから削除するガベージコレクションロジック。mail.go
: 受信したメールを解析し、関連するCLの更新をトリガーするロジック。people.go
: Goプロジェクトのレビュー担当者(gophers)のリストと、メールアドレスから担当者IDへのマッピングを管理するロジック。
static/
: 静的ファイル(画像など)を格納。
-
データモデル (
CL
エンティティ):dashboard/cl.go
で定義されるCL
構造体は、コードレビューの変更リストを表します。type CL struct { Number string // e.g. "5903061" Closed bool Owner string // email address Created, Modified time.Time Description []byte `datastore:",noindex"` FirstLine string `datastore:",noindex"` LGTMs []string // These are person IDs (e.g. "rsc"); they may be empty Author string Reviewer string }
Number
: CLの識別番号。Closed
: CLがクローズされているかどうかのフラグ。Owner
: CLの作成者のメールアドレス。Created
,Modified
: 作成日時、最終更新日時。Description
,FirstLine
: CLの概要。datastore:",noindex"
タグにより、Datastoreのインデックス作成対象から除外され、ストレージコストを削減し、書き込みパフォーマンスを向上させます。LGTMs
: "Looks Good To Me" (承認) を与えたレビュー担当者のリスト。Author
,Reviewer
: CLの作成者とレビュー担当者のID(例: "rsc")。
-
主要な機能とワークフロー:
- CL情報の取得と更新:
handleUpdateCL
関数(cl.go
)が、RietveldのAPI (http://codereview.appspot.com/api/<CL_NUMBER>?messages=true
) からCLの詳細情報を取得します。- 取得したJSONデータ(説明、作成者、更新日時、クローズ状態、メッセージなど)を解析し、
CL
エンティティにマッピングします。 - 特に、メッセージからLGTM(承認)の有無や、レビュー担当者による「Submitted as」メッセージを検出してCLのクローズ状態を更新するロジックが含まれています。
- 更新された
CL
エンティティはDatastoreに保存されます。トランザクション内で既存のレビュー担当者情報を保持しつつ更新を行います。
- 非同期更新 (
UpdateCLLater
):UpdateCLLater
関数(cl.go
)は、指定されたCLの更新タスクをApp Engine Task Queueに追加します。これにより、CLの更新処理が非同期で行われ、ユーザーリクエストの応答時間を短縮します。- メール受信時や、手動での更新トリガー時に利用されます。
- メール受信処理 (
handleMail
):mail.go
のhandleMail
関数は、App Engine Mail APIを通じて受信したメールを処理します。- メールの件名からCL番号を正規表現で抽出し、そのCLの更新を
UpdateCLLater
を介して非同期でトリガーします。これにより、コードレビューのコメントやステータス変更がメールで通知された際に、ダッシュボードのCL情報が自動的に更新されるようになります。
- CLの割り当て (
handleAssign
):cl.go
のhandleAssign
関数は、フロントエンドからのPOSTリクエストを受け取り、特定のCLにレビュー担当者を割り当てます。- Datastoreトランザクション内でCLエンティティを取得し、
Reviewer
フィールドを更新して保存します。
- フロントページ表示 (
handleFront
):front.go
のhandleFront
関数は、ダッシュボードのメインページをレンダリングします。- DatastoreからアクティブなCL(レビュー担当者に割り当てられたCL、自身が作成したCL、その他のアクティブなCL)と、最近クローズされたCLを取得します。
- 複数のDatastoreクエリを並行して実行するために
sync.WaitGroup
を使用し、パフォーマンスを向上させています。 - 取得したデータはHTMLテンプレートに渡され、動的にページが生成されます。
- フロントエンドではjQueryを使用して、レビュー担当者の割り当てを非同期で行うJavaScriptロジックが含まれています。
- ガベージコレクション (
handleGC
):gc.go
のhandleGC
関数は、定期的に実行されるCronジョブとして設定されています。- Datastoreから、クローズ済みでかつ168時間(7日間)以上更新されていない古いCLを検索し、一括削除します。これにより、Datastoreのデータ量を適切に保ち、コストとパフォーマンスを最適化します。
- 人物情報管理 (
people.go
):people.go
は、Goプロジェクトの主要なレビュー担当者(gophers)のリストと、彼らのメールアドレス(@golang.org
と@google.com
ドメイン)から短いIDへのマッピングを初期化します。これは、CLの所有者やレビュー担当者の表示を簡潔にするために使用されます。
- CL情報の取得と更新:
このアプリケーションは、Go言語の並行処理機能(goroutineとchannel)をfront.go
でのデータ取得に活用し、App Engineのマネージドサービスを効果的に利用することで、スケーラブルでメンテナンスしやすいコードレビューダッシュボードを実現しています。
コアとなるコードの変更箇所
このコミットでは、以下のファイルが新規追加されています。
misc/dashboard/codereview/app.yaml
: App Engineアプリケーションのデプロイ設定。misc/dashboard/codereview/cron.yaml
: 定期実行ジョブ(ガベージコレクション)の設定。misc/dashboard/codereview/dashboard/cl.go
: CLエンティティの定義、CLの更新・割り当てロジック。misc/dashboard/codereview/dashboard/front.go
: ダッシュボードのフロントページ表示ロジックとHTMLテンプレート。misc/dashboard/codereview/dashboard/gc.go
: 古いCLを削除するガベージコレクションロジック。misc/dashboard/codereview/dashboard/mail.go
: 受信メール処理ロジック。misc/dashboard/codereview/dashboard/people.go
: レビュー担当者情報の管理ロジック。misc/dashboard/codereview/index.yaml
: Datastoreのカスタムインデックス定義。misc/dashboard/codereview/queue.yaml
: タスクキューの設定。misc/dashboard/codereview/static/gopherstamp.jpg
: 静的画像ファイル。misc/dashboard/codereview/static/icon.png
: 静的画像ファイル。
コアとなるコードの解説
misc/dashboard/codereview/dashboard/cl.go
このファイルは、コードレビューの変更リスト(CL)に関する主要なデータモデルと操作を定義しています。
CL
構造体: CLの番号、クローズ状態、所有者、作成・更新日時、説明、LGTM(承認)リスト、作成者、レビュー担当者などの情報を保持します。datastore:",noindex"
タグは、Description
とFirstLine
フィールドがDatastoreのインデックス作成対象から除外されることを示し、ストレージと書き込みの効率化を図っています。ShortOwner()
: CLの所有者のメールアドレスを、people.go
で定義された短い担当者IDに変換して表示するためのヘルパーメソッドです。FirstLineHTML()
/LGTMHTML()
: HTMLテンプレートで表示するために、CLの最初の行やLGTMsリストを適切にエスケープし、整形するためのヘルパーメソッドです。特に、パッケージ名を太字にするなどの表示調整が含まれます。ModifiedAgo()
: CLが最後に更新されてからの経過時間を「X分前」のような形式で表示するためのヘルパーメソッドです。handleAssign(w http.ResponseWriter, r *http.Request)
:/assign
エンドポイントのハンドラ。- POSTリクエストを受け取り、CL番号と割り当てるレビュー担当者IDをフォーム値から取得します。
- 現在のユーザーがレビュー担当者リストに含まれているかを確認し、権限がない場合は
StatusUnauthorized
を返します。 - Datastoreトランザクション内で、指定されたCLエンティティを取得し、
Reviewer
フィールドを更新してDatastoreに保存します。これにより、CLにレビュー担当者を割り当てることができます。
UpdateCLLater(c appengine.Context, n string, delay time.Duration)
:- 指定されたCL番号
n
の更新タスクを、指定されたdelay
後にupdate-cl
という名前のタスクキューに追加します。 - これは、CL情報の更新を非同期で行うためのメカニズムであり、ユーザーリクエストの応答性を保ちつつ、外部APIからのデータ取得やDatastoreへの書き込みといった時間のかかる処理をバックグラウンドで実行するために使用されます。
- 指定されたCL番号
handleUpdateCL(w http.ResponseWriter, r *http.Request)
:/update-cl
エンドポイントのハンドラ。主にタスクキューから呼び出されます。- フォーム値からCL番号を取得し、
updateCL
関数を呼び出してCL情報を更新します。 - 更新が成功すれば"OK"を返します。
updateCL(c appengine.Context, n string) error
:- 単一のCLをRietveldのAPIから取得し、Datastoreに保存するコアロジック。
codereviewBase + "/api/" + n + "?messages=true"
というURLでRietveldのAPIを呼び出し、CLの詳細情報(メッセージを含む)をJSON形式で取得します。- 取得したJSONを
apiResp
構造体にデコードします。 apiResp
のデータからCL
エンティティを構築し、作成日時や更新日時をパースします。- CLの
Description
から最初の行を抽出し、FirstLine
として設定します。 - CLのメッセージをイテレートし、LGTM(承認)メッセージを検出して
LGTMs
リストに追加します。 - レビュー担当者からの「*** Submitted as 」というメッセージを検出した場合、CLがクローズされたと判断し、
Closed
フラグをtrue
に設定します。これは、RietveldのCLがすぐに「クローズ済み」にならない場合のシミュレーションです。 - Datastoreトランザクション内で、既存のCLエンティティを取得し、レビュー担当者情報(
Reviewer
)を保持したまま、新しいCL情報で上書き保存します。
misc/dashboard/codereview/dashboard/front.go
このファイルは、ダッシュボードのフロントページ表示に関するロジックを扱います。
handleFront(w http.ResponseWriter, r *http.Request)
:/
エンドポイントのハンドラ。frontPageData
構造体を初期化し、レビュー担当者リストと現在のユーザーがレビュー担当者であるかどうかのフラグを設定します。- Datastoreから以下のCLリストを並行して取得します(
sync.WaitGroup
を使用):- 現在のユーザーに割り当てられたCL(レビュー担当者の場合)
- 現在のユーザーが作成したCL
- その他のアクティブなCL(上記2つを除く)
- 最近クローズされたCL
- 取得したデータは
frontPage
テンプレートに渡され、HTMLが生成されてクライアントに返されます。
frontPageData
構造体: フロントページに表示するデータを保持します。clTable
の配列と、レビュー担当者リスト、現在のユーザーがレビュー担当者であるかどうかのフラグが含まれます。clTable
構造体: 各CLリスト(テーブル)のタイトル、割り当て可能かどうか、CLのリストを保持します。frontPage
テンプレート: HTMLテンプレートを定義します。- Go言語のテンプレート構文を使用し、
frontPageData
から動的にコンテンツを生成します。 - CLの所有者、レビュー担当者、タイトル、最終更新日時などを表示します。
- レビュー担当者の選択ドロップダウン(
select
要素)が含まれており、JavaScript(jQuery)を使用して、選択が変更された際に/assign
エンドポイントに非同期でPOSTリクエストを送信し、CLのレビュー担当者を更新するロジックが埋め込まれています。 - CLのステータス(保留中、失敗、保存済み)に応じて行の背景色を変更するCSSクラス(
pending
,failed
,saved
)が定義されています。
- Go言語のテンプレート構文を使用し、
misc/dashboard/codereview/dashboard/gc.go
このファイルは、古いCLのガベージコレクション(GC)ロジックを扱います。
handleGC(w http.ResponseWriter, r *http.Request)
:/gc
エンドポイントのハンドラ。cron.yaml
で設定されたCronジョブによって定期的に呼び出されます。- Datastoreから、クローズ済み(
Closed = true
)で、かつ168時間(7日間)以上更新されていない(Modified < cutoff
)CLを検索します。 - 最大100件のCLのキーのみを取得し(
KeysOnly()
)、datastore.DeleteMulti
を使用してそれらのCLを一括削除します。 - これにより、Datastoreに不要なデータが蓄積されるのを防ぎ、データベースのパフォーマンスとコストを最適化します。
misc/dashboard/codereview/dashboard/mail.go
このファイルは、App Engine Mail APIを通じて受信したメールを処理します。
handleMail(w http.ResponseWriter, r *http.Request)
:/_ah/mail/
パスへのリクエストを処理するハンドラ。App Engineが受信メールをこのエンドポイントにルーティングします。- 受信したメールの本文を
mail.ReadMessage
でパースします。 - メールの件名から正規表現
.*code review (\\d+):.*
を使用してCL番号を抽出します。 - CL番号が抽出できた場合、
UpdateCLLater
関数を呼び出し、10秒の遅延を伴ってそのCLの更新タスクをタスクキューに追加します。この遅延は、RietveldがCLの変更を反映するのに十分な時間を与えるためです。 - これにより、コードレビューのコメントやステータス変更がメールで通知された際に、ダッシュボードのCL情報が自動的に更新されるようになります。
misc/dashboard/codereview/dashboard/people.go
このファイルは、Goプロジェクトのレビュー担当者(gophers)の情報を管理します。
emailToPerson
マップ: メールアドレス(例:dsymonds@golang.org
,rsc@google.com
)から、短い担当者ID(例:dsymonds
,rsc
)へのマッピングを保持します。personList
スライス: 短い担当者IDのリストを保持します。init()
関数:- パッケージがロードされる際に自動的に実行されます。
gophers
という配列に、Goプロジェクトの主要なレビュー担当者の短いIDをハードコードしています。- これらのIDに対して、
@golang.org
と@google.com
の2つのドメインのメールアドレスをemailToPerson
マップに登録します。 personList
をソートし、フロントエンドでの表示順序を決定します。- このファイルは、アプリケーション全体で一貫した担当者IDの解決と表示を可能にします。
関連リンク
- Go Code Review Dashboard (ライブサイト): http://gocodereview.appspot.com/
- 元のGo Gerrit CL: https://golang.org/cl/6134043
- Rietveld (Google Code Review): https://code.google.com/p/rietveld/ (プロジェクトはアーカイブ済み)
参考にした情報源リンク
- Google App Engine Documentation (Go): (当時の公式ドキュメントを参照)
- Go Programming Language Documentation: https://go.dev/doc/
- App Engine Datastore: (当時の公式ドキュメントを参照)
- App Engine Task Queues: (当時の公式ドキュメントを参照)
- App Engine Mail API: (当時の公式ドキュメントを参照)
- App Engine URL Fetch: (当時の公式ドキュメントを参照)
- jQuery Documentation: https://api.jquery.com/
- HTML Template Package (Go): https://pkg.go.dev/html/template
net/mail
Package (Go): https://pkg.go.dev/net/mailregexp
Package (Go): https://pkg.go.dev/regexpencoding/json
Package (Go): https://pkg.go.dev/encoding/jsontime
Package (Go): https://pkg.go.dev/timenet/http
Package (Go): https://pkg.go.dev/net/httpnet/url
Package (Go): https://pkg.go.dev/net/urlsort
Package (Go): https://pkg.go.dev/sortstrings
Package (Go): https://pkg.go.dev/stringssync
Package (Go): https://pkg.go.dev/sync