Skip to content

Commit 02198f6

Browse files
authored
feat(uv): handle credential helpers and .netrc (bazel-contrib#2872)
This allows one to download the uv binaries from private mirrors. The plumbing of the auth attrs allows us to correctly use the `~/.netrc` or the credential helper for downloading from mirrors that require authentication. Testing notes: * When I tested this, it seems that the dist manifest json may not work with private mirrors, but I think it is fine for users in such cases to define the `uv` srcs using the `urls` attribute. Work towards bazel-contrib#1975.
1 parent af9e959 commit 02198f6

File tree

5 files changed

+58
-9
lines changed

5 files changed

+58
-9
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ END_UNRELEASED_TEMPLATE
109109
Set the `RULES_PYTHON_ENABLE_PIPSTAR=1` environment variable to enable it.
110110
* (utils) Add a way to run a REPL for any `rules_python` target that returns
111111
a `PyInfo` provider.
112+
* (uv) Handle `.netrc` and `auth_patterns` auth when downloading `uv`. Work towards
113+
[#1975](https://github.com/bazel-contrib/rules_python/issues/1975).
112114
* (toolchains) Arbitrary python-build-standalone runtimes can be registered
113115
and activated with custom flags. See the [Registering custom runtimes]
114116
docs and {obj}`single_version_platform_override()` API docs for more

python/uv/private/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,15 @@ bzl_library(
6262
":toolchain_types_bzl",
6363
":uv_repository_bzl",
6464
":uv_toolchains_repo_bzl",
65+
"//python/private:auth_bzl",
6566
],
6667
)
6768

6869
bzl_library(
6970
name = "uv_repository_bzl",
7071
srcs = ["uv_repository.bzl"],
7172
visibility = ["//python/uv:__subpackages__"],
73+
deps = ["//python/private:auth_bzl"],
7274
)
7375

7476
bzl_library(

python/uv/private/uv.bzl

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ EXPERIMENTAL: This is experimental and may be removed without notice
1818
A module extension for working with uv.
1919
"""
2020

21+
load("//python/private:auth.bzl", "AUTH_ATTRS", "get_auth")
2122
load(":toolchain_types.bzl", "UV_TOOLCHAIN_TYPE")
2223
load(":uv_repository.bzl", "uv_repository")
2324
load(":uv_toolchains_repo.bzl", "uv_toolchains_repo")
@@ -77,7 +78,7 @@ The version of uv to configure the sources for. If this is not specified it will
7778
last version used in the module or the default version set by `rules_python`.
7879
""",
7980
),
80-
}
81+
} | AUTH_ATTRS
8182

8283
default = tag_class(
8384
doc = """\
@@ -133,7 +134,7 @@ for a particular version.
133134
},
134135
)
135136

136-
def _configure(config, *, platform, compatible_with, target_settings, urls = [], sha256 = "", override = False, **values):
137+
def _configure(config, *, platform, compatible_with, target_settings, auth_patterns, urls = [], sha256 = "", override = False, **values):
137138
"""Set the value in the config if the value is provided"""
138139
for key, value in values.items():
139140
if not value:
@@ -144,6 +145,7 @@ def _configure(config, *, platform, compatible_with, target_settings, urls = [],
144145

145146
config[key] = value
146147

148+
config.setdefault("auth_patterns", {}).update(auth_patterns)
147149
config.setdefault("platforms", {})
148150
if not platform:
149151
if compatible_with or target_settings or urls:
@@ -173,7 +175,8 @@ def process_modules(
173175
hub_name = "uv",
174176
uv_repository = uv_repository,
175177
toolchain_type = str(UV_TOOLCHAIN_TYPE),
176-
hub_repo = uv_toolchains_repo):
178+
hub_repo = uv_toolchains_repo,
179+
get_auth = get_auth):
177180
"""Parse the modules to get the config for 'uv' toolchains.
178181
179182
Args:
@@ -182,6 +185,7 @@ def process_modules(
182185
uv_repository: the rule to create a uv_repository override.
183186
toolchain_type: the toolchain type to use here.
184187
hub_repo: the hub repo factory function to use.
188+
get_auth: the auth function to use.
185189
186190
Returns:
187191
the result of the hub_repo. Mainly used for tests.
@@ -216,6 +220,8 @@ def process_modules(
216220
compatible_with = tag.compatible_with,
217221
target_settings = tag.target_settings,
218222
override = mod.is_root,
223+
netrc = tag.netrc,
224+
auth_patterns = tag.auth_patterns,
219225
)
220226

221227
for key in [
@@ -271,6 +277,8 @@ def process_modules(
271277
sha256 = tag.sha256,
272278
urls = tag.urls,
273279
override = mod.is_root,
280+
netrc = tag.netrc,
281+
auth_patterns = tag.auth_patterns,
274282
)
275283

276284
if not versions:
@@ -301,6 +309,11 @@ def process_modules(
301309
for platform, src in config.get("urls", {}).items()
302310
if src.urls
303311
}
312+
auth = {
313+
"auth_patterns": config.get("auth_patterns"),
314+
"netrc": config.get("netrc"),
315+
}
316+
auth = {k: v for k, v in auth.items() if v}
304317

305318
# Or fallback to fetching them from GH manifest file
306319
# Example file: https://github.com/astral-sh/uv/releases/download/0.6.3/dist-manifest.json
@@ -313,6 +326,8 @@ def process_modules(
313326
),
314327
manifest_filename = config["manifest_filename"],
315328
platforms = sorted(platforms),
329+
get_auth = get_auth,
330+
**auth
316331
)
317332

318333
for platform_name, platform in platforms.items():
@@ -327,6 +342,7 @@ def process_modules(
327342
platform = platform_name,
328343
urls = urls[platform_name].urls,
329344
sha256 = urls[platform_name].sha256,
345+
**auth
330346
)
331347

332348
toolchain_names.append(toolchain_name)
@@ -363,7 +379,7 @@ def _overlap(first_collection, second_collection):
363379

364380
return False
365381

366-
def _get_tool_urls_from_dist_manifest(module_ctx, *, base_url, manifest_filename, platforms):
382+
def _get_tool_urls_from_dist_manifest(module_ctx, *, base_url, manifest_filename, platforms, get_auth = get_auth, **auth_attrs):
367383
"""Download the results about remote tool sources.
368384
369385
This relies on the tools using the cargo packaging to infer the actual
@@ -431,10 +447,13 @@ def _get_tool_urls_from_dist_manifest(module_ctx, *, base_url, manifest_filename
431447
"aarch64-apple-darwin"
432448
]
433449
"""
450+
auth_attr = struct(**auth_attrs)
434451
dist_manifest = module_ctx.path(manifest_filename)
452+
urls = [base_url + "/" + manifest_filename]
435453
result = module_ctx.download(
436-
base_url + "/" + manifest_filename,
454+
url = urls,
437455
output = dist_manifest,
456+
auth = get_auth(module_ctx, urls, ctx_attr = auth_attr),
438457
)
439458
if not result.success:
440459
fail(result)
@@ -454,11 +473,13 @@ def _get_tool_urls_from_dist_manifest(module_ctx, *, base_url, manifest_filename
454473

455474
checksum_fname = checksum["name"]
456475
checksum_path = module_ctx.path(checksum_fname)
476+
urls = ["{}/{}".format(base_url, checksum_fname)]
457477
downloads[checksum_path] = struct(
458478
download = module_ctx.download(
459-
"{}/{}".format(base_url, checksum_fname),
479+
url = urls,
460480
output = checksum_path,
461481
block = False,
482+
auth = get_auth(module_ctx, urls, ctx_attr = auth_attr),
462483
),
463484
archive_fname = fname,
464485
platforms = checksum["target_triples"],
@@ -473,7 +494,7 @@ def _get_tool_urls_from_dist_manifest(module_ctx, *, base_url, manifest_filename
473494

474495
sha256, _, checksummed_fname = module_ctx.read(checksum_path).partition(" ")
475496
checksummed_fname = checksummed_fname.strip(" *\n")
476-
if archive_fname != checksummed_fname:
497+
if checksummed_fname and archive_fname != checksummed_fname:
477498
fail("The checksum is for a different file, expected '{}' but got '{}'".format(
478499
archive_fname,
479500
checksummed_fname,

python/uv/private/uv_repository.bzl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ EXPERIMENTAL: This is experimental and may be removed without notice
1818
Create repositories for uv toolchain dependencies
1919
"""
2020

21+
load("//python/private:auth.bzl", "AUTH_ATTRS", "get_auth")
22+
2123
UV_BUILD_TMPL = """\
2224
# Generated by repositories.bzl
2325
load("@rules_python//python/uv:uv_toolchain.bzl", "uv_toolchain")
@@ -43,6 +45,7 @@ def _uv_repo_impl(repository_ctx):
4345
url = repository_ctx.attr.urls,
4446
sha256 = repository_ctx.attr.sha256,
4547
stripPrefix = strip_prefix,
48+
auth = get_auth(repository_ctx, repository_ctx.attr.urls),
4649
)
4750

4851
binary = "uv.exe" if is_windows else "uv"
@@ -70,5 +73,5 @@ uv_repository = repository_rule(
7073
"sha256": attr.string(mandatory = False),
7174
"urls": attr.string_list(mandatory = True),
7275
"version": attr.string(mandatory = True),
73-
},
76+
} | AUTH_ATTRS,
7477
)

tests/uv/uv/uv_tests.bzl

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ def _mod(*, name = None, default = [], configure = [], is_root = True):
100100
)
101101

102102
def _process_modules(env, **kwargs):
103-
result = process_modules(hub_repo = struct, **kwargs)
103+
result = process_modules(hub_repo = struct, get_auth = lambda *_, **__: None, **kwargs)
104104

105105
return env.expect.that_struct(
106106
struct(
@@ -124,6 +124,8 @@ def _default(
124124
platform = None,
125125
target_settings = None,
126126
version = None,
127+
netrc = None,
128+
auth_patterns = None,
127129
**kwargs):
128130
return struct(
129131
base_url = base_url,
@@ -132,6 +134,8 @@ def _default(
132134
platform = platform,
133135
target_settings = [] + (target_settings or []), # ensure that the type is correct
134136
version = version,
137+
netrc = netrc,
138+
auth_patterns = {} | (auth_patterns or {}), # ensure that the type is correct
135139
**kwargs
136140
)
137141

@@ -377,6 +381,11 @@ def _test_complex_configuring(env):
377381
platform = "linux",
378382
compatible_with = ["@platforms//os:linux"],
379383
),
384+
_configure(
385+
version = "1.0.4",
386+
netrc = "~/.my_netrc",
387+
auth_patterns = {"foo": "bar"},
388+
), # use auth
380389
],
381390
),
382391
),
@@ -388,18 +397,21 @@ def _test_complex_configuring(env):
388397
"1_0_1_osx",
389398
"1_0_2_osx",
390399
"1_0_3_linux",
400+
"1_0_4_osx",
391401
])
392402
uv.implementations().contains_exactly({
393403
"1_0_0_osx": "@uv_1_0_0_osx//:uv_toolchain",
394404
"1_0_1_osx": "@uv_1_0_1_osx//:uv_toolchain",
395405
"1_0_2_osx": "@uv_1_0_2_osx//:uv_toolchain",
396406
"1_0_3_linux": "@uv_1_0_3_linux//:uv_toolchain",
407+
"1_0_4_osx": "@uv_1_0_4_osx//:uv_toolchain",
397408
})
398409
uv.compatible_with().contains_exactly({
399410
"1_0_0_osx": ["@platforms//os:os"],
400411
"1_0_1_osx": ["@platforms//os:os"],
401412
"1_0_2_osx": ["@platforms//os:different"],
402413
"1_0_3_linux": ["@platforms//os:linux"],
414+
"1_0_4_osx": ["@platforms//os:os"],
403415
})
404416
uv.target_settings().contains_exactly({})
405417
env.expect.that_collection(calls).contains_exactly([
@@ -431,6 +443,15 @@ def _test_complex_configuring(env):
431443
"urls": ["https://example.org/1.0.3/linux"],
432444
"version": "1.0.3",
433445
},
446+
{
447+
"auth_patterns": {"foo": "bar"},
448+
"name": "uv_1_0_4_osx",
449+
"netrc": "~/.my_netrc",
450+
"platform": "osx",
451+
"sha256": "deadb00f",
452+
"urls": ["https://example.org/1.0.4/osx"],
453+
"version": "1.0.4",
454+
},
434455
])
435456

436457
_tests.append(_test_complex_configuring)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy