Skip to content

Commit ef58f55

Browse files
committed
Use bazel's downloader for Go modules when possible
1 parent 3e438c8 commit ef58f55

File tree

3 files changed

+75
-2
lines changed

3 files changed

+75
-2
lines changed

cmd/fetch_repo/main.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ package main
2727
import (
2828
"flag"
2929
"log"
30+
"os"
3031

32+
"golang.org/x/mod/sumdb/dirhash"
3133
"golang.org/x/tools/go/vcs"
3234
)
3335

@@ -70,7 +72,19 @@ func main() {
7072
}
7173

7274
if *no_fetch {
73-
// Nothing to do
75+
if *sum != "" {
76+
repoSum, err := dirhash.HashDir(*dest, *importpath+"@"+*version, dirhash.Hash1)
77+
if err != nil {
78+
log.Fatalf("failed computing sum: %v", err)
79+
}
80+
81+
if repoSum != *sum {
82+
if goModCache := os.Getenv("GOMODCACHE"); goModCache != "" {
83+
log.Fatalf("resulting module with sum %s; expected sum %s, Please try clearing your module cache directory %q", repoSum, *sum, goModCache)
84+
}
85+
log.Fatalf("resulting module with sum %s; expected sum %s", repoSum, *sum)
86+
}
87+
}
7488
} else if *path != "" {
7589
if *importpath != "" {
7690
log.Fatal("-importpath must not be set")

internal/go_repository.bzl

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,39 @@ Authorization: Bearer RANDOM-TOKEN
117117
# We can't disable timeouts on Bazel, but we can set them to large values.
118118
_GO_REPOSITORY_TIMEOUT = 86400
119119

120+
def _escape_go_module_path(module_path):
121+
"""Escapes a Go module path according to the Go proxy protocol."""
122+
result = ""
123+
for c in module_path.elems():
124+
if "A" <= c and c <= "Z":
125+
result += "!" + c.lower()
126+
else:
127+
result += c
128+
return result
129+
130+
def _get_goproxy_url(env):
131+
"""Return proxy URL to be used for module_path, or None if no proxy will be used."""
132+
goproxy = env.get("GOPROXY", "")
133+
goprivate = env.get("GOPRIVATE", "")
134+
gonoproxy = env.get("GONOPROXY", "")
135+
136+
# TODO(zbarsky): In the future, we could try to handle some subset of downloads even if GONOPROXY/GOPRIVATE are set.
137+
# Doing this correctly requires care, as the syntax is powerful, and we don't want to get it wrong and request from the proxy.
138+
if goproxy == "off" or goprivate or gonoproxy:
139+
return None
140+
141+
if not goproxy:
142+
return "https://proxy.golang.org"
143+
144+
idx = goproxy.find(",")
145+
if idx != -1:
146+
goproxy = goproxy[:idx]
147+
148+
if goproxy == "direct":
149+
return None
150+
151+
return goproxy
152+
120153
def _go_repository_impl(ctx):
121154
# TODO(#549): vcs repositories are not cached and still need to be fetched.
122155
# Download the repository or module.
@@ -137,6 +170,7 @@ def _go_repository_impl(ctx):
137170
gazelle_path = ctx.path(Label(_gazelle))
138171
watch(ctx, gazelle_path)
139172

173+
can_use_direct_download = False
140174
reproducible = False
141175
if ctx.attr.local_path:
142176
if hasattr(ctx, "watch_tree"):
@@ -204,6 +238,7 @@ def _go_repository_impl(ctx):
204238
else:
205239
fail("if version is specified, sum must also be")
206240
reproducible = True
241+
can_use_direct_download = True
207242

208243
fetch_path = ctx.attr.replace if ctx.attr.replace else ctx.attr.importpath
209244
fetch_repo_args = [
@@ -286,6 +321,30 @@ def _go_repository_impl(ctx):
286321
# Override external GO111MODULE, because it is needed by module mode, no-op in repository mode
287322
fetch_repo_env["GO111MODULE"] = "on"
288323

324+
if can_use_direct_download:
325+
proxy_url = _get_goproxy_url(env)
326+
if proxy_url:
327+
module = ctx.attr.replace if ctx.attr.replace else ctx.attr.importpath
328+
url = "%s/%s/@v/%s.zip" % (
329+
proxy_url,
330+
_escape_go_module_path(module),
331+
ctx.attr.version,
332+
)
333+
result = ctx.download_and_extract(
334+
url = url,
335+
sha256 = ctx.attr.sha256,
336+
canonical_id = url,
337+
stripPrefix = module + "@" + ctx.attr.version,
338+
type = ctx.attr.type,
339+
auth = use_netrc(read_user_netrc(ctx), ctx.attr.urls, ctx.attr.auth_patterns),
340+
allow_fail = True,
341+
)
342+
if result.success:
343+
# All we need to do now is verify the sum!
344+
fetch_repo_args = ["-no-fetch"] + fetch_repo_args
345+
# If we did not succeed, fallback to using the Go tooling to fetch the module. This matches
346+
# what would happen normally (especially since we only tried the first entry in GOPROXY).
347+
289348
result = env_execute(
290349
ctx,
291350
[fetch_repo] + fetch_repo_args,

internal/go_repository_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ go_repository(
180180
}
181181

182182
func TestModcacheRW(t *testing.T) {
183-
if err := bazel_testing.RunBazel("query", "@errors_go_mod//:go_default_library"); err != nil {
183+
if err := bazel_testing.RunBazel("query", "--repo_env=GOPROXY=direct", "@errors_go_mod//:go_default_library"); err != nil {
184184
t.Fatal(err)
185185
}
186186
out, err := bazel_testing.BazelOutput("info", "output_base")

0 commit comments

Comments
 (0)