Commit 8ffbc401 authored by Julian Phillips's avatar Julian Phillips Committed by Andrew Gerrand

goinstall: Add support for generic hosts using special import form

This change extends goinstall to support "magic" package names of the
form:
        <host>/<repo>.<vcs>/<path>

Where <host> is the hostname, <repo> the path to the repository, <vcs>
the type of vcs (git, hg, bzr or svn), and <path> is the path inside the
repository that contains the source code for the package.

For example: "example.com/pub/foo.hg/src" means download the Mercurial
repository at either pub/foo.hg or pub/foo from example.com and then
build and install the source files from src inside the repository
checkout.

Repositories on the built-in hostings sites (github, bitbucket,
launchpad and googlecode) must still use the old form (i.e.
github.com/xxx/yyy.git/src will be rejected).

R=adg, rsc
CC=golang-dev
https://golang.org/cl/4626064
parent 369418d2
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
package main package main
import ( import (
"exec"
"http" "http"
"os" "os"
"path/filepath" "path/filepath"
...@@ -34,6 +35,7 @@ func maybeReportToDashboard(path string) { ...@@ -34,6 +35,7 @@ func maybeReportToDashboard(path string) {
type host struct { type host struct {
pattern *regexp.Regexp pattern *regexp.Regexp
protocol string protocol string
suffix string
} }
// a vcs represents a version control system // a vcs represents a version control system
...@@ -76,9 +78,10 @@ var hg = vcs{ ...@@ -76,9 +78,10 @@ var hg = vcs{
logReleaseFlag: "-rrelease", logReleaseFlag: "-rrelease",
check: "identify", check: "identify",
protocols: []string{"http"}, protocols: []string{"http"},
suffix: ".hg",
defaultHosts: []host{ defaultHosts: []host{
{regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/hg)(/[a-z0-9A-Z_.\-/]*)?$`), "https"}, {regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/hg)(/[a-z0-9A-Z_.\-/]*)?$`), "https", ""},
{regexp.MustCompile(`^(bitbucket\.org/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`), "http"}, {regexp.MustCompile(`^(bitbucket\.org/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`), "http", ""},
}, },
} }
...@@ -98,7 +101,7 @@ var git = vcs{ ...@@ -98,7 +101,7 @@ var git = vcs{
protocols: []string{"git", "http"}, protocols: []string{"git", "http"},
suffix: ".git", suffix: ".git",
defaultHosts: []host{ defaultHosts: []host{
{regexp.MustCompile(`^(github\.com/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`), "http"}, {regexp.MustCompile(`^(github\.com/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`), "http", ".git"},
}, },
} }
...@@ -115,8 +118,9 @@ var svn = vcs{ ...@@ -115,8 +118,9 @@ var svn = vcs{
logReleaseFlag: "release", logReleaseFlag: "release",
check: "info", check: "info",
protocols: []string{"http", "svn"}, protocols: []string{"http", "svn"},
suffix: ".svn",
defaultHosts: []host{ defaultHosts: []host{
{regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/svn)(/[a-z0-9A-Z_.\-/]*)?$`), "https"}, {regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/svn)(/[a-z0-9A-Z_.\-/]*)?$`), "https", ""},
}, },
} }
...@@ -135,13 +139,40 @@ var bzr = vcs{ ...@@ -135,13 +139,40 @@ var bzr = vcs{
logReleaseFlag: "-rrelease", logReleaseFlag: "-rrelease",
check: "info", check: "info",
protocols: []string{"http", "bzr"}, protocols: []string{"http", "bzr"},
suffix: ".bzr",
defaultHosts: []host{ defaultHosts: []host{
{regexp.MustCompile(`^(launchpad\.net/([a-z0-9A-Z_.\-]+(/[a-z0-9A-Z_.\-]+)?|~[a-z0-9A-Z_.\-]+/(\+junk|[a-z0-9A-Z_.\-]+)/[a-z0-9A-Z_.\-]+))(/[a-z0-9A-Z_.\-/]+)?$`), "https"}, {regexp.MustCompile(`^(launchpad\.net/([a-z0-9A-Z_.\-]+(/[a-z0-9A-Z_.\-]+)?|~[a-z0-9A-Z_.\-]+/(\+junk|[a-z0-9A-Z_.\-]+)/[a-z0-9A-Z_.\-]+))(/[a-z0-9A-Z_.\-/]+)?$`), "https", ""},
}, },
} }
var vcsList = []*vcs{&git, &hg, &bzr, &svn} var vcsList = []*vcs{&git, &hg, &bzr, &svn}
func (v *vcs) findRepo(prefix string) *vcsMatch {
for _, proto := range v.protocols {
for _, suffix := range []string{v.suffix, ""} {
repo := proto + "://" + prefix + suffix
out, err := exec.Command(v.cmd, v.check, repo).CombinedOutput()
if err == nil {
return &vcsMatch{v, prefix + v.suffix, repo}
}
printf("find %s: %s %s %s: %v\n%s\n", prefix, v.cmd, v.check, repo, err, out)
}
}
errorf("find %s: couldn't find %s repository\n", prefix, v.name)
return nil
}
func findRepo(pkg string) *vcsMatch {
for _, v := range vcsList {
i := strings.Index(pkg+"/", v.suffix+"/")
if i >= 0 {
return v.findRepo(pkg[:i])
}
}
return nil
}
// isRemote returns true if the first part of the package name looks like a // isRemote returns true if the first part of the package name looks like a
// hostname - i.e. contains at least one '.' and the last part is at least 2 // hostname - i.e. contains at least one '.' and the last part is at least 2
// characters. // characters.
...@@ -162,6 +193,7 @@ func download(pkg, srcDir string) os.Error { ...@@ -162,6 +193,7 @@ func download(pkg, srcDir string) os.Error {
if strings.Contains(pkg, "..") { if strings.Contains(pkg, "..") {
return os.NewError("invalid path (contains ..)") return os.NewError("invalid path (contains ..)")
} }
dashpath := pkg
var m *vcsMatch var m *vcsMatch
for _, v := range vcsList { for _, v := range vcsList {
for _, host := range v.defaultHosts { for _, host := range v.defaultHosts {
...@@ -169,15 +201,19 @@ func download(pkg, srcDir string) os.Error { ...@@ -169,15 +201,19 @@ func download(pkg, srcDir string) os.Error {
if v.suffix != "" && strings.HasSuffix(hm[1], v.suffix) { if v.suffix != "" && strings.HasSuffix(hm[1], v.suffix) {
return os.NewError("repository " + pkg + " should not have " + v.suffix + " suffix") return os.NewError("repository " + pkg + " should not have " + v.suffix + " suffix")
} }
repo := host.protocol + "://" + hm[1] + v.suffix repo := host.protocol + "://" + hm[1] + host.suffix
m = &vcsMatch{v, hm[1], repo} m = &vcsMatch{v, hm[1], repo}
} }
} }
} }
if m == nil {
m = findRepo(pkg)
dashpath = "" // don't report to dashboard
}
if m == nil { if m == nil {
return os.NewError("cannot download: " + pkg) return os.NewError("cannot download: " + pkg)
} }
return vcsCheckout(m.vcs, srcDir, m.prefix, m.repo, pkg) return vcsCheckout(m.vcs, srcDir, m.prefix, m.repo, dashpath)
} }
// Try to detect if a "release" tag exists. If it does, update // Try to detect if a "release" tag exists. If it does, update
...@@ -219,7 +255,9 @@ func vcsCheckout(vcs *vcs, srcDir, pkgprefix, repo, dashpath string) os.Error { ...@@ -219,7 +255,9 @@ func vcsCheckout(vcs *vcs, srcDir, pkgprefix, repo, dashpath string) os.Error {
return err return err
} }
// success on first installation - report // success on first installation - report
maybeReportToDashboard(dashpath) if dashpath != "" {
maybeReportToDashboard(dashpath)
}
} else if *update { } else if *update {
// Retrieve new revisions from the remote branch, if the VCS // Retrieve new revisions from the remote branch, if the VCS
// supports this operation independently (e.g. svn doesn't) // supports this operation independently (e.g. svn doesn't)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment