parent
cda7bad625
commit
cac38eedcd
19
main.go
19
main.go
@ -115,8 +115,8 @@ func (repo *Repo) GopkgVersionRoot(version Version) string {
|
||||
}
|
||||
}
|
||||
|
||||
var patternOld = regexp.MustCompile(`^/(?:([a-z0-9][-a-z0-9]+)/)?((?:v0|v[1-9][0-9]*)(?:\.0|\.[1-9][0-9]*){0,2})/([a-zA-Z][-a-zA-Z0-9]*)(?:\.git)?((?:/[a-zA-Z][-a-zA-Z0-9]*)*)$`)
|
||||
var patternNew = regexp.MustCompile(`^/(?:([a-zA-Z0-9][-a-zA-Z0-9]+)/)?([a-zA-Z][-.a-zA-Z0-9]*)\.((?:v0|v[1-9][0-9]*)(?:\.0|\.[1-9][0-9]*){0,2})(?:\.git)?((?:/[a-zA-Z0-9][-.a-zA-Z0-9]*)*)$`)
|
||||
var patternOld = regexp.MustCompile(`^/(?:([a-z0-9][-a-z0-9]+)/)?((?:v0|v[1-9][0-9]*)(?:\.0|\.[1-9][0-9]*){0,2}(-unstable)?)/([a-zA-Z][-a-zA-Z0-9]*)(?:\.git)?((?:/[a-zA-Z][-a-zA-Z0-9]*)*)$`)
|
||||
var patternNew = regexp.MustCompile(`^/(?:([a-zA-Z0-9][-a-zA-Z0-9]+)/)?([a-zA-Z][-.a-zA-Z0-9]*)\.((?:v0|v[1-9][0-9]*)(?:\.0|\.[1-9][0-9]*){0,2}(-unstable)?)(?:\.git)?((?:/[a-zA-Z0-9][-.a-zA-Z0-9]*)*)$`)
|
||||
|
||||
func handler(resp http.ResponseWriter, req *http.Request) {
|
||||
if req.URL.Path == "/health-check" {
|
||||
@ -140,6 +140,7 @@ func handler(resp http.ResponseWriter, req *http.Request) {
|
||||
sendNotFound(resp, "Unsupported URL pattern; see the documentation at gopkg.in for details.")
|
||||
return
|
||||
}
|
||||
// "/v2/name" <= "/name.v2"
|
||||
m[2], m[3] = m[3], m[2]
|
||||
oldFormat = true
|
||||
}
|
||||
@ -153,7 +154,7 @@ func handler(resp http.ResponseWriter, req *http.Request) {
|
||||
repo := &Repo{
|
||||
User: m[1],
|
||||
Name: m[2],
|
||||
SubPath: m[4],
|
||||
SubPath: m[5],
|
||||
OldFormat: oldFormat,
|
||||
}
|
||||
|
||||
@ -174,8 +175,14 @@ func handler(resp http.ResponseWriter, req *http.Request) {
|
||||
sendNotFound(resp, "GitHub repository not found at https://%s", repo.GitHubRoot())
|
||||
return
|
||||
case ErrNoVersion:
|
||||
v := repo.MajorVersion.String()
|
||||
sendNotFound(resp, `GitHub repository at https://%s has no branch or tag "%s", "%s.N" or "%s.N.M"`, repo.GitHubRoot(), v, v, v)
|
||||
major := repo.MajorVersion
|
||||
suffix := ""
|
||||
if major.Unstable {
|
||||
major.Unstable = false
|
||||
suffix = unstableSuffix
|
||||
}
|
||||
v := major.String()
|
||||
sendNotFound(resp, `GitHub repository at https://%s has no branch or tag "%s%s", "%s.N%s" or "%s.N.M%s"`, repo.GitHubRoot(), v, suffix, v, suffix, v, suffix)
|
||||
return
|
||||
default:
|
||||
resp.WriteHeader(http.StatusBadGateway)
|
||||
@ -306,7 +313,7 @@ func hackedRefs(repo *Repo) (data []byte, versions []Version, err error) {
|
||||
}
|
||||
|
||||
// If there were absolutely no versions, and v0 was requested, accept the master as-is.
|
||||
if len(versions) == 0 && repo.MajorVersion == (Version{0, -1, -1}) {
|
||||
if len(versions) == 0 && repo.MajorVersion == (Version{0, -1, -1, false}) {
|
||||
return data, nil, nil
|
||||
}
|
||||
|
||||
|
33
page.go
33
page.go
@ -124,6 +124,11 @@ const packageTemplateString = `<!DOCTYPE html>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ if .Repo.MajorVersion.Unstable }}
|
||||
<div class="col-sm-12 alert alert-danger">
|
||||
This is an <b><i>unstable</i></b> package and should <i>not</i> be used in released code.
|
||||
</div>
|
||||
{{ end }}
|
||||
<div class="row" >
|
||||
<div class="col-sm-12" >
|
||||
<a class="btn btn-lg btn-info" href="https://{{.Repo.GitHubRoot}}/tree/{{if .Repo.AllVersions}}{{.FullVersion}}{{else}}master{{end}}{{.Repo.SubPath}}" ><i class="fa fa-github"></i> Source Code</a>
|
||||
@ -153,7 +158,7 @@ const packageTemplateString = `<!DOCTYPE html>
|
||||
{{ if .LatestVersions }}
|
||||
{{ range .LatestVersions }}
|
||||
<div>
|
||||
<a href="//{{gopkgVersionRoot $.Repo .}}{{$.Repo.SubPath}}" {{if eq .Major $.Repo.MajorVersion.Major}}class="current"{{end}} >v{{.Major}}</a>
|
||||
<a href="//{{gopkgVersionRoot $.Repo .}}{{$.Repo.SubPath}}" {{if eq .Major $.Repo.MajorVersion.Major}}{{if eq .Unstable $.Repo.MajorVersion.Unstable}}class="current"{{end}}{{end}}>v{{.Major}}{{if .Unstable}}-unstable{{end}}</a>
|
||||
→
|
||||
<span class="label label-default">{{.}}</span>
|
||||
</div>
|
||||
@ -227,21 +232,33 @@ func renderPackagePage(resp http.ResponseWriter, req *http.Request, repo *Repo)
|
||||
Repo: repo,
|
||||
}
|
||||
|
||||
// calculate version mapping
|
||||
latestVersionsMap := make(map[int]Version)
|
||||
// Calculate the latest version for each major version, both stable and unstable.
|
||||
latestVersions := make(map[int]Version)
|
||||
latestUnstable := make(map[int]Version)
|
||||
for _, v := range repo.AllVersions {
|
||||
v2, exists := latestVersionsMap[v.Major]
|
||||
m := latestVersions
|
||||
if v.Unstable {
|
||||
m = latestUnstable
|
||||
}
|
||||
v2, exists := m[v.Major]
|
||||
if !exists || v2.Less(v) {
|
||||
latestVersionsMap[v.Major] = v
|
||||
m[v.Major] = v
|
||||
}
|
||||
}
|
||||
data.FullVersion = latestVersionsMap[repo.MajorVersion.Major]
|
||||
data.LatestVersions = make(VersionList, 0, len(latestVersionsMap))
|
||||
for _, v := range latestVersionsMap {
|
||||
data.LatestVersions = make(VersionList, 0, len(latestVersions))
|
||||
for _, v := range latestVersions {
|
||||
data.LatestVersions = append(data.LatestVersions, v)
|
||||
}
|
||||
sort.Sort(sort.Reverse(data.LatestVersions))
|
||||
|
||||
if repo.MajorVersion.Unstable {
|
||||
data.FullVersion = latestUnstable[repo.MajorVersion.Major]
|
||||
// Prepend post-sorting so it's show first.
|
||||
data.LatestVersions = append([]Version{data.FullVersion}, data.LatestVersions...)
|
||||
} else {
|
||||
data.FullVersion = latestVersions[repo.MajorVersion.Major]
|
||||
}
|
||||
|
||||
var dataMutex sync.Mutex
|
||||
wantResps := 2
|
||||
gotResp := make(chan bool, wantResps)
|
||||
|
106
version.go
106
version.go
@ -7,20 +7,29 @@ import (
|
||||
// Version represents a version number.
|
||||
// An element that is not present is represented as -1.
|
||||
type Version struct {
|
||||
Major, Minor, Patch int
|
||||
Major int
|
||||
Minor int
|
||||
Patch int
|
||||
Unstable bool
|
||||
}
|
||||
|
||||
const unstableSuffix = "-unstable"
|
||||
|
||||
func (v Version) String() string {
|
||||
if v.Major < 0 {
|
||||
panic(fmt.Sprintf("cannot stringify invalid version (major is %d)", v.Major))
|
||||
}
|
||||
suffix := ""
|
||||
if v.Unstable {
|
||||
suffix = unstableSuffix
|
||||
}
|
||||
if v.Minor < 0 {
|
||||
return fmt.Sprintf("v%d", v.Major)
|
||||
return fmt.Sprintf("v%d%s", v.Major, suffix)
|
||||
}
|
||||
if v.Patch < 0 {
|
||||
return fmt.Sprintf("v%d.%d", v.Major, v.Minor)
|
||||
return fmt.Sprintf("v%d.%d%s", v.Major, v.Minor, suffix)
|
||||
}
|
||||
return fmt.Sprintf("v%d.%d.%d", v.Major, v.Minor, v.Patch)
|
||||
return fmt.Sprintf("v%d.%d.%d%s", v.Major, v.Minor, v.Patch, suffix)
|
||||
}
|
||||
|
||||
// Less returns whether v is less than other.
|
||||
@ -31,7 +40,10 @@ func (v Version) Less(other Version) bool {
|
||||
if v.Minor != other.Minor {
|
||||
return v.Minor < other.Minor
|
||||
}
|
||||
return v.Patch < other.Patch
|
||||
if v.Patch != other.Patch {
|
||||
return v.Patch < other.Patch
|
||||
}
|
||||
return v.Unstable && !other.Unstable
|
||||
}
|
||||
|
||||
// Contains returns whether version v contains version other.
|
||||
@ -40,7 +52,13 @@ func (v Version) Less(other Version) bool {
|
||||
//
|
||||
// For example, Version{1, 1, -1} contains both Version{1, 1, -1} and Version{1, 1, 2},
|
||||
// but not Version{1, -1, -1} or Version{1, 2, -1}.
|
||||
//
|
||||
// Unstable versions (-unstable) only contain unstable versions, and stable
|
||||
// versions only contain stable versions.
|
||||
func (v Version) Contains(other Version) bool {
|
||||
if v.Unstable != other.Unstable {
|
||||
return false
|
||||
}
|
||||
if v.Patch != -1 {
|
||||
return v == other
|
||||
}
|
||||
@ -55,61 +73,65 @@ func (v Version) IsValid() bool {
|
||||
}
|
||||
|
||||
// InvalidVersion represents a version that can't be parsed.
|
||||
var InvalidVersion = Version{-1, -1, -1}
|
||||
var InvalidVersion = Version{-1, -1, -1, false}
|
||||
|
||||
func parseVersion(s string) (Version, bool) {
|
||||
func parseVersion(s string) (v Version, ok bool) {
|
||||
v = InvalidVersion
|
||||
if len(s) < 2 {
|
||||
return InvalidVersion, false
|
||||
return
|
||||
}
|
||||
if s[0] != 'v' {
|
||||
return InvalidVersion, false
|
||||
return
|
||||
}
|
||||
v := Version{-1, -1, -1}
|
||||
vout := InvalidVersion
|
||||
unstable := false
|
||||
i := 1
|
||||
v.Major, i = parseVersionPart(s, i)
|
||||
if i < 0 {
|
||||
return InvalidVersion, false
|
||||
for _, vptr := range []*int{&vout.Major, &vout.Minor, &vout.Patch} {
|
||||
*vptr, unstable, i = parseVersionPart(s, i)
|
||||
if i < 0 {
|
||||
return
|
||||
}
|
||||
if i == len(s) {
|
||||
vout.Unstable = unstable
|
||||
return vout, true
|
||||
}
|
||||
}
|
||||
if i == len(s) {
|
||||
return v, true
|
||||
}
|
||||
v.Minor, i = parseVersionPart(s, i)
|
||||
if i < 0 {
|
||||
return InvalidVersion, false
|
||||
}
|
||||
if i == len(s) {
|
||||
return v, true
|
||||
}
|
||||
v.Patch, i = parseVersionPart(s, i)
|
||||
if i < 0 || i < len(s) {
|
||||
return InvalidVersion, false
|
||||
}
|
||||
return v, true
|
||||
return
|
||||
}
|
||||
|
||||
func parseVersionPart(s string, i int) (part int, newi int) {
|
||||
dot := i
|
||||
for dot < len(s) && s[dot] != '.' {
|
||||
dot++
|
||||
func parseVersionPart(s string, i int) (part int, unstable bool, newi int) {
|
||||
j := i
|
||||
for j < len(s) && s[j] != '.' && s[j] != '-' {
|
||||
j++
|
||||
}
|
||||
if dot == i || dot-i > 1 && s[i] == '0' {
|
||||
return -1, -1
|
||||
if j == i || j-i > 1 && s[i] == '0' {
|
||||
return -1, false, -1
|
||||
}
|
||||
for i < len(s) {
|
||||
if s[i] < '0' || s[i] > '9' {
|
||||
return -1, -1
|
||||
c := s[i]
|
||||
for {
|
||||
if c < '0' || c > '9' {
|
||||
return -1, false, -1
|
||||
}
|
||||
part *= 10
|
||||
part += int(s[i] - '0')
|
||||
part += int(c - '0')
|
||||
if part < 0 {
|
||||
return -1, -1
|
||||
return -1, false, -1
|
||||
}
|
||||
i++
|
||||
if i+1 < len(s) && s[i] == '.' {
|
||||
return part, i + 1
|
||||
if i == len(s) {
|
||||
return part, false, i
|
||||
}
|
||||
c = s[i]
|
||||
if i+1 < len(s) {
|
||||
if c == '.' {
|
||||
return part, false, i + 1
|
||||
}
|
||||
if c == '-' && s[i:] == unstableSuffix {
|
||||
return part, true, i + len(unstableSuffix)
|
||||
}
|
||||
}
|
||||
}
|
||||
return part, i
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
// VersionList implements sort.Interface
|
||||
|
105
version_test.go
105
version_test.go
@ -13,31 +13,35 @@ var _ = Suite(&VersionSuite{})
|
||||
type VersionSuite struct{}
|
||||
|
||||
var versionParseTests = []struct {
|
||||
major, minor, patch int
|
||||
s string
|
||||
major int
|
||||
minor int
|
||||
patch int
|
||||
dev bool
|
||||
s string
|
||||
}{
|
||||
{-1, -1, -1, "v"},
|
||||
{-1, -1, -1, "v-1"},
|
||||
{-1, -1, -1, "v01"},
|
||||
{-1, -1, -1, "v1.01"},
|
||||
{-1, -1, -1, "a1"},
|
||||
{-1, -1, -1, "v1a"},
|
||||
{-1, -1, -1, "v1..2"},
|
||||
{-1, -1, -1, "v1.2.3.4"},
|
||||
{-1, -1, -1, "v1."},
|
||||
{-1, -1, -1, "v1.2."},
|
||||
{-1, -1, -1, "v1.2.3."},
|
||||
{-1, -1, -1, false, "v"},
|
||||
{-1, -1, -1, false, "v-1"},
|
||||
{-1, -1, -1, false, "v-deb"},
|
||||
{-1, -1, -1, false, "v01"},
|
||||
{-1, -1, -1, false, "v1.01"},
|
||||
{-1, -1, -1, false, "a1"},
|
||||
{-1, -1, -1, false, "v1a"},
|
||||
{-1, -1, -1, false, "v1..2"},
|
||||
{-1, -1, -1, false, "v1.2.3.4"},
|
||||
{-1, -1, -1, false, "v1."},
|
||||
{-1, -1, -1, false, "v1.2."},
|
||||
{-1, -1, -1, false, "v1.2.3."},
|
||||
|
||||
{0, -1, -1,
|
||||
"v0"},
|
||||
{1, -1, -1,
|
||||
"v1"},
|
||||
{1, 2, -1,
|
||||
"v1.2"},
|
||||
{1, 2, 3,
|
||||
"v1.2.3"},
|
||||
{12, 34, 56,
|
||||
"v12.34.56"},
|
||||
{0, -1, -1, false, "v0"},
|
||||
{0, -1, -1, true, "v0-unstable"},
|
||||
{1, -1, -1, false, "v1"},
|
||||
{1, -1, -1, true, "v1-unstable"},
|
||||
{1, 2, -1, false, "v1.2"},
|
||||
{1, 2, -1, true, "v1.2-unstable"},
|
||||
{1, 2, 3, false, "v1.2.3"},
|
||||
{1, 2, 3, true, "v1.2.3-unstable"},
|
||||
{12, 34, 56, false, "v12.34.56"},
|
||||
{12, 34, 56, true, "v12.34.56-unstable"},
|
||||
}
|
||||
|
||||
func (s *VersionSuite) TestParse(c *C) {
|
||||
@ -48,7 +52,7 @@ func (s *VersionSuite) TestParse(c *C) {
|
||||
c.Fatalf("version %q is invalid but parsed as %#v", t.s, got)
|
||||
}
|
||||
} else {
|
||||
want := Version{t.major, t.minor, t.patch}
|
||||
want := Version{t.major, t.minor, t.patch, t.dev}
|
||||
if got != want {
|
||||
c.Fatalf("version %q must parse as %#v, got %#v", t.s, want, got)
|
||||
}
|
||||
@ -61,24 +65,29 @@ func (s *VersionSuite) TestParse(c *C) {
|
||||
|
||||
var versionLessTests = []struct {
|
||||
oneMajor, oneMinor, onePatch int
|
||||
oneUnstable bool
|
||||
twoMajor, twoMinor, twoPatch int
|
||||
less bool
|
||||
twoUnstable, less bool
|
||||
}{
|
||||
{0, 0, 0, 0, 0, 0, false},
|
||||
{1, 0, 0, 1, 0, 0, false},
|
||||
{1, 0, 0, 1, 1, 0, true},
|
||||
{1, 0, 0, 2, 0, 0, true},
|
||||
{0, 1, 0, 0, 1, 0, false},
|
||||
{0, 1, 0, 0, 1, 1, true},
|
||||
{0, 0, 0, 0, 2, 0, true},
|
||||
{0, 0, 1, 0, 0, 1, false},
|
||||
{0, 0, 1, 0, 0, 2, true},
|
||||
{0, 0, 0, false, 0, 0, 0, false, false},
|
||||
{1, 0, 0, false, 1, 0, 0, false, false},
|
||||
{1, 0, 0, false, 1, 1, 0, false, true},
|
||||
{1, 0, 0, false, 2, 0, 0, false, true},
|
||||
{0, 1, 0, false, 0, 1, 0, false, false},
|
||||
{0, 1, 0, false, 0, 1, 1, false, true},
|
||||
{0, 0, 0, false, 0, 2, 0, false, true},
|
||||
{0, 0, 1, false, 0, 0, 1, false, false},
|
||||
{0, 0, 1, false, 0, 0, 2, false, true},
|
||||
|
||||
{0, 0, 0, false, 0, 0, 0, true, false},
|
||||
{0, 0, 0, true, 0, 0, 0, false, true},
|
||||
{0, 0, 1, true, 0, 0, 0, false, false},
|
||||
}
|
||||
|
||||
func (s *VersionSuite) TestLess(c *C) {
|
||||
for _, t := range versionLessTests {
|
||||
one := Version{t.oneMajor, t.oneMinor, t.onePatch}
|
||||
two := Version{t.twoMajor, t.twoMinor, t.twoPatch}
|
||||
one := Version{t.oneMajor, t.oneMinor, t.onePatch, t.oneUnstable}
|
||||
two := Version{t.twoMajor, t.twoMinor, t.twoPatch, t.twoUnstable}
|
||||
if one.Less(two) != t.less {
|
||||
c.Fatalf("version %s < %s returned %v", one, two, !t.less)
|
||||
}
|
||||
@ -87,21 +96,25 @@ func (s *VersionSuite) TestLess(c *C) {
|
||||
|
||||
var versionContainsTests = []struct {
|
||||
oneMajor, oneMinor, onePatch int
|
||||
oneUnstable bool
|
||||
twoMajor, twoMinor, twoPatch int
|
||||
contains bool
|
||||
twoUnstable, contains bool
|
||||
}{
|
||||
{12, 34, 56, 12, 34, 56, true},
|
||||
{12, 34, 56, 12, 34, 78, false},
|
||||
{12, 34, -1, 12, 34, 56, true},
|
||||
{12, 34, -1, 12, 78, 56, false},
|
||||
{12, -1, -1, 12, 34, 56, true},
|
||||
{12, -1, -1, 78, 34, 56, false},
|
||||
{12, 34, 56, false, 12, 34, 56, false, true},
|
||||
{12, 34, 56, false, 12, 34, 78, false, false},
|
||||
{12, 34, -1, false, 12, 34, 56, false, true},
|
||||
{12, 34, -1, false, 12, 78, 56, false, false},
|
||||
{12, -1, -1, false, 12, 34, 56, false, true},
|
||||
{12, -1, -1, false, 78, 34, 56, false, false},
|
||||
|
||||
{12, -1, -1, true, 12, -1, -1, false, false},
|
||||
{12, -1, -1, false, 12, -1, -1, true, false},
|
||||
}
|
||||
|
||||
func (s *VersionSuite) TestContains(c *C) {
|
||||
for _, t := range versionContainsTests {
|
||||
one := Version{t.oneMajor, t.oneMinor, t.onePatch}
|
||||
two := Version{t.twoMajor, t.twoMinor, t.twoPatch}
|
||||
one := Version{t.oneMajor, t.oneMinor, t.onePatch, t.oneUnstable}
|
||||
two := Version{t.twoMajor, t.twoMinor, t.twoPatch, t.twoUnstable}
|
||||
if one.Contains(two) != t.contains {
|
||||
c.Fatalf("version %s.Contains(%s) returned %v", one, two, !t.contains)
|
||||
}
|
||||
@ -110,5 +123,5 @@ func (s *VersionSuite) TestContains(c *C) {
|
||||
|
||||
func (s *VersionSuite) TestIsValid(c *C) {
|
||||
c.Assert(InvalidVersion.IsValid(), Equals, false)
|
||||
c.Assert(Version{0, 0, 0}.IsValid(), Equals, true)
|
||||
c.Assert(Version{0, 0, 0, false}.IsValid(), Equals, true)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user