Commit 58b05e24 authored by Andrew Gerrand's avatar Andrew Gerrand

goinstall: select the tag that is closest to runtime.Version

release.r50 looks for newest tag <= go.r50
weekly.2010-10-10 looks for newest tag <= go.2010-10-10

Implements behavior for hg, git, and bzr.

R=dsymonds, rsc, n13m3y3r
CC=golang-dev
https://golang.org/cl/4873057
parent 596bf61d
...@@ -11,3 +11,9 @@ GOFILES=\ ...@@ -11,3 +11,9 @@ GOFILES=\
make.go\ make.go\
include ../../Make.cmd include ../../Make.cmd
test:
gotest
testshort:
gotest -test.short
...@@ -94,8 +94,11 @@ attempt to fetch updates. The -u flag changes this behavior, ...@@ -94,8 +94,11 @@ attempt to fetch updates. The -u flag changes this behavior,
causing goinstall to update all remote packages encountered during causing goinstall to update all remote packages encountered during
the installation. the installation.
When downloading or updating, goinstall first looks for a tag or branch When downloading or updating, goinstall looks for a tag with the "go." prefix
named "release". If there is one, it uses that version of the code. that corresponds to the local Go version. For Go "release.r58" it looks for a
tag named "go.r58". For "weekly.2011-06-03" it looks for "go.weekly.2011-06-03".
If the specific "go.X" tag is not found, it chooses the closest earlier version.
If an appropriate tag is found, goinstall uses that version of the code.
Otherwise it uses the default version selected by the version control Otherwise it uses the default version selected by the version control
system, typically HEAD for git, tip for Mercurial. system, typically HEAD for git, tip for Mercurial.
......
...@@ -7,12 +7,15 @@ ...@@ -7,12 +7,15 @@
package main package main
import ( import (
"bytes"
"exec" "exec"
"fmt" "fmt"
"http" "http"
"os" "os"
"path/filepath" "path/filepath"
"regexp" "regexp"
"runtime"
"strconv"
"strings" "strings"
) )
...@@ -36,22 +39,21 @@ func maybeReportToDashboard(path string) { ...@@ -36,22 +39,21 @@ func maybeReportToDashboard(path string) {
// a vcs represents a version control system // a vcs represents a version control system
// like Mercurial, Git, or Subversion. // like Mercurial, Git, or Subversion.
type vcs struct { type vcs struct {
name string name string
cmd string cmd string
metadir string metadir string
checkout string checkout string
clone string clone string
update string update string
updateReleaseFlag string updateRevFlag string
pull string pull string
pullForceFlag string pullForceFlag string
log string tagList string
logLimitFlag string tagListRe *regexp.Regexp
logReleaseFlag string check string
check string protocols []string
protocols []string suffix string
suffix string defaultHosts []host
defaultHosts []host
} }
type host struct { type host struct {
...@@ -61,20 +63,18 @@ type host struct { ...@@ -61,20 +63,18 @@ type host struct {
} }
var hg = vcs{ var hg = vcs{
name: "Mercurial", name: "Mercurial",
cmd: "hg", cmd: "hg",
metadir: ".hg", metadir: ".hg",
checkout: "checkout", checkout: "checkout",
clone: "clone", clone: "clone",
update: "update", update: "update",
updateReleaseFlag: "release", pull: "pull",
pull: "pull", tagList: "tags",
log: "log", tagListRe: regexp.MustCompile("([^ ]+)[^\n]+\n"),
logLimitFlag: "-l1", check: "identify",
logReleaseFlag: "-rrelease", protocols: []string{"https", "http"},
check: "identify", suffix: ".hg",
protocols: []string{"https", "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", ""},
...@@ -82,20 +82,18 @@ var hg = vcs{ ...@@ -82,20 +82,18 @@ var hg = vcs{
} }
var git = vcs{ var git = vcs{
name: "Git", name: "Git",
cmd: "git", cmd: "git",
metadir: ".git", metadir: ".git",
checkout: "checkout", checkout: "checkout",
clone: "clone", clone: "clone",
update: "pull", update: "pull",
updateReleaseFlag: "release", pull: "fetch",
pull: "fetch", tagList: "tag",
log: "show-ref", tagListRe: regexp.MustCompile("([^\n]+)\n"),
logLimitFlag: "", check: "ls-remote",
logReleaseFlag: "release", protocols: []string{"git", "https", "http"},
check: "ls-remote", suffix: ".git",
protocols: []string{"git", "https", "http"},
suffix: ".git",
defaultHosts: []host{ defaultHosts: []host{
{regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/git)(/[a-z0-9A-Z_.\-/]*)?$`), "https", ""}, {regexp.MustCompile(`^([a-z0-9\-]+\.googlecode\.com/git)(/[a-z0-9A-Z_.\-/]*)?$`), "https", ""},
{regexp.MustCompile(`^(github\.com/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`), "http", ".git"}, {regexp.MustCompile(`^(github\.com/[a-z0-9A-Z_.\-]+/[a-z0-9A-Z_.\-]+)(/[a-z0-9A-Z_.\-/]*)?$`), "http", ".git"},
...@@ -103,40 +101,35 @@ var git = vcs{ ...@@ -103,40 +101,35 @@ var git = vcs{
} }
var svn = vcs{ var svn = vcs{
name: "Subversion", name: "Subversion",
cmd: "svn", cmd: "svn",
metadir: ".svn", metadir: ".svn",
checkout: "checkout", checkout: "checkout",
clone: "checkout", clone: "checkout",
update: "update", update: "update",
updateReleaseFlag: "release", check: "info",
log: "log", protocols: []string{"https", "http", "svn"},
logLimitFlag: "-l1", suffix: ".svn",
logReleaseFlag: "release",
check: "info",
protocols: []string{"https", "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", ""},
}, },
} }
var bzr = vcs{ var bzr = vcs{
name: "Bazaar", name: "Bazaar",
cmd: "bzr", cmd: "bzr",
metadir: ".bzr", metadir: ".bzr",
checkout: "update", checkout: "update",
clone: "branch", clone: "branch",
update: "update", update: "update",
updateReleaseFlag: "-rrelease", updateRevFlag: "-r",
pull: "pull", pull: "pull",
pullForceFlag: "--overwrite", pullForceFlag: "--overwrite",
log: "log", tagList: "tags",
logLimitFlag: "-l1", tagListRe: regexp.MustCompile("([^ ]+)[^\n]+\n"),
logReleaseFlag: "-rrelease", check: "info",
check: "info", protocols: []string{"https", "http", "bzr"},
protocols: []string{"https", "http", "bzr"}, suffix: ".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", ""},
}, },
...@@ -240,20 +233,84 @@ func download(pkg, srcDir string) (public bool, err os.Error) { ...@@ -240,20 +233,84 @@ func download(pkg, srcDir string) (public bool, err os.Error) {
return return
} }
// Try to detect if a "release" tag exists. If it does, update // updateRepo gets a list of tags in the repository and
// to the tagged version, otherwise just update the current branch. // checks out the tag closest to the current runtime.Version.
// NOTE(_nil): svn will always fail because it is trying to get // If no matching tag is found, it just updates to tip.
// the revision history of a file named "release" instead of
// looking for a commit with a release tag
func (v *vcs) updateRepo(dst string) os.Error { func (v *vcs) updateRepo(dst string) os.Error {
if err := quietRun(dst, nil, v.cmd, v.log, v.logLimitFlag, v.logReleaseFlag); err == nil { if v.tagList == "" || v.tagListRe == nil {
if err := run(dst, nil, v.cmd, v.checkout, v.updateReleaseFlag); err != nil { // TODO(adg): fix for svn
return err return run(dst, nil, v.cmd, v.update)
} }
} else if err := run(dst, nil, v.cmd, v.update); err != nil {
// Get tag list.
stderr := new(bytes.Buffer)
cmd := exec.Command(v.cmd, v.tagList)
cmd.Dir = dst
cmd.Stderr = stderr
b, err := cmd.Output()
if err != nil {
errorf("%s %s: %s\n", v.cmd, v.tagList, stderr)
return err return err
} }
return nil var tags []string
for _, m := range v.tagListRe.FindAllStringSubmatch(string(b), -1) {
tags = append(tags, m[1])
}
// Only use the tag component of runtime.Version.
ver := strings.Split(runtime.Version(), " ")[0]
// Select tag.
if tag := selectTag(ver, tags); tag != "" {
printf("selecting revision %q\n", tag)
return run(dst, nil, v.cmd, v.checkout, v.updateRevFlag+tag)
}
// No matching tag found, make default selection.
printf("selecting tip\n")
return run(dst, nil, v.cmd, v.update)
}
// selectTag returns the closest matching tag for a given version.
// Closest means the latest one that is not after the current release.
// Version "release.rN" matches tags of the form "go.rN" (N being a decimal).
// Version "weekly.YYYY-MM-DD" matches tags like "go.weekly.YYYY-MM-DD".
func selectTag(goVersion string, tags []string) (match string) {
const rPrefix = "release.r"
if strings.HasPrefix(goVersion, rPrefix) {
p := "go.r"
v, err := strconv.Atof64(goVersion[len(rPrefix):])
if err != nil {
return ""
}
var matchf float64
for _, t := range tags {
if !strings.HasPrefix(t, p) {
continue
}
tf, err := strconv.Atof64(t[len(p):])
if err != nil {
continue
}
if matchf < tf && tf <= v {
match, matchf = t, tf
}
}
}
const wPrefix = "weekly."
if strings.HasPrefix(goVersion, wPrefix) {
p := "go.weekly."
v := goVersion[len(wPrefix):]
for _, t := range tags {
if !strings.HasPrefix(t, p) {
continue
}
if match < t && t[len(p):] <= v {
match = t
}
}
}
return match
} }
// checkoutRepo checks out repo into dst using vcs. // checkoutRepo checks out repo into dst using vcs.
......
// Copyright 2011 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 main
import "testing"
var selectTagTestTags = []string{
"go.r58",
"go.r58.1",
"go.r59",
"go.r59.1",
"go.r61",
"go.r61.1",
"go.weekly.2010-01-02",
"go.weekly.2011-10-12",
"go.weekly.2011-10-12.1",
"go.weekly.2011-10-14",
"go.weekly.2011-11-01",
// these should be ignored:
"release.r59",
"release.r59.1",
"release",
"weekly.2011-10-12",
"weekly.2011-10-12.1",
"weekly",
"foo",
"bar",
"go.f00",
"go!r60",
"go.1999-01-01",
}
var selectTagTests = []struct {
version string
selected string
}{
{"release.r57", ""},
{"release.r58.2", "go.r58.1"},
{"release.r59", "go.r59"},
{"release.r59.1", "go.r59.1"},
{"release.r60", "go.r59.1"},
{"release.r60.1", "go.r59.1"},
{"release.r61", "go.r61"},
{"release.r66", "go.r61.1"},
{"weekly.2010-01-01", ""},
{"weekly.2010-01-02", "go.weekly.2010-01-02"},
{"weekly.2010-01-02.1", "go.weekly.2010-01-02"},
{"weekly.2010-01-03", "go.weekly.2010-01-02"},
{"weekly.2011-10-12", "go.weekly.2011-10-12"},
{"weekly.2011-10-12.1", "go.weekly.2011-10-12.1"},
{"weekly.2011-10-13", "go.weekly.2011-10-12.1"},
{"weekly.2011-10-14", "go.weekly.2011-10-14"},
{"weekly.2011-10-14.1", "go.weekly.2011-10-14"},
{"weekly.2011-11-01", "go.weekly.2011-11-01"},
{"weekly.2014-01-01", "go.weekly.2011-11-01"},
{"weekly.3000-01-01", "go.weekly.2011-11-01"},
// faulty versions:
{"release.f00", ""},
{"weekly.1999-01-01", ""},
{"junk", ""},
{"", ""},
}
func TestSelectTag(t *testing.T) {
for _, c := range selectTagTests {
selected := selectTag(c.version, selectTagTestTags)
if selected != c.selected {
t.Errorf("selectTag(%q) = %q, want %q", c.version, selected, c.selected)
}
}
}
...@@ -218,7 +218,6 @@ NOTEST+=\ ...@@ -218,7 +218,6 @@ NOTEST+=\
../cmd/cgo\ ../cmd/cgo\
../cmd/ebnflint\ ../cmd/ebnflint\
../cmd/godoc\ ../cmd/godoc\
../cmd/goinstall\
../cmd/gotest\ ../cmd/gotest\
../cmd/goyacc\ ../cmd/goyacc\
../cmd/hgpatch\ ../cmd/hgpatch\
......
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