Skip to content

Commit 08d42e5

Browse files
committed
feat(uv): handle credential helpers and .netrc
This allows one to download the uv binaries from private mirrors. Work towards #1975.
1 parent 369ca91 commit 08d42e5

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
@@ -105,6 +105,8 @@ END_UNRELEASED_TEMPLATE
105105
Set the `RULES_PYTHON_ENABLE_PIPSTAR=1` environment variable to enable it.
106106
* (utils) Add a way to run a REPL for any `rules_python` target that returns
107107
a `PyInfo` provider.
108+
* (uv) Handle `.netrc` and `auth_patterns` auth when downloading `uv`. Work towards
109+
[#1975](https://github.com/bazel-contrib/rules_python/issues/1975).
108110

109111
{#v0-0-0-removed}
110112
### Removed

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)