├── gopkgvc.png ├── config.json ├── .gitignore ├── README.md ├── LICENSE ├── main.go ├── version.go ├── version_test.go ├── refs_test.go ├── page.go └── handler.go /gopkgvc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bjdgyc/gopkgvc/HEAD/gopkgvc.png -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "addr" : ":8080", 3 | "gopkg_url":"http://mygopkg.com", 4 | "vcs_url": "http://mygitlab.com", 5 | "vcs_auth_user":"gitlab_user", 6 | "vcs_auth_pass":"gitlab_pass" 7 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | 7 | # Test binary, build with `go test -c` 8 | *.test 9 | 10 | # Output of the go coverage tool, specifically when used with LiteIDE 11 | *.out 12 | 13 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 14 | .glide/ 15 | 16 | #goland 17 | .idea 18 | 19 | #program 20 | gopkgvc -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gopkgvc 2 | 3 | ## Introduction 4 | gopkgvc 5 | go包的版本管理工具,基于 [http://gopkg.in](http://gopkg.in) 开发。 6 | 主要用于企业内部包管理。现支持 (gitlab)等仓库的版本管理。 7 | 8 | # Screenshot 9 |  10 | 11 | ## TODO 12 | * 该程序仅实现了 `http` 协议,如需要 `https` 功能,需结合 `nginx` 等代理工具实现。 13 | * 该程序版本控制是基于 项目的 `tag` 或者 `branch` 实现的 14 | * 程序版本应严格按照 `语义化版本` 写法 [http://semver.org/lang/zh-CN/](http://semver.org/lang/zh-CN/) 15 | 16 | ## Installation 17 | 18 | `go get github.com/bjdgyc/gopkgvc` 19 | 20 | ## Json config 21 | 22 | ``` json 23 | 24 | { 25 | "addr" : ":8080", //程序监听地址 26 | "gopkg_url":"http://mygopkg.com", //包管理地址名 27 | "vcs_url": "http://mygitlab.com", //gitlab等仓库地址 28 | "vcs_auth_user":"gitlab_user", //gitlab用户名 29 | "vcs_auth_pass":"gitlab_pass" //gitlab密码 30 | } 31 | 32 | 33 | ``` 34 | 35 | ## Start 36 | 37 | `go build && ./gopkgvc -c ./config.json` 38 | 39 | 40 | ## Use 41 | 42 | 命令行执行 `go get -insecure mygopkg.com/user/project` 下载对应的包 43 | 44 | 请使用浏览器打开 `http://mygopkg.com/v/user/project.v1` 根据页面操作即可 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "flag" 6 | "fmt" 7 | "io/ioutil" 8 | "net/http" 9 | "net/url" 10 | "os" 11 | "time" 12 | "log" 13 | ) 14 | 15 | var ( 16 | configFile = flag.String("c", "./config.json", "The config file") 17 | ) 18 | 19 | var httpServer = &http.Server{ 20 | ReadTimeout: 30 * time.Second, 21 | WriteTimeout: 30 * time.Second, 22 | } 23 | 24 | var httpClient = &http.Client{ 25 | Timeout: 20 * time.Second, 26 | } 27 | 28 | type Config struct { 29 | //监听地址 30 | Addr string `json:"addr"` 31 | //gopkg服务地址 32 | GopkgUrl string `json:"gopkg_url"` 33 | //版本控制服务器地址 34 | VCSUrl string `json:"vcs_url"` 35 | //用户名 36 | VCSAuthUser string `json:"vcs_auth_user"` 37 | //密码 38 | VCSAuthPass string `json:"vcs_auth_pass"` 39 | //是否需要授权验证 40 | vcsNeedAuth bool 41 | //gopkg域名 42 | GopkgHost string 43 | //http协议 44 | GopkgScheme string 45 | VCSHost string 46 | } 47 | 48 | var config = Config{} 49 | 50 | func main() { 51 | log.SetFlags(log.LstdFlags | log.Lshortfile) 52 | flag.Parse() 53 | data, err := ioutil.ReadFile(*configFile) 54 | if err != nil { 55 | panic(err) 56 | } 57 | err = json.Unmarshal(data, &config) 58 | if err != nil { 59 | panic(err) 60 | } 61 | 62 | parseUrl, err := url.Parse(config.GopkgUrl) 63 | if err != nil { 64 | panic(err) 65 | } 66 | 67 | config.GopkgHost = parseUrl.Host 68 | config.GopkgScheme = parseUrl.Scheme 69 | 70 | pu, _ := url.Parse(config.VCSUrl) 71 | config.VCSHost = pu.Host 72 | 73 | if config.VCSAuthUser != "" && config.VCSAuthPass != "" { 74 | config.vcsNeedAuth = true 75 | } 76 | 77 | http.HandleFunc("/", handler) 78 | httpServer.Addr = config.Addr 79 | 80 | fmt.Println("http start " + config.Addr) 81 | err = httpServer.ListenAndServe() 82 | if err != nil { 83 | fmt.Fprintf(os.Stderr, "error: %v\n", err) 84 | os.Exit(1) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /version.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // Version represents a version number. 8 | // An element that is not present is represented as -1. 9 | type Version struct { 10 | Major int 11 | Minor int 12 | Patch int 13 | Unstable bool 14 | } 15 | 16 | const unstableSuffix = "-unstable" 17 | 18 | func (v Version) String() string { 19 | if v.Major < 0 { 20 | panic(fmt.Sprintf("cannot stringify invalid version (major is %d)", v.Major)) 21 | } 22 | suffix := "" 23 | if v.Unstable { 24 | suffix = unstableSuffix 25 | } 26 | if v.Minor < 0 { 27 | return fmt.Sprintf("v%d%s", v.Major, suffix) 28 | } 29 | if v.Patch < 0 { 30 | return fmt.Sprintf("v%d.%d%s", v.Major, v.Minor, suffix) 31 | } 32 | return fmt.Sprintf("v%d.%d.%d%s", v.Major, v.Minor, v.Patch, suffix) 33 | } 34 | 35 | // Less returns whether v is less than other. 36 | func (v Version) Less(other Version) bool { 37 | if v.Major != other.Major { 38 | return v.Major < other.Major 39 | } 40 | if v.Minor != other.Minor { 41 | return v.Minor < other.Minor 42 | } 43 | if v.Patch != other.Patch { 44 | return v.Patch < other.Patch 45 | } 46 | return v.Unstable && !other.Unstable 47 | } 48 | 49 | // Contains returns whether version v contains version other. 50 | // Version v is defined to contain version other when they both have the same Major 51 | // version and v.Minor and v.Patch are either undefined or are equal to other's. 52 | // 53 | // For example, Version{1, 1, -1} contains both Version{1, 1, -1} and Version{1, 1, 2}, 54 | // but not Version{1, -1, -1} or Version{1, 2, -1}. 55 | // 56 | // Unstable versions (-unstable) only contain unstable versions, and stable 57 | // versions only contain stable versions. 58 | func (v Version) Contains(other Version) bool { 59 | if v.Unstable != other.Unstable { 60 | return false 61 | } 62 | if v.Patch != -1 { 63 | return v == other 64 | } 65 | if v.Minor != -1 { 66 | return v.Major == other.Major && v.Minor == other.Minor 67 | } 68 | return v.Major == other.Major 69 | } 70 | 71 | func (v Version) IsValid() bool { 72 | return v != InvalidVersion 73 | } 74 | 75 | // InvalidVersion represents a version that can't be parsed. 76 | var InvalidVersion = Version{-1, -1, -1, false} 77 | 78 | func parseVersion(s string) (v Version, ok bool) { 79 | v = InvalidVersion 80 | if len(s) < 2 { 81 | return 82 | } 83 | if s[0] != 'v' { 84 | return 85 | } 86 | vout := InvalidVersion 87 | unstable := false 88 | i := 1 89 | for _, vptr := range []*int{&vout.Major, &vout.Minor, &vout.Patch} { 90 | *vptr, unstable, i = parseVersionPart(s, i) 91 | if i < 0 { 92 | return 93 | } 94 | if i == len(s) { 95 | vout.Unstable = unstable 96 | return vout, true 97 | } 98 | } 99 | return 100 | } 101 | 102 | func parseVersionPart(s string, i int) (part int, unstable bool, newi int) { 103 | j := i 104 | for j < len(s) && s[j] != '.' && s[j] != '-' { 105 | j++ 106 | } 107 | if j == i || j-i > 1 && s[i] == '0' { 108 | return -1, false, -1 109 | } 110 | c := s[i] 111 | for { 112 | if c < '0' || c > '9' { 113 | return -1, false, -1 114 | } 115 | part *= 10 116 | part += int(c - '0') 117 | if part < 0 { 118 | return -1, false, -1 119 | } 120 | i++ 121 | if i == len(s) { 122 | return part, false, i 123 | } 124 | c = s[i] 125 | if i+1 < len(s) { 126 | if c == '.' { 127 | return part, false, i + 1 128 | } 129 | if c == '-' && s[i:] == unstableSuffix { 130 | return part, true, i + len(unstableSuffix) 131 | } 132 | } 133 | } 134 | panic("unreachable") 135 | } 136 | 137 | // VersionList implements sort.Interface 138 | type VersionList []Version 139 | 140 | func (vl VersionList) Len() int { return len(vl) } 141 | func (vl VersionList) Less(i, j int) bool { return vl[i].Less(vl[j]) } 142 | func (vl VersionList) Swap(i, j int) { vl[i], vl[j] = vl[j], vl[i] } 143 | -------------------------------------------------------------------------------- /version_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | 6 | . "gopkg.in/check.v1" 7 | ) 8 | 9 | func Test(t *testing.T) { TestingT(t) } 10 | 11 | var _ = Suite(&VersionSuite{}) 12 | 13 | type VersionSuite struct{} 14 | 15 | var versionParseTests = []struct { 16 | major int 17 | minor int 18 | patch int 19 | dev bool 20 | s string 21 | }{ 22 | {-1, -1, -1, false, "v"}, 23 | {-1, -1, -1, false, "v-1"}, 24 | {-1, -1, -1, false, "v-deb"}, 25 | {-1, -1, -1, false, "v01"}, 26 | {-1, -1, -1, false, "v1.01"}, 27 | {-1, -1, -1, false, "a1"}, 28 | {-1, -1, -1, false, "v1a"}, 29 | {-1, -1, -1, false, "v1..2"}, 30 | {-1, -1, -1, false, "v1.2.3.4"}, 31 | {-1, -1, -1, false, "v1."}, 32 | {-1, -1, -1, false, "v1.2."}, 33 | {-1, -1, -1, false, "v1.2.3."}, 34 | 35 | {0, -1, -1, false, "v0"}, 36 | {0, -1, -1, true, "v0-unstable"}, 37 | {1, -1, -1, false, "v1"}, 38 | {1, -1, -1, true, "v1-unstable"}, 39 | {1, 2, -1, false, "v1.2"}, 40 | {1, 2, -1, true, "v1.2-unstable"}, 41 | {1, 2, 3, false, "v1.2.3"}, 42 | {1, 2, 3, true, "v1.2.3-unstable"}, 43 | {12, 34, 56, false, "v12.34.56"}, 44 | {12, 34, 56, true, "v12.34.56-unstable"}, 45 | } 46 | 47 | func (s *VersionSuite) TestParse(c *C) { 48 | for _, t := range versionParseTests { 49 | got, ok := parseVersion(t.s) 50 | if t.major == -1 { 51 | if ok || got != InvalidVersion { 52 | c.Fatalf("version %q is invalid but parsed as %#v", t.s, got) 53 | } 54 | } else { 55 | want := Version{t.major, t.minor, t.patch, t.dev} 56 | if got != want { 57 | c.Fatalf("version %q must parse as %#v, got %#v", t.s, want, got) 58 | } 59 | if got.String() != t.s { 60 | c.Fatalf("version %q got parsed as %#v and stringified as %q", t.s, got, got.String()) 61 | } 62 | } 63 | } 64 | } 65 | 66 | var versionLessTests = []struct { 67 | oneMajor, oneMinor, onePatch int 68 | oneUnstable bool 69 | twoMajor, twoMinor, twoPatch int 70 | twoUnstable, less bool 71 | }{ 72 | {0, 0, 0, false, 0, 0, 0, false, false}, 73 | {1, 0, 0, false, 1, 0, 0, false, false}, 74 | {1, 0, 0, false, 1, 1, 0, false, true}, 75 | {1, 0, 0, false, 2, 0, 0, false, true}, 76 | {0, 1, 0, false, 0, 1, 0, false, false}, 77 | {0, 1, 0, false, 0, 1, 1, false, true}, 78 | {0, 0, 0, false, 0, 2, 0, false, true}, 79 | {0, 0, 1, false, 0, 0, 1, false, false}, 80 | {0, 0, 1, false, 0, 0, 2, false, true}, 81 | 82 | {0, 0, 0, false, 0, 0, 0, true, false}, 83 | {0, 0, 0, true, 0, 0, 0, false, true}, 84 | {0, 0, 1, true, 0, 0, 0, false, false}, 85 | } 86 | 87 | func (s *VersionSuite) TestLess(c *C) { 88 | for _, t := range versionLessTests { 89 | one := Version{t.oneMajor, t.oneMinor, t.onePatch, t.oneUnstable} 90 | two := Version{t.twoMajor, t.twoMinor, t.twoPatch, t.twoUnstable} 91 | if one.Less(two) != t.less { 92 | c.Fatalf("version %s < %s returned %v", one, two, !t.less) 93 | } 94 | } 95 | } 96 | 97 | var versionContainsTests = []struct { 98 | oneMajor, oneMinor, onePatch int 99 | oneUnstable bool 100 | twoMajor, twoMinor, twoPatch int 101 | twoUnstable, contains bool 102 | }{ 103 | {12, 34, 56, false, 12, 34, 56, false, true}, 104 | {12, 34, 56, false, 12, 34, 78, false, false}, 105 | {12, 34, -1, false, 12, 34, 56, false, true}, 106 | {12, 34, -1, false, 12, 78, 56, false, false}, 107 | {12, -1, -1, false, 12, 34, 56, false, true}, 108 | {12, -1, -1, false, 78, 34, 56, false, false}, 109 | 110 | {12, -1, -1, true, 12, -1, -1, false, false}, 111 | {12, -1, -1, false, 12, -1, -1, true, false}, 112 | } 113 | 114 | func (s *VersionSuite) TestContains(c *C) { 115 | for _, t := range versionContainsTests { 116 | one := Version{t.oneMajor, t.oneMinor, t.onePatch, t.oneUnstable} 117 | two := Version{t.twoMajor, t.twoMinor, t.twoPatch, t.twoUnstable} 118 | if one.Contains(two) != t.contains { 119 | c.Fatalf("version %s.Contains(%s) returned %v", one, two, !t.contains) 120 | } 121 | } 122 | } 123 | 124 | func (s *VersionSuite) TestIsValid(c *C) { 125 | c.Assert(InvalidVersion.IsValid(), Equals, false) 126 | c.Assert(Version{0, 0, 0, false}.IsValid(), Equals, true) 127 | } 128 | -------------------------------------------------------------------------------- /refs_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "sort" 7 | 8 | . "gopkg.in/check.v1" 9 | ) 10 | 11 | var _ = Suite(&RefsSuite{}) 12 | 13 | type RefsSuite struct{} 14 | 15 | type refsTest struct { 16 | summary string 17 | original string 18 | version string 19 | changed string 20 | versions []string 21 | } 22 | 23 | var refsTests = []refsTest{{ 24 | "Version v0 works even without any references", 25 | reflines( 26 | "hash1 HEAD", 27 | ), 28 | "v0", 29 | reflines( 30 | "hash1 HEAD", 31 | ), 32 | nil, 33 | }, { 34 | "Preserve original capabilities", 35 | reflines( 36 | "hash1 HEAD\x00caps", 37 | ), 38 | "v0", 39 | reflines( 40 | "hash1 HEAD\x00caps", 41 | ), 42 | nil, 43 | }, { 44 | "Matching major version branch", 45 | reflines( 46 | "00000000000000000000000000000000000hash1 HEAD", 47 | "00000000000000000000000000000000000hash2 refs/heads/v0", 48 | "00000000000000000000000000000000000hash3 refs/heads/v1", 49 | "00000000000000000000000000000000000hash4 refs/heads/v2", 50 | ), 51 | "v1", 52 | reflines( 53 | "00000000000000000000000000000000000hash3 HEAD\x00symref=HEAD:refs/heads/v1", 54 | "00000000000000000000000000000000000hash3 refs/heads/master", 55 | "00000000000000000000000000000000000hash2 refs/heads/v0", 56 | "00000000000000000000000000000000000hash3 refs/heads/v1", 57 | "00000000000000000000000000000000000hash4 refs/heads/v2", 58 | ), 59 | []string{"v0", "v1", "v2"}, 60 | }, { 61 | "Matching minor version branch", 62 | reflines( 63 | "00000000000000000000000000000000000hash1 HEAD", 64 | "00000000000000000000000000000000000hash2 refs/heads/v1.1", 65 | "00000000000000000000000000000000000hash3 refs/heads/v1.3", 66 | "00000000000000000000000000000000000hash4 refs/heads/v1.2", 67 | ), 68 | "v1", 69 | reflines( 70 | "00000000000000000000000000000000000hash3 HEAD\x00symref=HEAD:refs/heads/v1.3", 71 | "00000000000000000000000000000000000hash3 refs/heads/master", 72 | "00000000000000000000000000000000000hash2 refs/heads/v1.1", 73 | "00000000000000000000000000000000000hash3 refs/heads/v1.3", 74 | "00000000000000000000000000000000000hash4 refs/heads/v1.2", 75 | ), 76 | []string{"v1.1", "v1.2", "v1.3"}, 77 | }, { 78 | "Disable original symref capability", 79 | reflines( 80 | "00000000000000000000000000000000000hash1 HEAD\x00foo symref=bar baz", 81 | "00000000000000000000000000000000000hash2 refs/heads/v1", 82 | ), 83 | "v1", 84 | reflines( 85 | "00000000000000000000000000000000000hash2 HEAD\x00symref=HEAD:refs/heads/v1 foo oldref=bar baz", 86 | "00000000000000000000000000000000000hash2 refs/heads/master", 87 | "00000000000000000000000000000000000hash2 refs/heads/v1", 88 | ), 89 | []string{"v1"}, 90 | }, { 91 | "Replace original master branch", 92 | reflines( 93 | "00000000000000000000000000000000000hash1 HEAD", 94 | "00000000000000000000000000000000000hash1 refs/heads/master", 95 | "00000000000000000000000000000000000hash2 refs/heads/v1", 96 | ), 97 | "v1", 98 | reflines( 99 | "00000000000000000000000000000000000hash2 HEAD\x00symref=HEAD:refs/heads/v1", 100 | "00000000000000000000000000000000000hash2 refs/heads/master", 101 | "00000000000000000000000000000000000hash2 refs/heads/v1", 102 | ), 103 | []string{"v1"}, 104 | }, { 105 | "Matching tag", 106 | reflines( 107 | "00000000000000000000000000000000000hash1 HEAD", 108 | "00000000000000000000000000000000000hash2 refs/tags/v0", 109 | "00000000000000000000000000000000000hash3 refs/tags/v1", 110 | "00000000000000000000000000000000000hash4 refs/tags/v2", 111 | ), 112 | "v1", 113 | reflines( 114 | "00000000000000000000000000000000000hash3 HEAD", 115 | "00000000000000000000000000000000000hash3 refs/heads/master", 116 | "00000000000000000000000000000000000hash2 refs/tags/v0", 117 | "00000000000000000000000000000000000hash3 refs/tags/v1", 118 | "00000000000000000000000000000000000hash4 refs/tags/v2", 119 | ), 120 | []string{"v0", "v1", "v2"}, 121 | }, { 122 | "Tag peeling", 123 | reflines( 124 | "00000000000000000000000000000000000hash1 HEAD", 125 | "00000000000000000000000000000000000hash2 refs/heads/master", 126 | "00000000000000000000000000000000000hash3 refs/tags/v1", 127 | "00000000000000000000000000000000000hash4 refs/tags/v1^{}", 128 | "00000000000000000000000000000000000hash5 refs/tags/v2", 129 | ), 130 | "v1", 131 | reflines( 132 | "00000000000000000000000000000000000hash4 HEAD", 133 | "00000000000000000000000000000000000hash4 refs/heads/master", 134 | "00000000000000000000000000000000000hash3 refs/tags/v1", 135 | "00000000000000000000000000000000000hash4 refs/tags/v1^{}", 136 | "00000000000000000000000000000000000hash5 refs/tags/v2", 137 | ), 138 | []string{"v1", "v1", "v2"}, 139 | }, { 140 | "Matching unstable versions", 141 | reflines( 142 | "00000000000000000000000000000000000hash1 HEAD", 143 | "00000000000000000000000000000000000hash2 refs/heads/master", 144 | "00000000000000000000000000000000000hash3 refs/heads/v1", 145 | "00000000000000000000000000000000000hash4 refs/heads/v1.1-unstable", 146 | "00000000000000000000000000000000000hash5 refs/heads/v1.3-unstable", 147 | "00000000000000000000000000000000000hash6 refs/heads/v1.2-unstable", 148 | "00000000000000000000000000000000000hash7 refs/heads/v2", 149 | ), 150 | "v1-unstable", 151 | reflines( 152 | "00000000000000000000000000000000000hash5 HEAD\x00symref=HEAD:refs/heads/v1.3-unstable", 153 | "00000000000000000000000000000000000hash5 refs/heads/master", 154 | "00000000000000000000000000000000000hash3 refs/heads/v1", 155 | "00000000000000000000000000000000000hash4 refs/heads/v1.1-unstable", 156 | "00000000000000000000000000000000000hash5 refs/heads/v1.3-unstable", 157 | "00000000000000000000000000000000000hash6 refs/heads/v1.2-unstable", 158 | "00000000000000000000000000000000000hash7 refs/heads/v2", 159 | ), 160 | []string{"v1", "v1.1-unstable", "v1.2-unstable", "v1.3-unstable", "v2"}, 161 | }} 162 | 163 | func reflines(lines ...string) string { 164 | var buf bytes.Buffer 165 | buf.WriteString("001e# service=git-upload-pack\n0000") 166 | for _, l := range lines { 167 | buf.WriteString(fmt.Sprintf("%04x%s\n", len(l)+5, l)) 168 | } 169 | buf.WriteString("0000") 170 | return buf.String() 171 | } 172 | 173 | func (s *RefsSuite) TestChangeRefs(c *C) { 174 | for _, test := range refsTests { 175 | c.Logf(test.summary) 176 | 177 | v, ok := parseVersion(test.version) 178 | if !ok { 179 | c.Fatalf("Test has an invalid version: %q", test.version) 180 | } 181 | 182 | changed, versions, err := changeRefs([]byte(test.original), v) 183 | c.Assert(err, IsNil) 184 | 185 | c.Assert(string(changed), Equals, test.changed) 186 | 187 | sort.Sort(versions) 188 | 189 | var vs []string 190 | for _, v := range versions { 191 | vs = append(vs, v.String()) 192 | } 193 | c.Assert(vs, DeepEquals, test.versions) 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /page.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "html/template" 6 | "log" 7 | "net/http" 8 | "os" 9 | "sort" 10 | ) 11 | 12 | const packageTemplateString = ` 13 | 14 |
15 | 16 |To get the package, execute:
133 |go get {{if eq .Repo.GopkgScheme "http"}}-insecure {{end}}{{.Repo.GopkgPath}}
134 | To import this package, add the following line to your code:
137 |import "{{.Repo.GopkgPath}}"
138 | {{if .PackageName}}Refer to it as {{.PackageName}}.{{end}} 139 |
For more details, see the API documentation.
142 |