go/vcs: delete
This will also close issues 10159, 12079, 19362, and 22556
as 'not planned'.
Fixes golang/go#57051.
Change-Id: I1cc6851a60c554d2266b5c03b8c010270919f5be
Reviewed-on: https://go-review.googlesource.com/c/tools/+/505956
Run-TryBot: Dmitri Shuralyov <dmitshur@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
gopls-CI: kokoro <noreply+kokoro@google.com>
Auto-Submit: Dmitri Shuralyov <dmitshur@golang.org>
Reviewed-by: Bryan Mills <bcmills@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
diff --git a/README.md b/README.md
index 0a2fcf1..4dbf6d6 100644
--- a/README.md
+++ b/README.md
@@ -62,12 +62,11 @@
Numerous other packages provide more esoteric functionality.
-<!-- Some that didn't make the cut:
+<!-- Some that didn't make the cut:
golang.org/x/tools/benchmark/parse
golang.org/x/tools/go/ast/astutil
golang.org/x/tools/go/types/typeutil
-golang.org/x/tools/go/vcs
golang.org/x/tools/playground
golang.org/x/tools/present
golang.org/x/tools/refactor/importgraph
diff --git a/go/vcs/discovery.go b/go/vcs/discovery.go
deleted file mode 100644
index 7d179bc..0000000
--- a/go/vcs/discovery.go
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright 2012 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package vcs
-
-import (
- "encoding/xml"
- "fmt"
- "io"
- "strings"
-)
-
-// charsetReader returns a reader for the given charset. Currently
-// it only supports UTF-8 and ASCII. Otherwise, it returns a meaningful
-// error which is printed by go get, so the user can find why the package
-// wasn't downloaded if the encoding is not supported. Note that, in
-// order to reduce potential errors, ASCII is treated as UTF-8 (i.e. characters
-// greater than 0x7f are not rejected).
-func charsetReader(charset string, input io.Reader) (io.Reader, error) {
- switch strings.ToLower(charset) {
- case "ascii":
- return input, nil
- default:
- return nil, fmt.Errorf("can't decode XML document using charset %q", charset)
- }
-}
-
-// parseMetaGoImports returns meta imports from the HTML in r.
-// Parsing ends at the end of the <head> section or the beginning of the <body>.
-//
-// This copy of cmd/go/internal/vcs.parseMetaGoImports always operates
-// in IgnoreMod ModuleMode.
-func parseMetaGoImports(r io.Reader) (imports []metaImport, err error) {
- d := xml.NewDecoder(r)
- d.CharsetReader = charsetReader
- d.Strict = false
- var t xml.Token
- for {
- t, err = d.RawToken()
- if err != nil {
- if err == io.EOF || len(imports) > 0 {
- err = nil
- }
- return
- }
- if e, ok := t.(xml.StartElement); ok && strings.EqualFold(e.Name.Local, "body") {
- return
- }
- if e, ok := t.(xml.EndElement); ok && strings.EqualFold(e.Name.Local, "head") {
- return
- }
- e, ok := t.(xml.StartElement)
- if !ok || !strings.EqualFold(e.Name.Local, "meta") {
- continue
- }
- if attrValue(e.Attr, "name") != "go-import" {
- continue
- }
- if f := strings.Fields(attrValue(e.Attr, "content")); len(f) == 3 {
- // Ignore VCS type "mod", which is applicable only in module mode.
- if f[1] == "mod" {
- continue
- }
- imports = append(imports, metaImport{
- Prefix: f[0],
- VCS: f[1],
- RepoRoot: f[2],
- })
- }
- }
-}
-
-// 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 ""
-}
diff --git a/go/vcs/env.go b/go/vcs/env.go
deleted file mode 100644
index 189210c..0000000
--- a/go/vcs/env.go
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package vcs
-
-import (
- "os"
- "strings"
-)
-
-// envForDir returns a copy of the environment
-// suitable for running in the given directory.
-// The environment is the current process's environment
-// but with an updated $PWD, so that an os.Getwd in the
-// child will be faster.
-func envForDir(dir string) []string {
- env := os.Environ()
- // Internally we only use rooted paths, so dir is rooted.
- // Even if dir is not rooted, no harm done.
- return mergeEnvLists([]string{"PWD=" + dir}, env)
-}
-
-// mergeEnvLists merges the two environment lists such that
-// variables with the same name in "in" replace those in "out".
-func mergeEnvLists(in, out []string) []string {
-NextVar:
- for _, inkv := range in {
- k := strings.SplitAfterN(inkv, "=", 2)[0]
- for i, outkv := range out {
- if strings.HasPrefix(outkv, k) {
- out[i] = inkv
- continue NextVar
- }
- }
- out = append(out, inkv)
- }
- return out
-}
diff --git a/go/vcs/go.mod b/go/vcs/go.mod
deleted file mode 100644
index 74da6cb..0000000
--- a/go/vcs/go.mod
+++ /dev/null
@@ -1,7 +0,0 @@
-// Deprecated: This module contains one deprecated package.
-// See the package deprecation notice for more information.
-module golang.org/x/tools/go/vcs
-
-go 1.19
-
-require golang.org/x/sys v0.9.0
diff --git a/go/vcs/go.sum b/go/vcs/go.sum
deleted file mode 100644
index cb35c1f..0000000
--- a/go/vcs/go.sum
+++ /dev/null
@@ -1,2 +0,0 @@
-golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
-golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
diff --git a/go/vcs/http.go b/go/vcs/http.go
deleted file mode 100644
index 5836511..0000000
--- a/go/vcs/http.go
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright 2012 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package vcs
-
-import (
- "fmt"
- "io"
- "io/ioutil"
- "log"
- "net/http"
- "net/url"
-)
-
-// httpClient is the default HTTP client, but a variable so it can be
-// changed by tests, without modifying http.DefaultClient.
-var httpClient = http.DefaultClient
-
-// httpGET returns the data from an HTTP GET request for the given URL.
-func httpGET(url string) ([]byte, error) {
- resp, err := httpClient.Get(url)
- if err != nil {
- return nil, err
- }
- defer resp.Body.Close()
- if resp.StatusCode != 200 {
- return nil, fmt.Errorf("%s: %s", url, resp.Status)
- }
- b, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- return nil, fmt.Errorf("%s: %v", url, err)
- }
- return b, nil
-}
-
-// 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"
- urlStr = u.String()
- if Verbose {
- log.Printf("Fetching %s", urlStr)
- }
- res, err = httpClient.Get(urlStr)
- return
- }
- closeBody := func(res *http.Response) {
- if res != nil {
- res.Body.Close()
- }
- }
- urlStr, res, err := fetch("https")
- if err != nil || res.StatusCode != 200 {
- if Verbose {
- if err != nil {
- log.Printf("https fetch failed.")
- } else {
- log.Printf("ignoring https fetch with status code %d", res.StatusCode)
- }
- }
- closeBody(res)
- urlStr, res, err = fetch("http")
- }
- if err != nil {
- closeBody(res)
- return "", nil, err
- }
- // Note: accepting a non-200 OK here, so people can serve a
- // meta import in their http 404 page.
- if Verbose {
- log.Printf("Parsing meta tags from %s (status code %d)", urlStr, res.StatusCode)
- }
- return urlStr, res.Body, nil
-}
diff --git a/go/vcs/vcs.go b/go/vcs/vcs.go
deleted file mode 100644
index 232177d..0000000
--- a/go/vcs/vcs.go
+++ /dev/null
@@ -1,764 +0,0 @@
-// Copyright 2012 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package vcs exposes functions for resolving import paths
-// and using version control systems, which can be used to
-// implement behavior similar to the standard "go get" command.
-//
-// Deprecated: Use the go list command with -json flag instead,
-// which implements up-to-date import path resolution behavior,
-// module support, and includes the latest security fixes.
-//
-// This package was a copy of internal code in package cmd/go/internal/get
-// before module support, modified to make the identifiers exported.
-// It was provided here for developers who wanted to write tools with similar semantics.
-// It needed to be manually kept in sync with upstream when changes were
-// made to cmd/go/internal/get, as tracked in go.dev/issue/11490.
-// By now, it has diverged significantly from upstream cmd/go/internal/get
-// behavior and will not receive any further updates.
-package vcs // import "golang.org/x/tools/go/vcs"
-
-import (
- "bytes"
- "encoding/json"
- "errors"
- "fmt"
- exec "golang.org/x/sys/execabs"
- "log"
- "net/url"
- "os"
- "path/filepath"
- "regexp"
- "strconv"
- "strings"
-)
-
-// Verbose enables verbose operation logging.
-var Verbose bool
-
-// ShowCmd controls whether VCS commands are printed.
-var ShowCmd bool
-
-// A Cmd describes how to use a version control system
-// like Mercurial, Git, or Subversion.
-type Cmd struct {
- Name string
- Cmd string // name of binary to invoke command
-
- CreateCmd string // command to download a fresh copy of a repository
- DownloadCmd string // command to download updates into an existing repository
-
- TagCmd []TagCmd // commands to list tags
- TagLookupCmd []TagCmd // commands to lookup tags before running tagSyncCmd
- TagSyncCmd string // command to sync to specific tag
- TagSyncDefault string // command to sync to default tag
-
- LogCmd string // command to list repository changelogs in an XML format
-
- Scheme []string
- PingCmd string
-}
-
-// A TagCmd describes a command to list available tags
-// that can be passed to Cmd.TagSyncCmd.
-type TagCmd struct {
- Cmd string // command to list tags
- Pattern string // regexp to extract tags from list
-}
-
-// vcsList lists the known version control systems
-var vcsList = []*Cmd{
- vcsHg,
- vcsGit,
- vcsSvn,
- vcsBzr,
-}
-
-// ByCmd returns the version control system for the given
-// command name (hg, git, svn, bzr).
-func ByCmd(cmd string) *Cmd {
- for _, vcs := range vcsList {
- if vcs.Cmd == cmd {
- return vcs
- }
- }
- return nil
-}
-
-// vcsHg describes how to use Mercurial.
-var vcsHg = &Cmd{
- Name: "Mercurial",
- Cmd: "hg",
-
- CreateCmd: "clone -U {repo} {dir}",
- DownloadCmd: "pull",
-
- // We allow both tag and branch names as 'tags'
- // for selecting a version. This lets people have
- // a go.release.r60 branch and a go1 branch
- // and make changes in both, without constantly
- // editing .hgtags.
- TagCmd: []TagCmd{
- {"tags", `^(\S+)`},
- {"branches", `^(\S+)`},
- },
- TagSyncCmd: "update -r {tag}",
- TagSyncDefault: "update default",
-
- LogCmd: "log --encoding=utf-8 --limit={limit} --template={template}",
-
- Scheme: []string{"https", "http", "ssh"},
- PingCmd: "identify {scheme}://{repo}",
-}
-
-// vcsGit describes how to use Git.
-var vcsGit = &Cmd{
- Name: "Git",
- Cmd: "git",
-
- CreateCmd: "clone {repo} {dir}",
- DownloadCmd: "pull --ff-only",
-
- TagCmd: []TagCmd{
- // tags/xxx matches a git tag named xxx
- // origin/xxx matches a git branch named xxx on the default remote repository
- {"show-ref", `(?:tags|origin)/(\S+)$`},
- },
- TagLookupCmd: []TagCmd{
- {"show-ref tags/{tag} origin/{tag}", `((?:tags|origin)/\S+)$`},
- },
- TagSyncCmd: "checkout {tag}",
- TagSyncDefault: "checkout master",
-
- Scheme: []string{"git", "https", "http", "git+ssh"},
- PingCmd: "ls-remote {scheme}://{repo}",
-}
-
-// vcsBzr describes how to use Bazaar.
-var vcsBzr = &Cmd{
- Name: "Bazaar",
- Cmd: "bzr",
-
- CreateCmd: "branch {repo} {dir}",
-
- // Without --overwrite bzr will not pull tags that changed.
- // Replace by --overwrite-tags after http://pad.lv/681792 goes in.
- DownloadCmd: "pull --overwrite",
-
- TagCmd: []TagCmd{{"tags", `^(\S+)`}},
- TagSyncCmd: "update -r {tag}",
- TagSyncDefault: "update -r revno:-1",
-
- Scheme: []string{"https", "http", "bzr", "bzr+ssh"},
- PingCmd: "info {scheme}://{repo}",
-}
-
-// vcsSvn describes how to use Subversion.
-var vcsSvn = &Cmd{
- Name: "Subversion",
- Cmd: "svn",
-
- CreateCmd: "checkout {repo} {dir}",
- DownloadCmd: "update",
-
- // There is no tag command in subversion.
- // The branch information is all in the path names.
-
- LogCmd: "log --xml --limit={limit}",
-
- Scheme: []string{"https", "http", "svn", "svn+ssh"},
- PingCmd: "info {scheme}://{repo}",
-}
-
-func (v *Cmd) String() string {
- return v.Name
-}
-
-// run runs the command line cmd in the given directory.
-// keyval is a list of key, value pairs. run expands
-// instances of {key} in cmd into value, but only after
-// splitting cmd into individual arguments.
-// If an error occurs, run prints the command line and the
-// command's combined stdout+stderr to standard error.
-// Otherwise run discards the command's output.
-func (v *Cmd) run(dir string, cmd string, keyval ...string) error {
- _, err := v.run1(dir, cmd, keyval, true)
- return err
-}
-
-// runVerboseOnly is like run but only generates error output to standard error in verbose mode.
-func (v *Cmd) runVerboseOnly(dir string, cmd string, keyval ...string) error {
- _, err := v.run1(dir, cmd, keyval, false)
- return err
-}
-
-// runOutput is like run but returns the output of the command.
-func (v *Cmd) runOutput(dir string, cmd string, keyval ...string) ([]byte, error) {
- return v.run1(dir, cmd, keyval, true)
-}
-
-// run1 is the generalized implementation of run and runOutput.
-func (v *Cmd) run1(dir string, cmdline string, keyval []string, verbose bool) ([]byte, error) {
- m := make(map[string]string)
- for i := 0; i < len(keyval); i += 2 {
- m[keyval[i]] = keyval[i+1]
- }
- args := strings.Fields(cmdline)
- for i, arg := range args {
- args[i] = expand(m, arg)
- }
-
- _, err := exec.LookPath(v.Cmd)
- if err != nil {
- fmt.Fprintf(os.Stderr,
- "go: missing %s command. See http://golang.org/s/gogetcmd\n",
- v.Name)
- return nil, err
- }
-
- cmd := exec.Command(v.Cmd, args...)
- cmd.Dir = dir
- cmd.Env = envForDir(cmd.Dir)
- if ShowCmd {
- fmt.Printf("cd %s\n", dir)
- fmt.Printf("%s %s\n", v.Cmd, strings.Join(args, " "))
- }
- var buf bytes.Buffer
- cmd.Stdout = &buf
- cmd.Stderr = &buf
- err = cmd.Run()
- out := buf.Bytes()
- if err != nil {
- if verbose || Verbose {
- fmt.Fprintf(os.Stderr, "# cd %s; %s %s\n", dir, v.Cmd, strings.Join(args, " "))
- os.Stderr.Write(out)
- }
- return nil, err
- }
- return out, nil
-}
-
-// Ping pings the repo to determine if scheme used is valid.
-// This repo must be pingable with this scheme and VCS.
-func (v *Cmd) Ping(scheme, repo string) error {
- return v.runVerboseOnly(".", v.PingCmd, "scheme", scheme, "repo", repo)
-}
-
-// Create creates a new copy of repo in dir.
-// The parent of dir must exist; dir must not.
-func (v *Cmd) Create(dir, repo string) error {
- return v.run(".", v.CreateCmd, "dir", dir, "repo", repo)
-}
-
-// CreateAtRev creates a new copy of repo in dir at revision rev.
-// The parent of dir must exist; dir must not.
-// rev must be a valid revision in repo.
-func (v *Cmd) CreateAtRev(dir, repo, rev string) error {
- if err := v.Create(dir, repo); err != nil {
- return err
- }
- return v.run(dir, v.TagSyncCmd, "tag", rev)
-}
-
-// Download downloads any new changes for the repo in dir.
-// dir must be a valid VCS repo compatible with v.
-func (v *Cmd) Download(dir string) error {
- return v.run(dir, v.DownloadCmd)
-}
-
-// Tags returns the list of available tags for the repo in dir.
-// dir must be a valid VCS repo compatible with v.
-func (v *Cmd) Tags(dir string) ([]string, error) {
- var tags []string
- for _, tc := range v.TagCmd {
- out, err := v.runOutput(dir, tc.Cmd)
- if err != nil {
- return nil, err
- }
- re := regexp.MustCompile(`(?m-s)` + tc.Pattern)
- for _, m := range re.FindAllStringSubmatch(string(out), -1) {
- tags = append(tags, m[1])
- }
- }
- return tags, nil
-}
-
-// TagSync syncs the repo in dir to the named tag, which is either a
-// tag returned by Tags or the empty string (the default tag).
-// dir must be a valid VCS repo compatible with v and the tag must exist.
-func (v *Cmd) TagSync(dir, tag string) error {
- if v.TagSyncCmd == "" {
- return nil
- }
- if tag != "" {
- for _, tc := range v.TagLookupCmd {
- out, err := v.runOutput(dir, tc.Cmd, "tag", tag)
- if err != nil {
- return err
- }
- re := regexp.MustCompile(`(?m-s)` + tc.Pattern)
- m := re.FindStringSubmatch(string(out))
- if len(m) > 1 {
- tag = m[1]
- break
- }
- }
- }
- if tag == "" && v.TagSyncDefault != "" {
- return v.run(dir, v.TagSyncDefault)
- }
- return v.run(dir, v.TagSyncCmd, "tag", tag)
-}
-
-// Log logs the changes for the repo in dir.
-// dir must be a valid VCS repo compatible with v.
-func (v *Cmd) Log(dir, logTemplate string) ([]byte, error) {
- if err := v.Download(dir); err != nil {
- return []byte{}, err
- }
-
- const N = 50 // how many revisions to grab
- return v.runOutput(dir, v.LogCmd, "limit", strconv.Itoa(N), "template", logTemplate)
-}
-
-// LogAtRev logs the change for repo in dir at the rev revision.
-// dir must be a valid VCS repo compatible with v.
-// rev must be a valid revision for the repo in dir.
-func (v *Cmd) LogAtRev(dir, rev, logTemplate string) ([]byte, error) {
- if err := v.Download(dir); err != nil {
- return []byte{}, err
- }
-
- // Append revision flag to LogCmd.
- logAtRevCmd := v.LogCmd + " --rev=" + rev
- return v.runOutput(dir, logAtRevCmd, "limit", strconv.Itoa(1), "template", logTemplate)
-}
-
-// A vcsPath describes how to convert an import path into a
-// version control system and repository name.
-type vcsPath struct {
- prefix string // prefix this description applies to
- re string // pattern for import path
- repo string // repository to use (expand with match of re)
- vcs string // version control system to use (expand with match of re)
- check func(match map[string]string) error // additional checks
- ping bool // ping for scheme to use to download repo
-
- regexp *regexp.Regexp // cached compiled form of re
-}
-
-// FromDir inspects dir and its parents to determine the
-// version control system and code repository to use.
-// On return, root is the import path
-// corresponding to the root of the repository.
-func FromDir(dir, srcRoot string) (vcs *Cmd, root string, err error) {
- // Clean and double-check that dir is in (a subdirectory of) srcRoot.
- dir = filepath.Clean(dir)
- srcRoot = filepath.Clean(srcRoot)
- if len(dir) <= len(srcRoot) || dir[len(srcRoot)] != filepath.Separator {
- return nil, "", fmt.Errorf("directory %q is outside source root %q", dir, srcRoot)
- }
-
- var vcsRet *Cmd
- var rootRet string
-
- origDir := dir
- for len(dir) > len(srcRoot) {
- for _, vcs := range vcsList {
- if _, err := os.Stat(filepath.Join(dir, "."+vcs.Cmd)); err == nil {
- root := filepath.ToSlash(dir[len(srcRoot)+1:])
- // Record first VCS we find, but keep looking,
- // to detect mistakes like one kind of VCS inside another.
- if vcsRet == nil {
- vcsRet = vcs
- rootRet = root
- continue
- }
- // Allow .git inside .git, which can arise due to submodules.
- if vcsRet == vcs && vcs.Cmd == "git" {
- continue
- }
- // Otherwise, we have one VCS inside a different VCS.
- return nil, "", fmt.Errorf("directory %q uses %s, but parent %q uses %s",
- filepath.Join(srcRoot, rootRet), vcsRet.Cmd, filepath.Join(srcRoot, root), vcs.Cmd)
- }
- }
-
- // Move to parent.
- ndir := filepath.Dir(dir)
- if len(ndir) >= len(dir) {
- // Shouldn't happen, but just in case, stop.
- break
- }
- dir = ndir
- }
-
- if vcsRet != nil {
- return vcsRet, rootRet, nil
- }
-
- return nil, "", fmt.Errorf("directory %q is not using a known version control system", origDir)
-}
-
-// RepoRoot represents a version control system, a repo, and a root of
-// where to put it on disk.
-type RepoRoot struct {
- VCS *Cmd
-
- // Repo is the repository URL, including scheme.
- Repo string
-
- // Root is the import path corresponding to the root of the
- // repository.
- Root string
-}
-
-// RepoRootForImportPath analyzes importPath to determine the
-// version control system, and code repository to use.
-func RepoRootForImportPath(importPath string, verbose bool) (*RepoRoot, error) {
- rr, err := RepoRootForImportPathStatic(importPath, "")
- if err == errUnknownSite {
- rr, err = RepoRootForImportDynamic(importPath, verbose)
-
- // RepoRootForImportDynamic returns error detail
- // that is irrelevant if the user didn't intend to use a
- // dynamic import in the first place.
- // Squelch it.
- if err != nil {
- if Verbose {
- log.Printf("import %q: %v", importPath, err)
- }
- err = fmt.Errorf("unrecognized import path %q", importPath)
- }
- }
-
- if err == nil && strings.Contains(importPath, "...") && strings.Contains(rr.Root, "...") {
- // Do not allow wildcards in the repo root.
- rr = nil
- err = fmt.Errorf("cannot expand ... in %q", 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)
-//
-// If scheme is non-empty, that scheme is forced.
-func RepoRootForImportPathStatic(importPath, scheme string) (*RepoRoot, error) {
- if strings.Contains(importPath, "://") {
- return nil, fmt.Errorf("invalid import path %q", importPath)
- }
- for _, srv := range vcsPaths {
- if !strings.HasPrefix(importPath, srv.prefix) {
- continue
- }
- m := srv.regexp.FindStringSubmatch(importPath)
- if m == nil {
- if srv.prefix != "" {
- return nil, fmt.Errorf("invalid %s import path %q", srv.prefix, importPath)
- }
- continue
- }
-
- // Build map of named subexpression matches for expand.
- match := map[string]string{
- "prefix": srv.prefix,
- "import": importPath,
- }
- for i, name := range srv.regexp.SubexpNames() {
- if name != "" && match[name] == "" {
- match[name] = m[i]
- }
- }
- if srv.vcs != "" {
- match["vcs"] = expand(match, srv.vcs)
- }
- if srv.repo != "" {
- match["repo"] = expand(match, srv.repo)
- }
- if srv.check != nil {
- if err := srv.check(match); err != nil {
- return nil, err
- }
- }
- vcs := ByCmd(match["vcs"])
- if vcs == nil {
- return nil, fmt.Errorf("unknown version control system %q", match["vcs"])
- }
- if srv.ping {
- if scheme != "" {
- match["repo"] = scheme + "://" + match["repo"]
- } else {
- for _, scheme := range vcs.Scheme {
- if vcs.Ping(scheme, match["repo"]) == nil {
- match["repo"] = scheme + "://" + match["repo"]
- break
- }
- }
- }
- }
- rr := &RepoRoot{
- VCS: vcs,
- Repo: match["repo"],
- Root: match["root"],
- }
- return rr, nil
- }
- return nil, errUnknownSite
-}
-
-// RepoRootForImportDynamic finds a *RepoRoot for a custom domain that's not
-// statically known by RepoRootForImportPathStatic.
-//
-// This handles custom import paths like "name.tld/pkg/foo" or just "name.tld".
-func RepoRootForImportDynamic(importPath string, verbose bool) (*RepoRoot, error) {
- slash := strings.Index(importPath, "/")
- if slash < 0 {
- slash = len(importPath)
- }
- host := importPath[:slash]
- if !strings.Contains(host, ".") {
- return nil, errors.New("import path doesn't contain a hostname")
- }
- urlStr, body, err := httpsOrHTTP(importPath)
- if err != nil {
- return nil, fmt.Errorf("http/https fetch: %v", err)
- }
- defer body.Close()
- imports, err := parseMetaGoImports(body)
- if err != nil {
- return nil, fmt.Errorf("parsing %s: %v", importPath, err)
- }
- metaImport, err := matchGoImport(imports, 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 verbose {
- log.Printf("get %q: found meta tag %#v at %s", importPath, metaImport, urlStr)
- }
- // If the import was "uni.edu/bob/project", which said the
- // prefix was "uni.edu" and the RepoRoot was "evilroot.com",
- // make sure we don't trust Bob and check out evilroot.com to
- // "uni.edu" yet (possibly overwriting/preempting another
- // non-evil student). Instead, first verify the root and see
- // if it matches Bob's claim.
- if metaImport.Prefix != importPath {
- if verbose {
- log.Printf("get %q: verifying non-authoritative meta tag", importPath)
- }
- urlStr0 := urlStr
- urlStr, body, err = httpsOrHTTP(metaImport.Prefix)
- if err != nil {
- return nil, fmt.Errorf("fetch %s: %v", urlStr, err)
- }
- imports, err := parseMetaGoImports(body)
- if err != nil {
- return nil, fmt.Errorf("parsing %s: %v", importPath, err)
- }
- if len(imports) == 0 {
- return nil, fmt.Errorf("fetch %s: no go-import meta tag", urlStr)
- }
- metaImport2, err := matchGoImport(imports, importPath)
- if err != nil || metaImport != metaImport2 {
- return nil, fmt.Errorf("%s and %s disagree about go-import for %s", urlStr0, urlStr, metaImport.Prefix)
- }
- }
-
- if err := validateRepoRoot(metaImport.RepoRoot); err != nil {
- return nil, fmt.Errorf("%s: invalid repo root %q: %v", urlStr, metaImport.RepoRoot, err)
- }
- rr := &RepoRoot{
- VCS: ByCmd(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
-}
-
-// validateRepoRoot returns an error if repoRoot does not seem to be
-// a valid URL with scheme.
-func validateRepoRoot(repoRoot string) error {
- url, err := url.Parse(repoRoot)
- if err != nil {
- return err
- }
- if url.Scheme == "" {
- return errors.New("no scheme")
- }
- return 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")
-
-// pathPrefix reports whether sub is a prefix of s,
-// only considering entire path components.
-func pathPrefix(s, sub string) bool {
- // strings.HasPrefix is necessary but not sufficient.
- if !strings.HasPrefix(s, sub) {
- return false
- }
- // The remainder after the prefix must either be empty or start with a slash.
- rem := s[len(sub):]
- return rem == "" || rem[0] == '/'
-}
-
-// 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 !pathPrefix(importPath, im.Prefix) {
- continue
- }
-
- 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
-}
-
-// expand rewrites s to replace {k} with match[k] for each key k in match.
-func expand(match map[string]string, s string) string {
- for k, v := range match {
- s = strings.Replace(s, "{"+k+"}", v, -1)
- }
- return s
-}
-
-// vcsPaths lists the known vcs paths.
-var vcsPaths = []*vcsPath{
- // Github
- {
- prefix: "github.com/",
- re: `^(?P<root>github\.com/[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+)(/[\p{L}0-9_.\-]+)*$`,
- vcs: "git",
- repo: "https://{root}",
- check: noVCSSuffix,
- },
-
- // Bitbucket
- {
- prefix: "bitbucket.org/",
- re: `^(?P<root>bitbucket\.org/(?P<bitname>[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`,
- repo: "https://{root}",
- check: bitbucketVCS,
- },
-
- // Launchpad
- {
- prefix: "launchpad.net/",
- re: `^(?P<root>launchpad\.net/((?P<project>[A-Za-z0-9_.\-]+)(?P<series>/[A-Za-z0-9_.\-]+)?|~[A-Za-z0-9_.\-]+/(\+junk|[A-Za-z0-9_.\-]+)/[A-Za-z0-9_.\-]+))(/[A-Za-z0-9_.\-]+)*$`,
- vcs: "bzr",
- repo: "https://{root}",
- check: launchpadVCS,
- },
-
- // Git at OpenStack
- {
- prefix: "git.openstack.org",
- re: `^(?P<root>git\.openstack\.org/[A-Za-z0-9_.\-]+/[A-Za-z0-9_.\-]+)(\.git)?(/[A-Za-z0-9_.\-]+)*$`,
- vcs: "git",
- repo: "https://{root}",
- check: noVCSSuffix,
- },
-
- // General syntax for any server.
- {
- re: `^(?P<root>(?P<repo>([a-z0-9.\-]+\.)+[a-z0-9.\-]+(:[0-9]+)?/[A-Za-z0-9_.\-/]*?)\.(?P<vcs>bzr|git|hg|svn))(/[A-Za-z0-9_.\-]+)*$`,
- ping: true,
- },
-}
-
-func init() {
- // fill in cached regexps.
- // Doing this eagerly discovers invalid regexp syntax
- // without having to run a command that needs that regexp.
- for _, srv := range vcsPaths {
- srv.regexp = regexp.MustCompile(srv.re)
- }
-}
-
-// noVCSSuffix checks that the repository name does not
-// end in .foo for any version control system foo.
-// The usual culprit is ".git".
-func noVCSSuffix(match map[string]string) error {
- repo := match["repo"]
- for _, vcs := range vcsList {
- if strings.HasSuffix(repo, "."+vcs.Cmd) {
- return fmt.Errorf("invalid version control suffix in %s path", match["prefix"])
- }
- }
- return nil
-}
-
-// bitbucketVCS determines the version control system for a
-// Bitbucket repository, by using the Bitbucket API.
-func bitbucketVCS(match map[string]string) error {
- if err := noVCSSuffix(match); err != nil {
- return err
- }
-
- var resp struct {
- SCM string `json:"scm"`
- }
- url := expand(match, "https://api.bitbucket.org/2.0/repositories/{bitname}?fields=scm")
- data, err := httpGET(url)
- if err != nil {
- return err
- }
- if err := json.Unmarshal(data, &resp); err != nil {
- return fmt.Errorf("decoding %s: %v", url, err)
- }
-
- if ByCmd(resp.SCM) != nil {
- match["vcs"] = resp.SCM
- if resp.SCM == "git" {
- match["repo"] += ".git"
- }
- return nil
- }
-
- return fmt.Errorf("unable to detect version control system for bitbucket.org/ path")
-}
-
-// launchpadVCS solves the ambiguity for "lp.net/project/foo". In this case,
-// "foo" could be a series name registered in Launchpad with its own branch,
-// and it could also be the name of a directory within the main project
-// branch one level up.
-func launchpadVCS(match map[string]string) error {
- if match["project"] == "" || match["series"] == "" {
- return nil
- }
- _, err := httpGET(expand(match, "https://code.launchpad.net/{project}{series}/.bzr/branch-format"))
- if err != nil {
- match["root"] = expand(match, "launchpad.net/{project}")
- match["repo"] = expand(match, "https://{root}")
- }
- return nil
-}
diff --git a/go/vcs/vcs_test.go b/go/vcs/vcs_test.go
deleted file mode 100644
index a17b50d..0000000
--- a/go/vcs/vcs_test.go
+++ /dev/null
@@ -1,309 +0,0 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package vcs
-
-import (
- "errors"
- "io/ioutil"
- "os"
- "path"
- "path/filepath"
- "reflect"
- "runtime"
- "strings"
- "testing"
-)
-
-// Test that RepoRootForImportPath creates the correct RepoRoot for a given importPath.
-// TODO(cmang): Add tests for SVN and BZR.
-func TestRepoRootForImportPath(t *testing.T) {
- if runtime.GOOS == "android" {
- t.Skipf("incomplete source tree on %s", runtime.GOOS)
- }
-
- tests := []struct {
- path string
- want *RepoRoot
- }{
- {
- "github.com/golang/groupcache",
- &RepoRoot{
- VCS: vcsGit,
- Repo: "https://github.com/golang/groupcache",
- },
- },
- // Unicode letters in directories (issue 18660).
- {
- "github.com/user/unicode/испытание",
- &RepoRoot{
- VCS: vcsGit,
- Repo: "https://github.com/user/unicode",
- },
- },
- }
-
- for _, test := range tests {
- got, err := RepoRootForImportPath(test.path, false)
- if err != nil {
- t.Errorf("RepoRootForImportPath(%q): %v", test.path, err)
- continue
- }
- want := test.want
- if got.VCS.Name != want.VCS.Name || got.Repo != want.Repo {
- t.Errorf("RepoRootForImportPath(%q) = VCS(%s) Repo(%s), want VCS(%s) Repo(%s)", test.path, got.VCS, got.Repo, want.VCS, want.Repo)
- }
- }
-}
-
-// Test that FromDir correctly inspects a given directory and returns the right VCS and root.
-func TestFromDir(t *testing.T) {
- tempDir, err := ioutil.TempDir("", "vcstest")
- if err != nil {
- t.Fatal(err)
- }
- defer os.RemoveAll(tempDir)
-
- for j, vcs := range vcsList {
- dir := filepath.Join(tempDir, "example.com", vcs.Name, "."+vcs.Cmd)
- if j&1 == 0 {
- err := os.MkdirAll(dir, 0755)
- if err != nil {
- t.Fatal(err)
- }
- } else {
- err := os.MkdirAll(filepath.Dir(dir), 0755)
- if err != nil {
- t.Fatal(err)
- }
- f, err := os.Create(dir)
- if err != nil {
- t.Fatal(err)
- }
- f.Close()
- }
-
- want := RepoRoot{
- VCS: vcs,
- Root: path.Join("example.com", vcs.Name),
- }
- var got RepoRoot
- got.VCS, got.Root, err = FromDir(dir, tempDir)
- if err != nil {
- t.Errorf("FromDir(%q, %q): %v", dir, tempDir, err)
- continue
- }
- if got.VCS.Name != want.VCS.Name || got.Root != want.Root {
- t.Errorf("FromDir(%q, %q) = VCS(%s) Root(%s), want VCS(%s) Root(%s)", dir, tempDir, got.VCS, got.Root, want.VCS, want.Root)
- }
- }
-}
-
-var parseMetaGoImportsTests = []struct {
- in string
- out []metaImport
-}{
- {
- `<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">`,
- []metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}},
- },
- {
- `<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">
- <meta name="go-import" content="baz/quux git http://github.com/rsc/baz/quux">`,
- []metaImport{
- {"foo/bar", "git", "https://github.com/rsc/foo/bar"},
- {"baz/quux", "git", "http://github.com/rsc/baz/quux"},
- },
- },
- {
- `<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">
- <meta name="go-import" content="foo/bar mod http://github.com/rsc/baz/quux">`,
- []metaImport{
- {"foo/bar", "git", "https://github.com/rsc/foo/bar"},
- },
- },
- {
- `<meta name="go-import" content="foo/bar mod http://github.com/rsc/baz/quux">
- <meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">`,
- []metaImport{
- {"foo/bar", "git", "https://github.com/rsc/foo/bar"},
- },
- },
- {
- `<head>
- <meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">
- </head>`,
- []metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}},
- },
- {
- `<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">
- <body>`,
- []metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}},
- },
- {
- `<!doctype html><meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">`,
- []metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}},
- },
- {
- // XML doesn't like <div style=position:relative>.
- `<!doctype html><title>Page Not Found</title><meta name=go-import content="chitin.io/chitin git https://github.com/chitin-io/chitin"><div style=position:relative>DRAFT</div>`,
- []metaImport{{"chitin.io/chitin", "git", "https://github.com/chitin-io/chitin"}},
- },
- {
- `<meta name="go-import" content="myitcv.io git https://github.com/myitcv/x">
- <meta name="go-import" content="myitcv.io/blah2 mod https://raw.githubusercontent.com/myitcv/pubx/master">
- `,
- []metaImport{{"myitcv.io", "git", "https://github.com/myitcv/x"}},
- },
-}
-
-func TestParseMetaGoImports(t *testing.T) {
- for i, tt := range parseMetaGoImportsTests {
- out, err := parseMetaGoImports(strings.NewReader(tt.in))
- if err != nil {
- t.Errorf("test#%d: %v", i, err)
- continue
- }
- if !reflect.DeepEqual(out, tt.out) {
- t.Errorf("test#%d:\n\thave %q\n\twant %q", i, out, tt.out)
- }
- }
-}
-
-func TestValidateRepoRoot(t *testing.T) {
- tests := []struct {
- root string
- ok bool
- }{
- {
- root: "",
- ok: false,
- },
- {
- root: "http://",
- ok: true,
- },
- {
- root: "git+ssh://",
- ok: true,
- },
- {
- root: "http#://",
- ok: false,
- },
- {
- root: "-config",
- ok: false,
- },
- {
- root: "-config://",
- ok: false,
- },
- }
-
- for _, test := range tests {
- err := validateRepoRoot(test.root)
- ok := err == nil
- if ok != test.ok {
- want := "error"
- if test.ok {
- want = "nil"
- }
- t.Errorf("validateRepoRoot(%q) = %q, want %s", test.root, err, want)
- }
- }
-}
-
-func TestMatchGoImport(t *testing.T) {
- tests := []struct {
- imports []metaImport
- path string
- mi metaImport
- err error
- }{
- {
- imports: []metaImport{
- {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
- },
- path: "example.com/user/foo",
- mi: metaImport{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
- },
- {
- imports: []metaImport{
- {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
- },
- path: "example.com/user/foo/",
- mi: metaImport{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
- },
- {
- imports: []metaImport{
- {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
- {Prefix: "example.com/user/fooa", VCS: "git", RepoRoot: "https://example.com/repo/target"},
- },
- path: "example.com/user/foo",
- mi: metaImport{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
- },
- {
- imports: []metaImport{
- {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
- {Prefix: "example.com/user/fooa", VCS: "git", RepoRoot: "https://example.com/repo/target"},
- },
- path: "example.com/user/fooa",
- mi: metaImport{Prefix: "example.com/user/fooa", VCS: "git", RepoRoot: "https://example.com/repo/target"},
- },
- {
- imports: []metaImport{
- {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
- {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"},
- },
- path: "example.com/user/foo/bar",
- err: errors.New("should not be allowed to create nested repo"),
- },
- {
- imports: []metaImport{
- {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
- {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"},
- },
- path: "example.com/user/foo/bar/baz",
- err: errors.New("should not be allowed to create nested repo"),
- },
- {
- imports: []metaImport{
- {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
- {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"},
- },
- path: "example.com/user/foo/bar/baz/qux",
- err: errors.New("should not be allowed to create nested repo"),
- },
- {
- imports: []metaImport{
- {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
- {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"},
- },
- path: "example.com/user/foo/bar/baz/",
- err: errors.New("should not be allowed to create nested repo"),
- },
- {
- imports: []metaImport{
- {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"},
- {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"},
- },
- path: "example.com",
- err: errors.New("pathologically short path"),
- },
- }
-
- for _, test := range tests {
- mi, err := matchGoImport(test.imports, test.path)
- if mi != test.mi {
- t.Errorf("unexpected metaImport; got %v, want %v", mi, test.mi)
- }
-
- got := err
- want := test.err
- if (got == nil) != (want == nil) {
- t.Errorf("unexpected error; got %v, want %v", got, want)
- }
- }
-}