Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 12419] ファイルの概要

このコミットは、Go言語のパッケージ管理ツールである go get コマンドの機能を拡張し、任意のURLをバージョン管理システム(VCS)のリポジトリへのエイリアスとして使用できるようにするものです。これにより、ユーザーはより短く、またはカスタムドメインを使用したインポートパスでGoパッケージを取得できるようになります。

コミット

commit 932c8ddba158a91056eba87045bb6d5ddbeb39f7
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date:   Mon Mar 5 22:36:15 2012 -0800

    cmd/go: allow go get with arbitrary URLs
    
    This CL permits using arbitrary, non-VCS-qualified URLs as
    aliases for fully VCS-qualified and/or well-known code hosting
    sites.
    
    Example 1) A VCS-qualified URL can now be shorter.
    
    Before:
    $ go get camlistore.org/r/p/camlistore.git/pkg/blobref
    After:
    $ go get camlistore.org/pkg/blobref
    
    Example 2) A custom domain can be used as the import,
    referencing a well-known code hosting site.
    
    Before:
    $ go get github.com/bradfitz/sonden
    After:
    $ go get bradfitz.com/pkg/sonden
    
    The mechanism used is a <meta> tag in the HTML document
    retrieved from fetching:
    
        https://<import>?go-get=1  (preferred)
        http://<import>?go-get=1   (fallback)
    
    The meta tag should look like:
    
    <meta name="go-import" content="import-alias-prefix vcs full-repo-root">
    
    The full-repo-root must be a full URL root to a repository containing
    a scheme and *not* containing a ".vcs" qualifier.
    
    The vcs is one of "git", "hg", "svn", etc.
    
    The import-alias-prefix must be a prefix or exact match of the
    package being fetched with "go get".
    
    If there are multiple meta tags, only the one with a prefix
    matching the import path is used. It is an error if multiple
    go-import values match the import prefix.
    
    If the import-alias-prefix is not an exact match for the import,
    another HTTP fetch is performed, at the declared root (which does
    *not* need to be the domain's root).
    
    For example, assuming that "camlistore.org/pkg/blobref" declares
    in its HTML head:
    
    <meta name="go-import" content="camlistore.org git https://camlistore.org/r/p/camlistore" />
    
    ... then:
    
    $ go get camlistore.org/pkg/blobref
    
    ... looks at the following URLs:
    
       https://camlistore.org/pkg/blobref?go-get=1
       http://camlistore.org/pkg/blobref?go-get=1
       https://camlistore.org/?go-get=1
       http://camlistore.org/?go-get=1
    
    Ultimately it finds, at the root (camlistore.org/), the same go-import:
    
    <meta name="go-import" content="camlistore.org git https://camlistore.org/r/p/camlistore" />
    
    ... and proceeds to trust it, checking out git //camlistore.org/r/p/camlistore at
    the import path of "camlistore.org" on disk.
    
    Fixes #3099
    
    R=r, rsc, gary.burd, eikeon, untheoretic, n13m3y3r, rsc
    CC=golang-dev
    https://golang.org/cl/5660051

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/932c8ddba158a91056eba87045bb6d5ddbeb39f7

元コミット内容

このコミットは、go get コマンドが、バージョン管理システム(VCS)のタイプやリポジトリのフルパスを直接指定せずに、より簡潔なインポートパスでGoパッケージを取得できるようにする機能を追加します。具体的には、ウェブサイトのHTML内に埋め込まれた <meta name="go-import"> タグを介して、カスタムのインポートパスを実際のVCSリポジトリにマッピングするメカニズムを導入します。

これにより、以下のような利点が得られます。

  1. インポートパスの短縮: VCSのタイプやリポジトリの .git などの拡張子を含まない、より短いインポートパスを使用できるようになります。
  2. カスタムドメインの利用: ユーザーは自身のカスタムドメインをGoパッケージのインポートパスとして使用し、その背後でGitHubやBitbucketなどの既存のコードホスティングサイトを利用できるようになります。これは「バニティインポートパス(Vanity Import Paths)」として知られる機能です。

この機能は、go get が指定されたインポートパスに対応するURLに ?go-get=1 クエリパラメータを付加してHTTP(S)リクエストを送信し、そのレスポンスHTMLから特定のメタタグを解析することで実現されます。

変更の背景

この変更が導入される以前の go get コマンドは、Goパッケージのインポートパスが直接VCSリポジトリの構造を反映しているか、またはGoが認識している特定のコードホスティングサイト(例: github.combitbucket.org)のパターンに従っている必要がありました。

しかし、この方式にはいくつかの課題がありました。

  • インポートパスの冗長性: リポジトリのフルパスやVCSのタイプ(例: .git)がインポートパスに含まれるため、パスが長くなりがちでした。
  • ベンダーロックイン: 特定のコードホスティングサービスに依存したインポートパスは、将来的にサービスを移行する際にインポートパスを変更する必要が生じ、既存のコードベースに影響を与える可能性がありました。
  • ブランドの一貫性: 開発者が自身のカスタムドメインをGoパッケージのインポートパスとして使用したい場合、直接的な方法がありませんでした。これは、プロジェクトのブランドを確立し、よりプロフェッショナルな印象を与える上で重要です。

これらの課題を解決し、go get の柔軟性と使いやすさを向上させるために、任意のURLをVCSリポジトリへのエイリアスとして機能させるメカニズムが求められていました。このコミットは、その解決策としてHTMLのメタタグを利用した「バニティインポートパス」のサポートを導入しました。

前提知識の解説

このコミットの理解を深めるために、以下の概念について事前に理解しておくことが役立ちます。

  • go get コマンド: Go言語の公式ツールチェーンに含まれるコマンドで、リモートリポジトリからGoパッケージのソースコードを取得し、ローカルの GOPATH に配置する役割を担います。依存関係の解決とダウンロードも行います。
  • インポートパス (Import Path): Go言語において、パッケージを一意に識別するための文字列です。通常、リポジトリのURLとディレクトリ構造を組み合わせた形式を取ります(例: github.com/user/repo/package)。
  • バージョン管理システム (VCS): ソフトウェアのソースコードやその他のファイルの変更履歴を管理するためのシステムです。Git、Mercurial (Hg)、Subversion (Svn) などがあります。go get はこれらのVCSと連携してリポジトリをクローンします。
  • HTML <meta> タグ: HTMLドキュメントの <head> セクション内に配置され、ページのメタデータ(ページに関する情報)を提供するタグです。name 属性と content 属性を組み合わせて、様々な情報を指定できます。このコミットでは、name="go-import" という特定のメタタグが利用されます。
  • HTTP(S) リクエスト: ウェブブラウザとウェブサーバー間で情報をやり取りするためのプロトコルです。go get は、パッケージの情報を取得するためにHTTP(S)リクエストを送信します。
  • クエリパラメータ: URLの末尾に ? の後に続く key=value 形式のデータです。サーバーに特定の情報や指示を伝えるために使用されます。このコミットでは ?go-get=1 が使用されます。
  • GOPATH: Go言語のワークスペースのルートディレクトリを指す環境変数です。go get でダウンロードされたパッケージは、このディレクトリの src サブディレクトリ以下に配置されます。
  • ブートストラップ (Bootstrap): ソフトウェア開発において、より複雑なシステムを構築するために必要な最小限の機能セットや初期プロセスを指します。Goのツールチェーンでは、自己ホスト型コンパイラやツールを構築するために、よりシンプルなブートストラップ版の go コマンドが存在します。このコミットでは、ブートストラップ版とフル機能版の go コマンドでHTTP処理の有無が区別されています。

技術的詳細

このコミットの核となる技術的詳細は、go get コマンドがインポートパスを解決する際の新しいロジックと、それに伴うHTTPリクエストおよびHTML解析の導入にあります。

  1. インポートパス解決のフロー変更:

    • 従来の go get は、インポートパスが既知のVCSホスティングサイトのパターン(例: github.com/user/repo)に一致するか、またはVCSタイプを明示的に含むパス(例: example.com/repo.git/pkg)であるかを静的に判断していました。
    • このコミットにより、repoRootForImportPath 関数が導入され、まず repoRootForImportPathStatic で静的なパターンマッチングを試みます。
    • 静的なマッチングに失敗した場合(errUnknownSite が返された場合)、repoRootForImportDynamic 関数が呼び出され、動的な解決プロセスが開始されます。
  2. 動的なインポートパス解決 (repoRootForImportDynamic):

    • この関数は、指定されたインポートパス(例: bradfitz.com/pkg/sonden)に対してHTTP(S)リquestを送信します。
    • リクエストURLは、インポートパスに ?go-get=1 クエリパラメータを付加した形式になります(例: https://bradfitz.com/pkg/sonden?go-get=1)。HTTPSが優先され、失敗した場合はHTTPが試行されます。この処理は httpsOrHTTP 関数によって行われます。
    • レスポンスとして返されたHTMLドキュメントの <head> セクションを解析し、<meta name="go-import" content="import-alias-prefix vcs full-repo-root"> 形式のメタタグを探します。この解析は parseMetaGoImports 関数(discovery.go に新設)によって行われます。
    • 複数の go-import メタタグが存在する場合、matchGoImport 関数がインポートパスに最も一致する import-alias-prefix を持つタグを選択します。複数のタグが同じインポートパスに一致する場合はエラーとなります。
    • 取得したメタタグの情報(PrefixVCSRepoRoot)に基づいて、実際のVCSリポジトリのURLと、そのリポジトリがディスク上のどこに配置されるべきかのルートパスを決定します。
  3. 非権威的なメタタグの検証:

    • もし go-import メタタグの import-alias-prefix が元のインポートパスと完全に一致しない場合(例: go get example.com/pkg/subexample.com というプレフィックスのメタタグが見つかった場合)、go get はそのメタタグが宣言している Prefix のURL(例: https://example.com/?go-get=1)に対しても再度HTTP(S)リクエストを送信し、同じ go-import メタタグが存在するかどうかを確認します。
    • この「二重チェック」は、悪意のあるユーザーが自身のサブパスに偽の go-import メタタグを配置し、ユーザーを別のリポジトリに誘導する「なりすまし」攻撃を防ぐための重要なセキュリティ対策です。ルートパスで同じ情報が確認できなければ、そのメタタグは信頼されません。
  4. エラーハンドリングとロギング:

    • HTTPフェッチの失敗、メタタグの解析エラー、不正なメタタグの形式(例: RepoRoot にスキームがない)、未知のVCSタイプなどが適切にエラーとして処理されます。
    • 詳細モード (-v フラグ) が有効な場合、log.Printf を使用してフェッチURLや見つかったメタタグの情報がログに出力され、デバッグが容易になります。

これらの変更により、go get はより柔軟なインポートパスの解決能力を獲得し、Goパッケージの配布と利用の自由度を大幅に向上させました。

コアとなるコードの変更箇所

このコミットでは、主に以下の5つのファイルが変更されています。

  1. src/cmd/go/bootstrap.go:

    • ブートストラップ版の go コマンドにおけるHTTP関連関数のスタブが変更されました。
    • httpGET 関数が errHTTP を返すように変更され、httpsOrHTTP および parseMetaGoImports のスタブが追加されました。これは、ブートストラッププロセス中にネットワーク機能が不要であることを保証するためです。
  2. src/cmd/go/discovery.go (新規ファイル):

    • HTMLドキュメントから go-import メタタグを解析するための新しいロジックが実装されています。
    • parseMetaGoImports 関数は io.Reader からHTMLを読み込み、XMLデコーダを使用して <head> セクション内の <meta name="go-import"> タグを抽出します。
    • attrValue ヘルパー関数は、XML属性リストから指定された名前の属性値を取得します。
    • このファイルは、ブートストラップ版の go コマンドにはコンパイルされないように // +build !cmd_go_bootstrap ディレクティブが付けられています。
  3. src/cmd/go/get.go:

    • downloadPackage 関数内で、VCSとリポジトリの情報を取得する部分が変更されました。
    • 従来の vcsForImportPath の呼び出しが、新しく導入された repoRootForImportPath の呼び出しに置き換えられています。これにより、静的および動的なインポートパス解決のロジックが統合されます。
  4. src/cmd/go/http.go:

    • HTTPリクエストを処理するための新しい関数 httpsOrHTTP が追加されました。
    • この関数は、指定されたインポートパスに対してまずHTTPSで ?go-get=1 クエリパラメータ付きのリクエストを試み、失敗した場合はHTTPで再試行します。
    • httpClient 変数が導入され、テスト時にHTTPクライアントを差し替えられるようになりました。
    • httpGET 関数は、io/ioutil の代わりに io パッケージを使用するように変更されました。
  5. src/cmd/go/vcs.go:

    • インポートパスからVCSリポジトリのルートを決定する主要なロジックが大幅に改修されました。
    • repoRoot 構造体が新しく定義され、VCSコマンド、リポジトリURL、およびリポジトリのルートパスをカプセル化します。
    • repoRootForImportPath 関数が導入され、これがインポートパス解決の主要なエントリポイントとなります。この関数は、まず静的な解決 (repoRootForImportPathStatic) を試み、失敗した場合に動的な解決 (repoRootForImportDynamic) にフォールバックします。
    • repoRootForImportPathStatic は、既知のVCSホスティングサイトのパターンや、VCSタイプを明示的に含むパスを処理します。
    • repoRootForImportDynamic は、カスタムドメインのインポートパスを処理するために、HTTPフェッチとメタタグ解析 (parseMetaGoImportsmatchGoImport) を行います。
    • metaImport 構造体と matchGoImport 関数が追加され、解析された go-import メタタグの情報を表現し、インポートパスに一致するタグを選択するロジックを提供します。
    • セキュリティ対策として、非権威的なメタタグの検証ロジックが repoRootForImportDynamic 内に実装されました。

コアとなるコードの解説

src/cmd/go/discovery.go (新規)

// parseMetaGoImports returns meta imports from the HTML in r.
// Parsing ends at the end of the <head> section or the beginning of the <body>.
func parseMetaGoImports(r io.Reader) (imports []metaImport) {
	d := xml.NewDecoder(r)
	d.Strict = false // HTMLは厳密なXMLではないため、非厳密モードで解析
	for {
		t, err := d.Token() // 次のXMLトークンを取得
		if err != nil {
			return // エラーまたはEOFで終了
		}
		if e, ok := t.(xml.StartElement); ok && strings.EqualFold(e.Name.Local, "body") {
			return // <body>タグの開始で解析終了
		}
		if e, ok := t.(xml.EndElement); ok && strings.EqualFold(e.Name.Local, "head") {
			return // </head>タグの終了で解析終了
		}
		e, ok := t.(xml.StartElement)
		if !ok || !strings.EqualFold(e.Name.Local, "meta") {
			continue // 開始タグでなく、かつmetaタグでなければスキップ
		}
		if attrValue(e.Attr, "name") != "go-import" {
			continue // name属性が"go-import"でなければスキップ
		}
		if f := strings.Fields(attrValue(e.Attr, "content")); len(f) == 3 {
			// content属性をスペースで分割し、3つのフィールドがあればmetaImportとして追加
			imports = append(imports, metaImport{
				Prefix:   f[0],
				VCS:      f[1],
				RepoRoot: f[2],
			})
		}
	}
	return
}

// attrValue returns the attribute value for the case-insensitive key
// `name', or the empty string if nothing is found.
func attrValue(attrs []xml.Attr, name string) string {
	for _, a := range attrs {
		if strings.EqualFold(a.Name.Local, name) {
			return a.Value // 属性名が一致すればその値を返す
		}
	}
	return "" // 見つからなければ空文字列
}

discovery.go は、HTMLドキュメントから go-import メタタグを効率的に抽出するための新しいファイルです。parseMetaGoImports 関数は、xml.NewDecoder を使用してHTMLをストリームとして解析し、<head> セクション内にある <meta name="go-import" ...> タグを探します。d.Strict = false は、HTMLが厳密なXMLではないため、解析エラーを許容するように設定しています。<body> タグの開始または </head> タグの終了で解析を停止し、不要な部分の処理を避けます。attrValue はヘルパー関数で、属性リストから指定された名前の属性値をケースインセンシティブに検索します。

src/cmd/go/http.go

// httpsOrHTTP returns the body of either the importPath's
// https resource or, if unavailable, the http resource.
func httpsOrHTTP(importPath string) (urlStr string, body io.ReadCloser, err error) {
	fetch := func(scheme string) (urlStr string, res *http.Response, err error) {
		u, err := url.Parse(scheme + "://" + importPath)
		if err != nil {
			return "", nil, err
		}
		u.RawQuery = "go-get=1" // go-get=1 クエリパラメータを追加
		urlStr = u.String()
		if buildV {
			log.Printf("Fetching %s", urlStr) // 詳細モードでフェッチURLをログ出力
		}
		res, err = httpClient.Get(urlStr) // HTTP GETリクエストを実行
		return
	}
	closeBody := func(res *http.Response) {
		if res != nil {
			res.Body.Close() // レスポンスボディをクローズ
		}
	}

	// まずHTTPSを試行
	urlStr, res, err := fetch("https")
	if err != nil || res.StatusCode != 200 {
		if buildV {
			if err != nil {
				log.Printf("https fetch failed.")
			} else {
				log.Printf("ignoring https fetch with status code %d", res.StatusCode)
			}
		}
		closeBody(res)
		// HTTPSが失敗またはステータスコード200以外の場合、HTTPを試行
		urlStr, res, err = fetch("http")
	}

	if err != nil {
		closeBody(res)
		log.Printf("http fetch failed")
		return "", nil, err // HTTPも失敗したらエラーを返す
	}
	// Note: accepting a non-200 OK here, so people can serve a
	// meta import in their http 404 page.
	log.Printf("Parsing meta tags from %s (status code %d)", urlStr, res.StatusCode)
	return urlStr, res.Body, nil // 成功したURLとレスポンスボディを返す
}

http.go に追加された httpsOrHTTP 関数は、go get がリモートのHTMLドキュメントを取得する際の主要なネットワーク処理を担います。この関数は、まずHTTPSで指定された importPath?go-get=1 クエリパラメータを付加してリクエストを送信します。HTTPSでのリクエストが失敗するか、ステータスコードが200以外の場合(ただし、404ページなどでメタタグを提供できるように、200以外でもボディは解析対象となる)、フォールバックとしてHTTPで同じリクエストを試みます。これにより、セキュアな接続が優先されつつも、互換性が確保されます。

src/cmd/go/vcs.go

// repoRoot represents a version control system, a repo, and a root of
// where to put it on disk.
type repoRoot struct {
	vcs *vcsCmd // 使用するVCSコマンド (git, hgなど)
	// repo is the repository URL, including scheme
	repo string // リポジトリの完全なURL (例: https://github.com/user/repo)
	// root is the import path corresponding to the root of the
	// repository
	root string // ディスク上に配置される際のルートインポートパス (例: example.com/pkg)
}

// repoRootForImportPath analyzes importPath to determine the
// version control system, and code repository to use.
func repoRootForImportPath(importPath string) (*repoRoot, error) {
	// まず静的な解決を試みる
	rr, err := repoRootForImportPathStatic(importPath, "")
	if err == errUnknownSite {
		// 静的な解決が失敗した場合、動的な解決を試みる
		rr, err = repoRootForImportDynamic(importPath)
	}
	return rr, err
}

var errUnknownSite = errors.New("dynamic lookup required to find mapping")

// repoRootForImportPathStatic attempts to map importPath to a
// repoRoot using the commonly-used VCS hosting sites in vcsPaths
// (github.com/user/dir), or from a fully-qualified importPath already
// containing its VCS type (foo.com/repo.git/dir)
// ... (既存の静的解析ロジック) ...

// repoRootForImportDynamic finds a *repoRoot for a custom domain that's not
// statically known by repoRootForImportPathStatic.
//
// This handles "vanity import paths" like "name.tld/pkg/foo".
func repoRootForImportDynamic(importPath string) (*repoRoot, error) {
	slash := strings.Index(importPath, "/")
	if slash < 0 {
		return nil, fmt.Errorf("missing / in import %q", importPath)
	}

	// HTTP(S)フェッチでHTMLを取得
	urlStr, body, err := httpsOrHTTP(importPath)
	if err != nil {
		return nil, fmt.Errorf("http/https fetch for import %q: %v", importPath, err)
	}
	defer body.Close()

	// HTMLからgo-importメタタグを解析し、importPathに一致するものを選択
	metaImport, err := matchGoImport(parseMetaGoImports(body), importPath)
	if err != nil {
		if err != errNoMatch {
			return nil, fmt.Errorf("parse %s: %v", urlStr, err)
		}
		return nil, fmt.Errorf("parse %s: no go-import meta tags", urlStr)
	}
	if buildV {
		log.Printf("get %q: found meta tag %#v at %s", importPath, metaImport, urlStr)
	}

	// 非権威的なメタタグの検証 (セキュリティ対策)
	// metaImport.Prefix が元の importPath と異なる場合、Prefix のURLも確認する
	if metaImport.Prefix != importPath {
		if buildV {
			log.Printf("get %q: verifying non-authoritative meta tag", importPath)
		}
		urlStr0 := urlStr // 最初のURLを保存
		// Prefix のURLをフェッチ
		urlStr, body, err = httpsOrHTTP(metaImport.Prefix)
		if err != nil {
			return nil, fmt.Errorf("fetch %s: %v", urlStr, err)
		}
		imports := parseMetaGoImports(body) // 再度メタタグを解析
		if len(imports) == 0 {
			return nil, fmt.Errorf("fetch %s: no go-import meta tag", urlStr)
		}
		metaImport2, err := matchGoImport(imports, importPath)
		// 最初のメタタグと2回目のメタタグが一致しない場合、エラー
		if err != nil || metaImport != metaImport2 {
			return nil, fmt.Errorf("%s and %s disagree about go-import for %s", urlStr0, urlStr, metaImport.Prefix)
		}
	}

	// RepoRootにスキームが含まれているか検証
	if !strings.Contains(metaImport.RepoRoot, "://") {
		return nil, fmt.Errorf("%s: invalid repo root %q; no scheme", urlStr, metaImport.RepoRoot)
	}

	// repoRoot構造体を構築して返す
	rr := &repoRoot{
		vcs:  vcsByCmd(metaImport.VCS),
		repo: metaImport.RepoRoot,
		root: metaImport.Prefix,
	}
	if rr.vcs == nil {
		return nil, fmt.Errorf("%s: unknown vcs %q", urlStr, metaImport.VCS)
	}
	return rr, nil
}

// metaImport represents the parsed <meta name="go-import"
// content="prefix vcs reporoot" /> tags from HTML files.
type metaImport struct {
	Prefix, VCS, RepoRoot string
}

// errNoMatch is returned from matchGoImport when there's no applicable match.
var errNoMatch = errors.New("no import match")

// matchGoImport returns the metaImport from imports matching importPath.
// An error is returned if there are multiple matches.
// errNoMatch is returned if none match.
func matchGoImport(imports []metaImport, importPath string) (_ metaImport, err error) {
	match := -1
	for i, im := range imports {
		if !strings.HasPrefix(importPath, im.Prefix) {
			continue // importPathがPrefixで始まらなければスキップ
		}
		if match != -1 {
			// 複数のメタタグが一致する場合、エラー
			err = fmt.Errorf("multiple meta tags match import path %q", importPath)
			return
		}
		match = i // 一致するタグのインデックスを記録
	}
	if match == -1 {
		err = errNoMatch // 一致するタグがなければエラー
		return
	}
	return imports[match], nil // 一致したタグを返す
}

vcs.go はこのコミットで最も大きく変更されたファイルであり、go get のインポートパス解決ロジックの核心を担っています。

  • repoRoot 構造体: VCSの種類、リポジトリのURL、そしてディスク上のルートパスをまとめる新しい構造体です。
  • repoRootForImportPath: インポートパス解決の新しいエントリポイントです。まず静的な解決 (repoRootForImportPathStatic) を試み、それが失敗した場合に動的な解決 (repoRootForImportDynamic) にフォールバックします。
  • repoRootForImportDynamic: バニティインポートパスを処理するための主要な関数です。
    • httpsOrHTTP を呼び出して、インポートパスに対応するURLからHTMLを取得します。
    • parseMetaGoImports を呼び出して、HTMLから go-import メタタグを解析します。
    • matchGoImport を呼び出して、解析されたメタタグの中から現在のインポートパスに最も一致するものを選択します。
    • セキュリティ検証: metaImport.Prefix が元の importPath と異なる場合、metaImport.Prefix のURLに対しても再度HTTPリクエストを送信し、同じ go-import メタタグが存在するかを確認します。これは、悪意のあるリダイレクトを防ぐための重要なステップです。
    • RepoRoot にスキーム (://) が含まれているかどうかの検証も行い、不正なリポジトリURLを検出します。
    • 最終的に、取得した情報に基づいて repoRoot 構造体を構築して返します。
  • metaImport 構造体: go-import メタタグの content 属性から解析された PrefixVCSRepoRoot の各フィールドを保持します。
  • matchGoImport: 複数の go-import メタタグが存在する場合に、指定された importPath に最も一致する Prefix を持つタグを選択します。複数のタグが一致する場合はエラーを返します。

これらの変更により、go get はカスタムドメインやより簡潔なインポートパスを透過的に処理できるようになり、Goパッケージの配布と利用の柔軟性が大幅に向上しました。

関連リンク

  • Go Issue #3099: go get with custom domains: https://github.com/golang/go/issues/3099
  • Go Change-Id: I2222222222222222222222222222222222222222 (コミットメッセージに記載の https://golang.org/cl/5660051 はCL番号であり、GitHubのコミットハッシュとは異なりますが、関連する変更リストを示しています。)

参考にした情報源リンク