diff --git a/cmd/golangorg/regtest_test.go b/cmd/golangorg/regtest_test.go index c6a5e11d..cf8a491b 100644 --- a/cmd/golangorg/regtest_test.go +++ b/cmd/golangorg/regtest_test.go @@ -118,6 +118,11 @@ func TestLiveServer(t *testing.T) { Substring: "", // just check it is a 2xx. NoAnalytics: true, }, + { + Path: "/x/net", + Substring: ``, + NoAnalytics: true, + }, } for _, tc := range substringTests { diff --git a/cmd/golangorg/x.go b/cmd/golangorg/x.go index 539316f9..6aae7d66 100644 --- a/cmd/golangorg/x.go +++ b/cmd/golangorg/x.go @@ -13,68 +13,39 @@ import ( "log" "net/http" "strings" + + "golang.org/x/build/repos" ) const xPrefix = "/x/" -type xRepo struct { - URL, VCS string -} - -var xMap = map[string]xRepo{ - "codereview": {"https://code.google.com/p/go.codereview", "hg"}, // Not included at https://golang.org/pkg/#subrepo. - - "arch": {"https://go.googlesource.com/arch", "git"}, // Not included at https://golang.org/pkg/#subrepo. - "benchmarks": {"https://go.googlesource.com/benchmarks", "git"}, - "blog": {"https://go.googlesource.com/blog", "git"}, - "build": {"https://go.googlesource.com/build", "git"}, - "crypto": {"https://go.googlesource.com/crypto", "git"}, - "debug": {"https://go.googlesource.com/debug", "git"}, - "exp": {"https://go.googlesource.com/exp", "git"}, - "image": {"https://go.googlesource.com/image", "git"}, - "lint": {"https://go.googlesource.com/lint", "git"}, // Not included at https://golang.org/pkg/#subrepo. - "mobile": {"https://go.googlesource.com/mobile", "git"}, - "mod": {"https://go.googlesource.com/mod", "git"}, - "net": {"https://go.googlesource.com/net", "git"}, - "oauth2": {"https://go.googlesource.com/oauth2", "git"}, // Not included at https://golang.org/pkg/#subrepo. - "perf": {"https://go.googlesource.com/perf", "git"}, - "playground": {"https://go.googlesource.com/playground", "git"}, // Not included at https://golang.org/pkg/#subrepo. - "review": {"https://go.googlesource.com/review", "git"}, - "sync": {"https://go.googlesource.com/sync", "git"}, - "sys": {"https://go.googlesource.com/sys", "git"}, - "talks": {"https://go.googlesource.com/talks", "git"}, // Not included at https://golang.org/pkg/#subrepo. - "term": {"https://go.googlesource.com/term", "git"}, // Not included at https://golang.org/pkg/#subrepo. - "text": {"https://go.googlesource.com/text", "git"}, - "time": {"https://go.googlesource.com/time", "git"}, - "tools": {"https://go.googlesource.com/tools", "git"}, - "tour": {"https://go.googlesource.com/tour", "git"}, - "vgo": {"https://go.googlesource.com/vgo", "git"}, // Not included at https://golang.org/pkg/#subrepo. - "website": {"https://go.googlesource.com/website", "git"}, // Not included at https://golang.org/pkg/#subrepo. - "xerrors": {"https://go.googlesource.com/xerrors", "git"}, // Not included at https://golang.org/pkg/#subrepo. -} - func init() { http.HandleFunc(xPrefix, xHandler) } func xHandler(w http.ResponseWriter, r *http.Request) { - head, tail := strings.TrimPrefix(r.URL.Path, xPrefix), "" - if i := strings.Index(head, "/"); i != -1 { - head, tail = head[:i], head[i:] - } - if head == "" { + if !strings.HasPrefix(r.URL.Path, xPrefix) { + // Shouldn't happen if handler is registered correctly. http.Redirect(w, r, "https://godoc.org/-/subrepo", http.StatusTemporaryRedirect) return } - repo, ok := xMap[head] - if !ok { + proj, suffix := strings.TrimPrefix(r.URL.Path, xPrefix), "" + if i := strings.Index(proj, "/"); i != -1 { + proj, suffix = proj[:i], proj[i:] + } + if proj == "" { + http.Redirect(w, r, "https://godoc.org/-/subrepo", http.StatusTemporaryRedirect) + return + } + repo, ok := repos.ByGerritProject[proj] + if !ok || !strings.HasPrefix(repo.ImportPath, "golang.org/x/") { http.NotFound(w, r) return } data := struct { - Prefix, Head, Tail string - Repo xRepo - }{xPrefix, head, tail, repo} + Proj string // Gerrit project ("net", "sys", etc) + Suffix string // optional "/path" for requests like /x/PROJ/path + }{proj, suffix} if err := xTemplate.Execute(w, data); err != nil { log.Println("xHandler:", err) } @@ -84,12 +55,12 @@ var xTemplate = template.Must(template.New("x").Parse(`
- - - + + + -Nothing to see here; move along. +Nothing to see here; move along. `)) diff --git a/cmd/golangorg/x_test.go b/cmd/golangorg/x_test.go new file mode 100644 index 00000000..28982c3f --- /dev/null +++ b/cmd/golangorg/x_test.go @@ -0,0 +1,92 @@ +// Copyright 2019 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 ( + "net/http/httptest" + "strings" + "testing" +) + +func TestXHandler(t *testing.T) { + type check func(t *testing.T, rec *httptest.ResponseRecorder) + status := func(v int) check { + return func(t *testing.T, rec *httptest.ResponseRecorder) { + t.Helper() + if rec.Code != v { + t.Errorf("response status = %v; want %v", rec.Code, v) + } + } + } + substr := func(s string) check { + return func(t *testing.T, rec *httptest.ResponseRecorder) { + t.Helper() + if !strings.Contains(rec.Body.String(), s) { + t.Errorf("missing expected substring %q in value: %#q", s, rec.Body) + } + } + } + hasHeader := func(k, v string) check { + return func(t *testing.T, rec *httptest.ResponseRecorder) { + t.Helper() + if got := rec.HeaderMap.Get(k); got != v { + t.Errorf("header[%q] = %q; want %q", k, got, v) + } + } + } + + tests := []struct { + name string + path string + checks []check + }{ + { + name: "net", + path: "/x/net", + checks: []check{ + status(200), + substr(``), + substr(`http-equiv="refresh" content="0; url=https://godoc.org/golang.org/x/net">`), + }, + }, + { + name: "net-suffix", + path: "/x/net/suffix", + checks: []check{ + status(200), + substr(``), + substr(`http-equiv="refresh" content="0; url=https://godoc.org/golang.org/x/net/suffix">`), + }, + }, + { + name: "notexist", + path: "/x/notexist", + checks: []check{status(404)}, + }, + { + name: "empty", + path: "/x/", + checks: []check{ + status(307), + hasHeader("Location", "https://godoc.org/-/subrepo"), + }, + }, + { + name: "invalid", + path: "/x/In%20Valid,X", + checks: []check{status(404)}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + req := httptest.NewRequest("GET", tt.path, nil) + rec := httptest.NewRecorder() + xHandler(rec, req) + for _, check := range tt.checks { + check(t, rec) + } + }) + } +} diff --git a/go.mod b/go.mod index f1f3f5a4..aefe1839 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( cloud.google.com/go v0.38.0 github.com/gomodule/redigo v2.0.0+incompatible go.opencensus.io v0.22.0 // indirect + golang.org/x/build v0.0.0-20191213161705-41fffb13b6ef golang.org/x/net v0.0.0-20190620200207-3b0461eec859 golang.org/x/tools v0.0.0-20190930201159-7c411dea38b0 google.golang.org/api v0.9.0 // indirect diff --git a/go.sum b/go.sum index 5a5d064e..e0b12482 100644 --- a/go.sum +++ b/go.sum @@ -5,7 +5,13 @@ cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -21,20 +27,32 @@ github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.2-0.20191028172631-481baca67f93 h1:VvBteXw2zOXEgm0o3PgONTWf+bhUGsCaiNn3pbkU9LA= +github.com/google/go-cmp v0.3.2-0.20191028172631-481baca67f93/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= +golang.org/x/build v0.0.0-20191213161705-41fffb13b6ef h1:vxq/R6+rmxSS0Dpy3fgjNUIa3RMHLyN1HLFnCVSSaPc= +golang.org/x/build v0.0.0-20191213161705-41fffb13b6ef/go.mod h1:ZnlK0tLKZc2fY0bQwgXiLO1V9KYILhZHGJZGCecl/1Q= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -48,15 +66,19 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190502183928-7f726cade0ab/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c h1:uOCk1iQW6Vc18bnC13MfzScl+wdKBmM9Y9kU7Z83/lw= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= @@ -67,7 +89,9 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b h1:ag/x1USPSsqHud38I9BAC88qdNLDHHtQ4mlgQIZPPNA= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -75,6 +99,7 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3 golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -101,6 +126,8 @@ google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRn google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a h1:LJwr7TCTghdatWv40WobzlKXc9c4s8oGa7QKJUtHhWA=