[インデックス 19486] ファイルの概要
このコミットは、Go言語のリリースプロセスで使用される misc/makerelease
ツールに対する変更です。具体的には、新しいダウンロードページへのアップロード報告メカニズムを導入し、リリースされたバイナリやソースアーカイブの情報を適切に新しいシステムに送信するように更新しています。これにより、Goの公式ダウンロードページがより正確かつ効率的に更新されるようになります。
コミット
commit 865904f6d8c1dd80b1a203c531d0eb03e7f403f7
Author: Andrew Gerrand <adg@golang.org>
Date: Mon Jun 2 12:46:03 2014 +1000
misc/makerelease: report uploads to the new downloads page
LGTM=bradfitz
R=golang-codereviews, bradfitz
CC=golang-codereviews
https://golang.org/cl/102040047
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/865904f6d8c1dd80b1a203c531d0eb03e7f403f7
元コミット内容
このコミットは、misc/makerelease
ツールが、Goの新しいダウンロードページにアップロード情報を報告するように変更するものです。以前は go.googlecode.com/files
に直接ファイルをアップロードしていましたが、新しいダウンロードページでは、アップロードされたファイルに関するメタデータ(ファイル名、バージョン、OS、アーキテクチャ、チェックサム、種類など)をHTTP POSTリクエストで特定のURL (golang.org/dl/upload
) に送信することで、ダウンロードページが動的に更新される仕組みに移行しました。
変更の背景
Go言語のリリースプロセスは、新しいバージョンのGoが公開されるたびに、そのバイナリやソースコードをユーザーがダウンロードできるようにするための重要な手順です。以前は、Google Codeのファイルホスティングサービス(go.googlecode.com/files
)を利用してリリースファイルを公開していました。しかし、このシステムは静的なファイルホスティングであり、ダウンロードページの情報を手動で更新する必要があるか、あるいは限定的な自動化しか提供していなかった可能性があります。
このコミットが行われた2014年頃は、Google Codeが徐々にその役割を終え、GitHubなどの他のプラットフォームへの移行が進んでいた時期と重なります。Goプロジェクトも、より柔軟で動的なダウンロードページを提供するために、インフラストラクチャの変更を計画していました。
この変更の主な背景は以下の通りです。
- ダウンロードページの刷新: ユーザーエクスペリエンスの向上と、より詳細な情報(チェックサム、ファイルの種類など)を提供できる新しいダウンロードページへの移行。
- 自動化の強化: リリースファイルのアップロードと同時に、ダウンロードページの情報も自動的に更新されるようにすることで、手動での作業を減らし、エラーのリスクを低減する。
- Google Codeからの移行: Google Codeのサービス終了を見据え、Goのリリースインフラをより持続可能なものにする必要があった。新しいシステムでは、Google Cloud Storageにファイルを保存し、そのメタデータを新しいダウンロードページに報告する形に変わりました。
これにより、Goのリリース管理がより堅牢で自動化されたものになり、ユーザーは常に最新かつ正確なダウンロード情報にアクセスできるようになりました。
前提知識の解説
このコミットを理解するためには、以下の技術的背景知識が役立ちます。
- Go言語のリリースプロセス: Go言語の新しいバージョンがリリースされる際の一連の作業。これには、ソースコードのビルド、各種プラットフォーム向けバイナリの生成、テスト、そして生成されたファイルの公開が含まれます。
misc/makerelease
はこのプロセスの一部を自動化するツールです。 misc/makerelease
ツール: Goプロジェクトのmisc
ディレクトリにある内部ツールで、Goの公式リリースをビルドし、公開するためのスクリプトやユーティリティが含まれています。このツールは、Goのソースからコンパイラやツールチェインをビルドし、様々なOS/アーキテクチャ向けのバイナリパッケージを作成し、それらをストレージにアップロードする役割を担っています。- Google Cloud Storage (GCS): Googleが提供するオブジェクトストレージサービス。大量のデータを保存し、世界中からアクセス可能にするためのスケーラブルなインフラを提供します。Goのリリースファイルは、このGCSバケットに保存されます。
- OAuth: Open Authorizationの略で、ユーザーが自分のアカウント情報を第三者のアプリケーションと安全に共有するための標準プロトコルです。
makerelease
ツールがGCSにファイルをアップロードする際に、認証のためにOAuthが使用されます。 go.googlecode.com/files
: 以前GoのリリースファイルがホストされていたGoogle Codeのファイルホスティングサービス。このコミットの変更前は、makerelease
が直接ここにファイルをアップロードしていました。golang.org/dl/upload
: このコミットで導入された新しいエンドポイント。makerelease
ツールが、アップロードされたファイルに関するメタデータをJSON形式でHTTP POSTリクエストとして送信する先のURLです。このエンドポイントが受け取った情報に基づいて、Goの公式ダウンロードページが動的に更新されます。- SHA1 チェックサム: Secure Hash Algorithm 1の略で、データの完全性を検証するために使用される暗号学的ハッシュ関数です。ファイルの内容から一意の短い文字列(ハッシュ値)を生成し、ダウンロードされたファイルが破損していないか、改ざんされていないかを確認するために利用されます。
- JSON (JavaScript Object Notation): 軽量なデータ交換フォーマット。人間にとっても読み書きしやすく、機械にとっても解析しやすい形式です。このコミットでは、アップロードされるファイルのメタデータをJSON形式で新しいダウンロードページに送信しています。
- HTTP POSTリクエスト: Webサーバーにデータを送信するためのHTTPメソッドの一つ。このコミットでは、JSON形式のメタデータを
golang.org/dl/upload
エンドポイントに送信するために使用されます。
これらの知識は、コミットの変更がGoのリリースインフラストラクチャ全体にどのように影響するかを理解する上で不可欠です。
技術的詳細
このコミットの主要な技術的変更点は、misc/makerelease/makerelease.go
ファイルにおけるアップロード処理のロジックの変更です。
-
新しいアップロードURLの導入:
- 以前はハードコードされていた
uploadURL = "https://go.googlecode.com/files"
が削除されました。 - 代わりに、
flag
パッケージを使用してuploadURL
という新しいコマンドラインフラグが追加され、デフォルト値としてhttp://golang.org/dl/upload
が設定されました。これにより、アップロード先のURLを柔軟に設定できるようになりました。 defaultUploadURL
定数がhttp://golang.org/dl/upload
として定義されました。
- 以前はハードコードされていた
-
ファイルの読み込みとSHA1チェックサムの計算:
Upload
関数内で、まずアップロード対象のファイルをioutil.ReadFile(filename)
でメモリに読み込みます。- 読み込んだファイルの内容に対して
sha1.Sum(file)
を実行し、SHA1チェックサムを計算します。このチェックサムは、ダウンロードされたファイルの整合性検証に利用されます。
-
ファイルメタデータ構造
File
の定義:- アップロード情報をJSON形式で送信するために、新しい構造体
File
が定義されました。 File
構造体には以下のフィールドが含まれます:Filename
(string): アップロードされるファイルの名前(例:go1.3.linux-amd64.tar.gz
)。OS
(string): 対象オペレーティングシステム(例:linux
)。Arch
(string): 対象アーキテクチャ(例:amd64
)。Version
(string): Goのバージョン(例:go1.3
)。Checksum
(string): 計算されたSHA1チェックサム。datastore:",noindex"
タグが付与されており、Datastoreに保存される際にこのフィールドがインデックスされないことを示唆しています(これは新しいダウンロードページがDatastoreを使用している可能性を示唆します)。Kind
(string): ファイルの種類("archive"
,"installer"
,"source"
)。
- アップロード情報をJSON形式で送信するために、新しい構造体
-
ファイル種類の自動判別:
Upload
関数内で、ファイルの拡張子やビルド設定に基づいてKind
フィールドが自動的に判別されます。b.Source
がtrue
の場合(ソースアーカイブの場合)は"source"
。- ファイル名が
.tar.gz
または.zip
で終わる場合は"archive"
。 - ファイル名が
.msi
または.pkg
で終わる場合は"installer"
。 - それ以外は
"unknown"
。
-
JSONデータの生成とHTTP POSTリクエスト:
File
構造体のインスタンスが作成され、必要なメタデータが設定されます。json.Marshal(File{...})
を使用して、この構造体がJSONバイト配列に変換されます。- 新しいアップロードURL (
*uploadURL
) と、builderKey
という認証キーを含むクエリパラメータが結合され、最終的なリクエストURLが構築されます。 http.Post(u, "application/json", bytes.NewReader(req))
を使用して、JSONデータが新しいダウンロードページのエンドポイントにHTTP POSTリクエストとして送信されます。"application/json"
ヘッダーは、送信されるデータのMIMEタイプがJSONであることを示します。
-
エラーハンドリングの改善:
- アップロード処理中のエラーメッセージがより詳細になり、どのファイルでエラーが発生したかを明確に報告するようになりました。例えば、
log.Printf("%s: %v", targ, err)
がlog.Printf("uploading %s: %v", targ, err)
に変更され、return err
がreturn fmt.Errorf("uploading %s: %v", targ, err)
に変更されています。 - HTTP POSTリクエストのレスポンスステータスコードが
http.StatusOK
(200 OK) でない場合、エラーとして報告されます。
- アップロード処理中のエラーメッセージがより詳細になり、どのファイルでエラーが発生したかを明確に報告するようになりました。例えば、
これらの変更により、makerelease
ツールは単にファイルをGCSにアップロードするだけでなく、そのアップロードに関する豊富なメタデータを新しいダウンロードページシステムに通知する役割も担うようになりました。これにより、ダウンロードページはアップロードされたファイルを自動的に認識し、ユーザーに表示できるようになります。
コアとなるコードの変更箇所
misc/makerelease/makerelease.go
ファイルにおける主要な変更箇所は以下の通りです。
--- a/misc/makerelease/makerelease.go
+++ b/misc/makerelease/makerelease.go
@@ -12,12 +12,15 @@ import (
"bufio"
"bytes"
"compress/gzip"
+ "crypto/sha1"
+ "encoding/json"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
+ "net/url"
"os"
"os/exec"
"path"
@@ -43,12 +46,13 @@ var (
staticToolchain = flag.Bool("static", true, "try to build statically linked toolchain (only supported on ELF targets)")
tokenCache = flag.String("token", defaultCacheFile, "Authentication token cache file")
storageBucket = flag.String("bucket", "golang", "Cloud Storage Bucket")
+ uploadURL = flag.String("upload_url", defaultUploadURL, "Upload URL")
defaultCacheFile = filepath.Join(os.Getenv("HOME"), ".makerelease-request-token")
+ defaultUploadURL = "http://golang.org/dl/upload"
)
const (
- uploadURL = "https://go.googlecode.com/files"
blogPath = "code.google.com/p/go.blog"
toolPath = "code.google.com/p/go.tools"
tourPath = "code.google.com/p/go-tour"
@@ -167,7 +171,7 @@ func main() {
continue
}
if err := b.Upload(version, targ); err != nil {
- log.Printf("%s: %v", targ, err)
+ log.Printf("uploading %s: %v", targ, err)
}
continue
}
@@ -455,7 +459,7 @@ func (b *Build) Do() error {
for _, targ := range targs {
err = b.Upload(version, targ)
if err != nil {
- return err
+ return fmt.Errorf("uploading %s: %v", targ, err)
}
}
}
@@ -653,28 +657,67 @@ func (b *Build) env() []string {
}
func (b *Build) Upload(version string, filename string) error {
- svc, err := storage.New(oauthClient)
+ file, err := ioutil.ReadFile(filename)
if err != nil {
return err
}
+ svc, err := storage.New(oauthClient)
+ if err != nil {
+ return err
+ }
obj := &storage.Object{
Acl: []*storage.ObjectAccessControl{{Entity: "allUsers", Role: "READER"}},
Name: filename,
}
- f, err := os.Open(filename)
+ _, err = svc.Objects.Insert(*storageBucket, obj).Media(bytes.NewReader(file)).Do()
if err != nil {
return err
}
- defer f.Close()
- _, err = svc.Objects.Insert(*storageBucket, obj).Media(f).Do()
+
+ sum := fmt.Sprintf("%x", sha1.Sum(file))
+ kind := "unknown"
+ switch {
+ case b.Source:
+ kind = "source"
+ case strings.HasSuffix(filename, ".tar.gz"), strings.HasSuffix(filename, ".zip"):
+ kind = "archive"
+ case strings.HasSuffix(filename, ".msi"), strings.HasSuffix(filename, ".pkg"):
+ kind = "installer"
+ }
+ req, err := json.Marshal(File{
+ Filename: filename,
+ Version: version,
+ OS: b.OS,
+ Arch: b.Arch,
+ Checksum: sum,
+ Kind: kind,
+ })
+ if err != nil {
+ return err
+ }
+ u := fmt.Sprintf("%s?%s", *uploadURL, url.Values{"key": []string{builderKey}}.Encode())
+ resp, err := http.Post(u, "application/json", bytes.NewReader(req))
if err != nil {
return err
}
+ defer resp.Body.Close()
+ if resp.StatusCode != http.StatusOK {
+ return fmt.Errorf("upload status: %v", resp.Status)
+ }
return nil
}
+type File struct {
+ Filename string
+ OS string
+ Arch string
+ Version string
+ Checksum string `datastore:",noindex"`
+ Kind string // "archive", "installer", "source"
+}
+
func setupOAuthClient() {
config := &oauth.Config{
ClientId: "999119582588-h7kpj5pcm6d9solh5lgrbusmvvk4m9dn.apps.googleusercontent.com",
コアとなるコードの解説
このコミットの核心は、Upload
関数内のロジックの変更と、新しい File
構造体の導入にあります。
-
Upload
関数の変更:- ファイルの読み込みとGCSへのアップロード: 以前は
os.Open
でファイルを開き、そのファイルハンドルをGCSアップロードに渡していましたが、変更後はioutil.ReadFile
でファイル全体をメモリに読み込んでいます。これにより、ファイルの内容全体にアクセスできるようになり、後続のSHA1チェックサム計算が可能になります。GCSへのアップロード自体はsvc.Objects.Insert(*storageBucket, obj).Media(bytes.NewReader(file)).Do()
で行われ、これは以前とほぼ同じですが、bytes.NewReader(file)
を使用してメモリ上のファイル内容を渡しています。 - SHA1チェックサムの計算:
sum := fmt.Sprintf("%x", sha1.Sum(file))
の行で、メモリに読み込んだファイルの内容からSHA1ハッシュを計算しています。このハッシュ値は、ダウンロードされたファイルの整合性を検証するために非常に重要です。 - ファイル種類の判別:
switch
ステートメントを使用して、アップロードされるファイルの種類(ソース、アーカイブ、インストーラー)を自動的に判別し、kind
変数に設定しています。これは、ダウンロードページでファイルを適切に分類・表示するために使用されます。 - メタデータのJSON化:
json.Marshal(File{...})
の行で、ファイル名、バージョン、OS、アーキテクチャ、チェックサム、種類といったメタデータをFile
構造体に格納し、それをJSON形式のバイト配列に変換しています。 - 新しいダウンロードページへの報告:
http.Post
を使用して、生成されたJSONデータを*uploadURL
(デフォルトではhttp://golang.org/dl/upload
)に送信しています。このHTTP POSTリクエストには、認証のためのbuilderKey
もクエリパラメータとして含まれています。これにより、新しいダウンロードページシステムは、アップロードされたファイルに関する情報をリアルタイムで受け取り、自身のデータベースを更新することができます。 - エラーハンドリング: HTTPレスポンスのステータスコードが
StatusOK
でない場合、エラーを返すことで、アップロード報告が成功したかどうかを確認しています。
- ファイルの読み込みとGCSへのアップロード: 以前は
-
File
構造体の導入:- この新しい構造体は、Goのリリースファイルに関する標準化されたメタデータ形式を提供します。これにより、
makerelease
ツールと新しいダウンロードページシステムの間で、ファイル情報を一貫した方法で交換できるようになります。Checksum
フィールドにdatastore:",noindex"
タグが付いていることから、この情報がGoogle Cloud Datastoreに保存され、かつこのフィールドでは検索インデックスが不要であることが示唆されます。
- この新しい構造体は、Goのリリースファイルに関する標準化されたメタデータ形式を提供します。これにより、
これらの変更により、Goのリリースプロセスは、単にファイルをストレージに置くだけでなく、そのファイルに関する豊富な情報を新しいダウンロードページに自動的に通知する、より洗練されたシステムへと進化しました。
関連リンク
- Go Code Review 102040047: https://golang.org/cl/102040047
- Go言語の公式ダウンロードページ: https://golang.org/dl/ (このコミットによって更新される対象)
参考にした情報源リンク
- GitHubコミットページ: https://github.com/golang/go/commit/865904f6d8c1dd80b1a203c531d0eb03e7f403f7
- Go言語のドキュメント (一般的なリリースプロセスやツールに関する情報): https://golang.org/doc/
- Google Cloud Storage ドキュメント (一般的なGCSの利用方法): https://cloud.google.com/storage/docs
- OAuth 2.0 の概要 (一般的なOAuthの仕組み): https://oauth.net/2/
- JSONの公式ウェブサイト (JSON形式の概要): https://www.json.org/json-ja.html
- SHA-1 (Wikipedia): https://ja.wikipedia.org/wiki/SHA-1
- HTTP POST メソッド (MDN Web Docs): https://developer.mozilla.org/ja/docs/Web/HTTP/Methods/POST
[インデックス 19486] ファイルの概要
このコミットは、Go言語のリリースプロセスで使用される misc/makerelease
ツールに対する変更です。具体的には、新しいダウンロードページへのアップロード報告メカニズムを導入し、リリースされたバイナリやソースアーカイブの情報を適切に新しいシステムに送信するように更新しています。これにより、Goの公式ダウンロードページがより正確かつ効率的に更新されるようになります。
コミット
commit 865904f6d8c1dd80b1a203c531d0eb03e7f403f7
Author: Andrew Gerrand <adg@golang.org>
Date: Mon Jun 2 12:46:03 2014 +1000
misc/makerelease: report uploads to the new downloads page
LGTM=bradfitz
R=golang-codereviews, bradfitz
CC=golang-codereviews
https://golang.org/cl/102040047
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/865904f6d8c1dd80b1a203c531d0eb03e7f403f7
元コミット内容
このコミットは、misc/makerelease
ツールが、Goの新しいダウンロードページにアップロード情報を報告するように変更するものです。以前は go.googlecode.com/files
に直接ファイルをアップロードしていましたが、新しいダウンロードページでは、アップロードされたファイルに関するメタデータ(ファイル名、バージョン、OS、アーキテクチャ、チェックサム、種類など)をHTTP POSTリクエストで特定のURL (golang.org/dl/upload
) に送信することで、ダウンロードページが動的に更新される仕組みに移行しました。
変更の背景
Go言語のリリースプロセスは、新しいバージョンのGoが公開されるたびに、そのバイナリやソースコードをユーザーがダウンロードできるようにするための重要な手順です。以前は、Google Codeのファイルホスティングサービス(go.googlecode.com/files
)を利用してリリースファイルを公開していました。しかし、このシステムは静的なファイルホスティングであり、ダウンロードページの情報を手動で更新する必要があるか、あるいは限定的な自動化しか提供していなかった可能性があります。
このコミットが行われた2014年頃は、Google Codeが徐々にその役割を終え、GitHubなどの他のプラットフォームへの移行が進んでいた時期と重なります。Goプロジェクトも、より柔軟で動的なダウンロードページを提供するために、インフラストラクチャの変更を計画していました。
この変更の主な背景は以下の通りです。
- ダウンロードページの刷新: ユーザーエクスペリエンスの向上と、より詳細な情報(チェックサム、ファイルの種類など)を提供できる新しいダウンロードページへの移行。
- 自動化の強化: リリースファイルのアップロードと同時に、ダウンロードページの情報も自動的に更新されるようにすることで、手動での作業を減らし、エラーのリスクを低減する。
- Google Codeからの移行: Google Codeのサービス終了を見据え、Goのリリースインフラをより持続可能なものにする必要があった。新しいシステムでは、Google Cloud Storageにファイルを保存し、そのメタデータを新しいダウンロードページに報告する形に変わりました。
これにより、Goのリリース管理がより堅牢で自動化されたものになり、ユーザーは常に最新かつ正確なダウンロード情報にアクセスできるようになりました。
前提知識の解説
このコミットを理解するためには、以下の技術的背景知識が役立ちます。
- Go言語のリリースプロセス: Go言語の新しいバージョンがリリースされる際の一連の作業。これには、ソースコードのビルド、各種プラットフォーム向けバイナリの生成、テスト、そして生成されたファイルの公開が含まれます。
misc/makerelease
はこのプロセスの一部を自動化するツールです。 misc/makerelease
ツール: Goプロジェクトのmisc
ディレクトリにある内部ツールで、Goの公式リリースをビルドし、公開するためのスクリプトやユーティリティが含まれています。このツールは、Goのソースからコンパイラやツールチェインをビルドし、様々なOS/アーキテクチャ向けのバイナリパッケージを作成し、それらをストレージにアップロードする役割を担っています。- Google Cloud Storage (GCS): Googleが提供するオブジェクトストレージサービス。大量のデータを保存し、世界中からアクセス可能にするためのスケーラブルなインフラを提供します。Goのリリースファイルは、このGCSバケットに保存されます。
- OAuth: Open Authorizationの略で、ユーザーが自分のアカウント情報を第三者のアプリケーションと安全に共有するための標準プロトコルです。
makerelease
ツールがGCSにファイルをアップロードする際に、認証のためにOAuthが使用されます。 go.googlecode.com/files
: 以前GoのリリースファイルがホストされていたGoogle Codeのファイルホスティングサービス。このコミットの変更前は、makerelease
が直接ここにファイルをアップロードしていました。golang.org/dl/upload
: このコミットで導入された新しいエンドポイント。makerelease
ツールが、アップロードされたファイルに関するメタデータをJSON形式でHTTP POSTリクエストとして送信する先のURLです。このエンドポイントが受け取った情報に基づいて、Goの公式ダウンロードページが動的に更新されます。- SHA1 チェックサム: Secure Hash Algorithm 1の略で、データの完全性を検証するために使用される暗号学的ハッシュ関数です。ファイルの内容から一意の短い文字列(ハッシュ値)を生成し、ダウンロードされたファイルが破損していないか、改ざんされていないかを確認するために利用されます。
- JSON (JavaScript Object Notation): 軽量なデータ交換フォーマット。人間にとっても読み書きしやすく、機械にとっても解析しやすい形式です。このコミットでは、アップロードされるファイルのメタデータをJSON形式で新しいダウンロードページに送信しています。
- HTTP POSTリクエスト: Webサーバーにデータを送信するためのHTTPメソッドの一つ。このコミットでは、JSON形式のメタデータを
golang.org/dl/upload
エンドポイントに送信するために使用されます。
これらの知識は、コミットの変更がGoのリリースインフラストラクチャ全体にどのように影響するかを理解する上で不可欠です。
技術的詳細
このコミットの主要な技術的変更点は、misc/makerelease/makerelease.go
ファイルにおけるアップロード処理のロジックの変更です。
-
新しいアップロードURLの導入:
- 以前はハードコードされていた
uploadURL = "https://go.googlecode.com/files"
が削除されました。 - 代わりに、
flag
パッケージを使用してuploadURL
という新しいコマンドラインフラグが追加され、デフォルト値としてhttp://golang.org/dl/upload
が設定されました。これにより、アップロード先のURLを柔軟に設定できるようになりました。 defaultUploadURL
定数がhttp://golang.org/dl/upload
として定義されました。
- 以前はハードコードされていた
-
ファイルの読み込みとSHA1チェックサムの計算:
Upload
関数内で、まずアップロード対象のファイルをioutil.ReadFile(filename)
でメモリに読み込みます。- 読み込んだファイルの内容に対して
sha1.Sum(file)
を実行し、SHA1チェックサムを計算します。このチェックサムは、ダウンロードされたファイルの整合性検証に利用されます。
-
ファイルメタデータ構造
File
の定義:- アップロード情報をJSON形式で送信するために、新しい構造体
File
が定義されました。 File
構造体には以下のフィールドが含まれます:Filename
(string): アップロードされるファイルの名前(例:go1.3.linux-amd64.tar.gz
)。OS
(string): 対象オペレーティングシステム(例:linux
)。Arch
(string): 対象アーキテクチャ(例:amd64
)。Version
(string): Goのバージョン(例:go1.3
)。Checksum
(string): 計算されたSHA1チェックサム。datastore:",noindex"
タグが付与されており、Datastoreに保存される際にこのフィールドがインデックスされないことを示唆しています(これは新しいダウンロードページがDatastoreを使用している可能性を示唆します)。Kind
(string): ファイルの種類("archive"
,"installer"
,"source"
)。
- アップロード情報をJSON形式で送信するために、新しい構造体
-
ファイル種類の自動判別:
Upload
関数内で、ファイルの拡張子やビルド設定に基づいてKind
フィールドが自動的に判別されます。b.Source
がtrue
の場合(ソースアーカイブの場合)は"source"
。- ファイル名が
.tar.gz
または.zip
で終わる場合は"archive"
。 - ファイル名が
.msi
または.pkg
で終わる場合は"installer"
。 - それ以外は
"unknown"
。
-
JSONデータの生成とHTTP POSTリクエスト:
File
構造体のインスタンスが作成され、必要なメタデータが設定されます。json.Marshal(File{...})
を使用して、この構造体がJSONバイト配列に変換されます。- 新しいアップロードURL (
*uploadURL
) と、builderKey
という認証キーを含むクエリパラメータが結合され、最終的なリクエストURLが構築されます。 http.Post(u, "application/json", bytes.NewReader(req))
を使用して、JSONデータが新しいダウンロードページのエンドポイントにHTTP POSTリクエストとして送信されます。"application/json"
ヘッダーは、送信されるデータのMIMEタイプがJSONであることを示します。
-
エラーハンドリングの改善:
- アップロード処理中のエラーメッセージがより詳細になり、どのファイルでエラーが発生したかを明確に報告するようになりました。例えば、
log.Printf("%s: %v", targ, err)
がlog.Printf("uploading %s: %v", targ, err)
に変更され、return err
がreturn fmt.Errorf("uploading %s: %v", targ, err)
に変更されています。 - HTTP POSTリクエストのレスポンスステータスコードが
http.StatusOK
(200 OK) でない場合、エラーとして報告されます。
- アップロード処理中のエラーメッセージがより詳細になり、どのファイルでエラーが発生したかを明確に報告するようになりました。例えば、
これらの変更により、makerelease
ツールは単にファイルをGCSにアップロードするだけでなく、そのアップロードに関する豊富なメタデータを新しいダウンロードページシステムに通知する役割も担うようになりました。これにより、ダウンロードページはアップロードされたファイルを自動的に認識し、ユーザーに表示できるようになります。
コアとなるコードの変更箇所
misc/makerelease/makerelease.go
ファイルにおける主要な変更箇所は以下の通りです。
--- a/misc/makerelease/makerelease.go
+++ b/misc/makerelease/makerelease.go
@@ -12,12 +12,15 @@ import (
"bufio"
"bytes"
"compress/gzip"
+ "crypto/sha1"
+ "encoding/json"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
+ "net/url"
"os"
"os/exec"
"path"
@@ -43,12 +46,13 @@ var (
staticToolchain = flag.Bool("static", true, "try to build statically linked toolchain (only supported on ELF targets)")
tokenCache = flag.String("token", defaultCacheFile, "Authentication token cache file")
storageBucket = flag.String("bucket", "golang", "Cloud Storage Bucket")
+ uploadURL = flag.String("upload_url", defaultUploadURL, "Upload URL")
defaultCacheFile = filepath.Join(os.Getenv("HOME"), ".makerelease-request-token")
+ defaultUploadURL = "http://golang.org/dl/upload"
)
const (
- uploadURL = "https://go.googlecode.com/files"
blogPath = "code.google.com/p/go.blog"
toolPath = "code.google.com/p/go.tools"
tourPath = "code.google.com/p/go-tour"
@@ -167,7 +171,7 @@ func main() {
continue
}
if err := b.Upload(version, targ); err != nil {
- log.Printf("%s: %v", targ, err)
+ log.Printf("uploading %s: %v", targ, err)
}
continue
}
@@ -455,7 +459,7 @@ func (b *Build) Do() error {
for _, targ := range targs {
err = b.Upload(version, targ)
if err != nil {
- return err
+ return fmt.Errorf("uploading %s: %v", targ, err)
}
}
}
@@ -653,28 +657,67 @@ func (b *Build) env() []string {
}
func (b *Build) Upload(version string, filename string) error {
- svc, err := storage.New(oauthClient)
+ file, err := ioutil.ReadFile(filename)
if err != nil {
return err
}
+ svc, err := storage.New(oauthClient)
+ if err != nil {
+ return err
+ }
obj := &storage.Object{
Acl: []*storage.ObjectAccessControl{{Entity: "allUsers", Role: "READER"}},
Name: filename,
}
- f, err := os.Open(filename)
+ _, err = svc.Objects.Insert(*storageBucket, obj).Media(bytes.NewReader(file)).Do()
if err != nil {
return err
}
- defer f.Close()
- _, err = svc.Objects.Insert(*storageBucket, obj).Media(f).Do()
+
+ sum := fmt.Sprintf("%x", sha1.Sum(file))
+ kind := "unknown"
+ switch {
+ case b.Source:
+ kind = "source"
+ case strings.HasSuffix(filename, ".tar.gz"), strings.HasSuffix(filename, ".zip"):
+ kind = "archive"
+ case strings.HasSuffix(filename, ".msi"), strings.HasSuffix(filename, ".pkg"):
+ kind = "installer"
+ }
+ req, err := json.Marshal(File{
+ Filename: filename,
+ Version: version,
+ OS: b.OS,
+ Arch: b.Arch,
+ Checksum: sum,
+ Kind: kind,
+ })
+ if err != nil {
+ return err
+ }
+ u := fmt.Sprintf("%s?%s", *uploadURL, url.Values{"key": []string{builderKey}}.Encode())
+ resp, err := http.Post(u, "application/json", bytes.NewReader(req))
if err != nil {
return err
}
+ defer resp.Body.Close()
+ if resp.StatusCode != http.StatusOK {
+ return fmt.Errorf("upload status: %v", resp.Status)
+ }
return nil
}
+type File struct {
+ Filename string
+ OS string
+ Arch string
+ Version string
+ Checksum string `datastore:",noindex"`
+ Kind string // "archive", "installer", "source"
+}
+
func setupOAuthClient() {
config := &oauth.Config{
ClientId: "999119582588-h7kpj5pcm6d9solh5lgrbusmvvk4m9dn.apps.googleusercontent.com",
コアとなるコードの解説
このコミットの核心は、Upload
関数内のロジックの変更と、新しい File
構造体の導入にあります。
-
Upload
関数の変更:- ファイルの読み込みとGCSへのアップロード: 以前は
os.Open
でファイルを開き、そのファイルハンドルをGCSアップロードに渡していましたが、変更後はioutil.ReadFile
でファイル全体をメモリに読み込んでいます。これにより、ファイルの内容全体にアクセスできるようになり、後続のSHA1チェックサム計算が可能になります。GCSへのアップロード自体はsvc.Objects.Insert(*storageBucket, obj).Media(bytes.NewReader(file)).Do()
で行われ、これは以前とほぼ同じですが、bytes.NewReader(file)
を使用してメモリ上のファイル内容を渡しています。 - SHA1チェックサムの計算:
sum := fmt.Sprintf("%x", sha1.Sum(file))
の行で、メモリに読み込んだファイルの内容からSHA1ハッシュを計算しています。このハッシュ値は、ダウンロードされたファイルの整合性を検証するために非常に重要です。 - ファイル種類の判別:
switch
ステートメントを使用して、アップロードされるファイルの種類(ソース、アーカイブ、インストーラー)を自動的に判別し、kind
変数に設定しています。これは、ダウンロードページでファイルを適切に分類・表示するために使用されます。 - メタデータのJSON化:
json.Marshal(File{...})
の行で、ファイル名、バージョン、OS、アーキテクチャ、チェックサム、種類といったメタデータをFile
構造体に格納し、それをJSON形式のバイト配列に変換しています。 - 新しいダウンロードページへの報告:
http.Post
を使用して、生成されたJSONデータを*uploadURL
(デフォルトではhttp://golang.org/dl/upload
)に送信しています。このHTTP POSTリクエストには、認証のためのbuilderKey
もクエリパラメータとして含まれています。これにより、新しいダウンロードページシステムは、アップロードされたファイルに関する情報をリアルタイムで受け取り、自身のデータベースを更新することができます。 - エラーハンドリング: HTTPレスポンスのステータスコードが
StatusOK
でない場合、エラーを返すことで、アップロード報告が成功したかどうかを確認しています。
- ファイルの読み込みとGCSへのアップロード: 以前は
-
File
構造体の導入:- この新しい構造体は、Goのリリースファイルに関する標準化されたメタデータ形式を提供します。これにより、
makerelease
ツールと新しいダウンロードページシステムの間で、ファイル情報を一貫した方法で交換できるようになります。Checksum
フィールドにdatastore:",noindex"
タグが付いていることから、この情報がGoogle Cloud Datastoreに保存され、かつこのフィールドでは検索インデックスが不要であることが示唆されます。
- この新しい構造体は、Goのリリースファイルに関する標準化されたメタデータ形式を提供します。これにより、
これらの変更により、Goのリリースプロセスは、単にファイルをストレージに置くだけでなく、そのファイルに関する豊富な情報を新しいダウンロードページに自動的に通知する、より洗練されたシステムへと進化しました。
関連リンク
- Go Code Review 102040047: https://golang.org/cl/102040047
- Go言語の公式ダウンロードページ: https://golang.org/dl/ (このコミットによって更新される対象)
参考にした情報源リンク
- GitHubコミットページ: https://github.com/golang/go/commit/865904f6d8c1dd80b1a203c531d0eb03e7f403f7
- Go言語のドキュメント (一般的なリリースプロセスやツールに関する情報): https://golang.org/doc/
- Google Cloud Storage ドキュメント (一般的なGCSの利用方法): https://cloud.google.com/storage/docs
- OAuth 2.0 の概要 (一般的なOAuthの仕組み): https://oauth.net/2/
- JSONの公式ウェブサイト (JSON形式の概要): https://www.json.org/json-ja.html
- SHA-1 (Wikipedia): https://ja.wikipedia.org/wiki/SHA-1
- HTTP POST メソッド (MDN Web Docs): https://developer.mozilla.org/ja/docs/Web/HTTP/Methods/POST