[インデックス 12587] ファイルの概要
このコミットは、Go言語の公式ドキュメントツールである godoc コマンドから、ソースコードリポジトリの同期機能を削除するものです。具体的には、godoc が定期的に外部コマンドを実行してソースコードを更新する機能が廃止されました。これにより、godoc のコードベースが簡素化され、特定の同期メカニズムへの依存がなくなりました。
コミット
commit d46438c3dadc5ce4903a2a2244b5bde84e4357cc
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date: Mon Mar 12 15:57:38 2012 -0700
cmd/godoc: remove sync code
Fixes #3273
R=gri
CC=golang-dev
https://golang.org/cl/5795065
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/d46438c3dadc5ce4903a2a2244b5bde84e4357cc
元コミット内容
cmd/godoc: remove sync code
Fixes #3273
このコミットは、godoc コマンドから同期コードを削除します。これはIssue #3273を修正するものです。
変更の背景
この変更の背景には、godoc の設計思想と運用上の課題がありました。
godoc は、Go言語のソースコードからドキュメントを生成し、Webサーバーとして提供するツールです。初期の godoc には、-sync および -sync_minutes というフラグが存在し、これらを設定することで、指定されたコマンド(通常は git pull や hg pull など)を定期的に実行し、godoc が参照するソースコードリポジトリを自動的に最新の状態に保つ機能がありました。
しかし、この自動同期機能はいくつかの問題を引き起こしていました。
- 複雑性の増加:
godoc自体が外部コマンドの実行、その結果の解析、エラーハンドリング、指数関数的なバックオフといった同期ロジックを持つことで、コードベースが不必要に複雑になっていました。 - セキュリティリスク: 任意のコマンドを実行できる機能は、設定ミスや悪意のある利用によってセキュリティ上の脆弱性につながる可能性がありました。
- 運用上の問題: 同期コマンドの失敗や、同期中に
godocが参照するファイルシステムの状態が一時的に不整合になる可能性がありました。また、同期処理がgodocプロセスのパフォーマンスに影響を与えることも考えられます。 - 役割の分離:
godocの主要な役割はドキュメントの提供であり、ソースコードの同期は別のツールや運用プロセス(例:cronジョブ、CI/CDパイプライン)に任せるべきであるという考え方が強まりました。
特に、コミットメッセージにある Fixes #3273 は、この同期機能に関連する具体的な問題を示唆しています。Issue #3273は「cmd/godoc: sync command should not be run as root」というタイトルで、godoc がroot権限で実行されている場合に、同期コマンドもroot権限で実行されてしまうというセキュリティ上の懸念が指摘されていました。この問題は、同期機能を完全に削除することで根本的に解決されます。
これらの理由から、godoc のコア機能に集中し、よりシンプルで堅牢な設計にするために、同期機能が削除されることになりました。
前提知識の解説
godoc: Go言語のソースコードからドキュメントを抽出し、Webブラウザで閲覧可能な形式で提供するツール。Goの標準ライブラリやサードパーティパッケージのドキュメントをローカルで参照する際に利用されます。- Go言語のコマンドラインツール (
cmd/): Go言語のプロジェクトでは、cmd/ディレクトリ以下に実行可能なコマンドラインツールが配置されるのが一般的です。cmd/godocはgodocコマンドの実装を指します。 flagパッケージ: Go言語の標準ライブラリで、コマンドライン引数を解析するために使用されます。-syncや-sync_minutesといったフラグは、このパッケージによって定義されていました。os.Pipe()/os.StartProcess()/p.Wait(): Go言語で外部プロセスを実行し、その標準入出力と通信するための関数群です。同期機能では、これらの関数を使って外部の同期コマンド(例:git pull)を実行していました。http.HandlerFunc/http.DefaultServeMux: Go言語の標準ライブラリnet/httpパッケージで、HTTPサーバーを構築するための基本的な要素です。godocはWebサーバーとして動作するため、これらの機能を利用していました。- Issue Tracker (GitHub Issues): ソフトウェア開発プロジェクトで、バグ報告、機能要望、タスク管理などを行うためのシステム。
Fixes #3273は、このコミットがGitHubのIssue #3273を解決したことを意味します。
技術的詳細
このコミットは、godoc の自動同期機能に関連するコードを完全に削除することで、以下の技術的な変更を加えています。
- コマンドラインフラグの削除:
src/cmd/godoc/doc.goから、-syncと-sync_minutesというコマンドラインフラグに関する説明が削除されました。これらのフラグは、同期コマンドとその実行間隔を設定するために使用されていました。
- 同期ロジックの削除:
src/cmd/godoc/main.goから、exec関数とdosync関数が完全に削除されました。exec関数は、指定された外部コマンドを新しいプロセスとして実行し、その出力をキャプチャする役割を担っていました。dosync関数は、exec関数を呼び出して同期コマンドを実行し、その終了ステータスに基づいてgodocのインデックスを更新したり、同期間隔を調整(成功時は通常に戻し、失敗時は指数関数的にバックオフ)したりする主要な同期ロジックを含んでいました。
main.goのmain関数内から、dosync関数をHTTPハンドラとして登録する部分 (http.Handle("/debug/sync", http.HandlerFunc(dosync))) と、定期的に同期を実行するためのゴルーチン (go func() { ... }) の起動ロジックが削除されました。
- 関連する変数の削除:
src/cmd/godoc/main.goから、同期機能に関連するグローバル変数(syncCmd,syncMin,syncDelay)が削除されました。src/cmd/godoc/godoc.goのfsTree変数に関するコメントが「updated with each sync」から「updated with each sync (but sync code is removed now)」に変更され、同期機能が削除されたことが明示されています。
- インポートの削除:
src/cmd/godoc/main.goから、同期機能で必要とされていたtimeパッケージのインポートが削除されました。
これらの変更により、godoc は外部コマンドによる自動同期の責任から解放され、起動時に一度インデックスを作成するシンプルな動作に戻りました。ソースコードの更新は、godoc の外部で別途管理されることが前提となります。
コアとなるコードの変更箇所
このコミットにおける主要な変更は、以下の3つのファイルに集中しています。
-
src/cmd/godoc/doc.go:-syncと-sync_minutesフラグに関する説明が削除されました。- インデックスの更新に関する説明から、同期コマンドの終了ステータスに基づくインデックス更新ロジック(成功時の更新、失敗時のバックオフなど)が削除され、「The index is created at startup.」というシンプルな記述に変更されました。
--- a/src/cmd/godoc/doc.go +++ b/src/cmd/godoc/doc.go @@ -77,12 +77,6 @@ The flags are: HTTP service address (e.g., '127.0.0.1:6060' or just ':6060') -server=addr webserver address for command line searches - -sync="command" - if this and -sync_minutes are set, run the argument as a - command every sync_minutes; it is intended to update the - repository holding the source files. - -sync_minutes=0 - sync interval in minutes; sync is disabled if <= 0 -templates="" directory containing alternate template files; if set, the directory may provide alternative template files @@ -110,15 +104,7 @@ as follows: /public/x -> public/x When godoc runs as a web server and -index is set, a search index is maintained. -The index is created at startup and is automatically updated every time the --sync command terminates with exit status 0, indicating that files have changed. - -If the sync exit status is 1, godoc assumes that it succeeded without errors -but that no files changed; the index is not updated in this case. - -In all other cases, sync is assumed to have failed and godoc backs off running -sync exponentially (up to 1 day). As soon as sync succeeds again (exit status 0 -or 1), the normal sync rhythm is re-established. +The index is created at startup. The index contains both identifier and full text search information (searchable via regular expressions). The maximum number of full text search results shown -
src/cmd/godoc/godoc.go:fsTree変数のコメントが更新され、同期コードが削除されたことが明記されました。
--- a/src/cmd/godoc/godoc.go +++ b/src/cmd/godoc/godoc.go @@ -72,7 +72,7 @@ var ( indexThrottle = flag.Float64("index_throttle", 0.75, "index throttle value; 0.0 = no time allocated, 1.0 = full throttle") // file system information - fsTree RWValue // *Directory tree of packages, updated with each sync + fsTree RWValue // *Directory tree of packages, updated with each sync (but sync code is removed now) fsModified RWValue // timestamp of last call to invalidateIndex docMetadata RWValue // mapping from paths to *Metadata -
src/cmd/godoc/main.go:timeパッケージのインポートが削除されました。syncCmd,syncMin,syncDelayといった同期関連のフラグ定義が削除されました。exec関数とdosync関数が完全に削除されました。これらは外部コマンドの実行と同期ロジックをカプセル化していました。main関数内から、/debug/syncエンドポイントの登録と、定期的な同期を実行するゴルーチンの起動ロジックが削除されました。
--- a/src/cmd/godoc/main.go +++ b/src/cmd/godoc/main.go @@ -45,7 +45,6 @@ import ( "regexp" "runtime" "strings" - "time" ) const defaultAddr = ":6060" // default webserver address @@ -58,11 +57,6 @@ var ( // file-based index writeIndex = flag.Bool("write_index", false, "write index to a file; the file name must be specified with -index_files") - // periodic sync - syncCmd = flag.String("sync", "", "sync command; disabled if empty") - syncMin = flag.Int("sync_minutes", 0, "sync interval in minutes; disabled if <= 0") - syncDelay delayTime // actual sync interval in minutes; usually syncDelay == syncMin, but syncDelay may back off exponentially - // network httpAddr = flag.String("http", "", "HTTP service address (e.g., '"+defaultAddr+"')") serverAddr = flag.String("String("server", "", "webserver address for command line searches") @@ -82,75 +76,6 @@ func serveError(w http.ResponseWriter, r *http.Request, relpath string, err erro servePage(w, "File "+relpath, "", "", contents) } -func exec(rw http.ResponseWriter, args []string) (status int) { - r, w, err := os.Pipe() - if err != nil { - log.Printf("os.Pipe(): %v", err) - return 2 - } - - bin := args[0] - fds := []*os.File{nil, w, w} - if *verbose { - log.Printf("executing %v", args) - } - p, err := os.StartProcess(bin, args, &os.ProcAttr{Files: fds, Dir: *goroot}) - defer r.Close() - w.Close() - if err != nil { - log.Printf("os.StartProcess(%q): %v", bin, err) - return 2 - } - - var buf bytes.Buffer - io.Copy(&buf, r) - wait, err := p.Wait() - if err != nil { - os.Stderr.Write(buf.Bytes()) - log.Printf("os.Wait(%d, 0): %v", p.Pid, err) - return 2 - } - if !wait.Success() { - os.Stderr.Write(buf.Bytes()) - log.Printf("executing %v failed", args) - status = 1 // See comment in default case in dosync. - return - } - - if *verbose { - os.Stderr.Write(buf.Bytes()) - } - if rw != nil { - rw.Header().Set("Content-Type", "text/plain; charset=utf-8") - rw.Write(buf.Bytes()) - } - - return -} - -func dosync(w http.ResponseWriter, r *http.Request) { - args := []string{"/bin/sh", "-c", *syncCmd} - switch exec(w, args) { - case 0: - // sync succeeded and some files have changed; - // update package tree. - // TODO(gri): The directory tree may be temporarily out-of-sync. - // Consider keeping separate time stamps so the web- - // page can indicate this discrepancy. - initFSTree() - fallthrough - case 1: - // sync failed because no files changed; - // don't change the package tree - syncDelay.set(time.Duration(*syncMin) * time.Minute) // revert to regular sync schedule - default: - // TODO(r): this cannot happen now, since Wait has a boolean exit condition, - // not an integer. - // sync failed because of an error - back off exponentially, but try at least once a day - syncDelay.backoff(24 * time.Hour) - } -} - func usage() { fmt.Fprintf(os.Stderr, "usage: godoc package [name ...]\n"+ @@ -348,30 +273,11 @@ func main() { } registerPublicHandlers(http.DefaultServeMux) - if *syncCmd != "" { - http.Handle("/debug/sync", http.HandlerFunc(dosync)) - } // Initialize default directory tree with corresponding timestamp. // (Do it in a goroutine so that launch is quick.) go initFSTree() - // Start sync goroutine, if enabled. - if *syncCmd != "" && *syncMin > 0 { - syncDelay.set(*syncMin) // initial sync delay - go func() { - for { - dosync(nil, nil) - delay, _ := syncDelay.get() - dt := delay.(time.Duration) - if *verbose { - log.Printf("next sync in %s", dt) - } - time.Sleep(dt) - } - }() - } - // Immediately update metadata. updateMetadata() // Periodically refresh metadata.
コアとなるコードの解説
このコミットの核心は、godoc が外部のソースコードリポジトリを自動的に同期する機能を完全に排除した点にあります。
-
src/cmd/godoc/main.goからのexecおよびdosync関数の削除:exec関数は、os.StartProcessを用いてシェルコマンドを実行し、その標準出力をキャプチャする汎用的なヘルパー関数でした。これはdosync関数から呼び出され、実際の同期コマンド(例:git pull)を実行するために使われていました。dosync関数は、同期処理のメインロジックを担っていました。この関数はexecを呼び出して同期コマンドを実行し、その結果(終了ステータス)に応じてgodocの内部状態(パッケージツリーの更新)を調整していました。また、同期が失敗した場合には、指数関数的にバックオフして再試行間隔を延ばすロジックも含まれていました。- これらの関数が削除されたことで、
godocはもはや外部コマンドを実行して自身が参照するソースコードを更新する能力を持たなくなりました。
-
main関数からの同期ゴルーチンとHTTPハンドラの削除:- 以前は、
main関数内で-syncと-sync_minutesフラグが設定されている場合、dosync関数を定期的に実行するゴルーチンが起動されていました。これにより、godocはバックグラウンドで自動的にソースコードを同期し続けていました。 - また、
/debug/syncというHTTPエンドポイントが提供されており、これにアクセスすることで手動で同期をトリガーすることが可能でした。 - これらの起動ロジックとハンドラが削除されたことで、
godocは起動時に一度インデックスを作成するのみとなり、その後のソースコードの変更はgodocの再起動、または外部からのファイルシステム更新によって反映されることになります。
- 以前は、
-
src/cmd/godoc/doc.goおよびsrc/cmd/godoc/godoc.goの変更:doc.goから同期関連のフラグ説明と、インデックス更新ロジックの詳細な説明が削除されたことで、ユーザーインターフェースとドキュメントから同期機能の存在が完全に消えました。godoc.goのコメント修正は、コードベースの現状を正確に反映させるためのものです。
これらの変更は、godoc の役割を「ドキュメントの提供」に特化させ、ソースコードの管理・同期は godoc の外部で行うという明確な分離を示しています。これにより、godoc 自体のコードベースはよりシンプルで保守しやすくなり、セキュリティ上の懸念も解消されました。
関連リンク
- Go Issue #3273: https://github.com/golang/go/issues/3273
- このコミットが修正した具体的なIssue。
godocの同期コマンドがroot権限で実行される問題について議論されています。
- このコミットが修正した具体的なIssue。
- Gerrit Change-Id 5795065: https://golang.org/cl/5795065
- このコミットの元のGerritレビューページ。当時の議論や変更の経緯をより詳細に確認できます。
参考にした情報源リンク
- Go言語の公式ドキュメント (
godocの現在の動作): https://pkg.go.dev/cmd/godoc - Go言語のIssueトラッカー (GitHub): https://github.com/golang/go/issues
- Go言語のGerritコードレビューシステム: https://go.googlesource.com/go/+/refs/heads/master
osパッケージのドキュメント: https://pkg.go.dev/osnet/httpパッケージのドキュメント: https://pkg.go.dev/net/httpflagパッケージのドキュメント: https://pkg.go.dev/flag- Go言語の歴史と設計思想に関する一般的な情報源。