Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/export/.bazelversion
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
7.0.0
7 changes: 7 additions & 0 deletions examples/export/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
load("@rules_uv//uv:uv_export.bzl", "uv_export")

uv_export(
name = "export",
pyproject_toml = "pyproject.toml",
requirements_txt = "requirements.txt",
)
15 changes: 15 additions & 0 deletions examples/export/MODULE.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module(
name = "uv__examples__export",
version = "0.0.0",
)

bazel_dep(name = "rules_uv", version = "0.0.0")
bazel_dep(name = "rules_python", version = "0.31.0")

local_path_override(
module_name = "rules_uv",
path = "../..",
)

python = use_extension("@rules_python//python/extensions:python.bzl", "python")
python.toolchain(python_version = "3.11")
450 changes: 450 additions & 0 deletions examples/export/MODULE.bazel.lock

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions examples/export/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[project]
name = "lock-example"
version = "0.1.0"
dependencies = [
"requests==2.31.0",
]
84 changes: 84 additions & 0 deletions examples/export/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
certifi==2026.1.4 \
--hash=sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c \
--hash=sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120
# via requests
charset-normalizer==3.4.4 \
--hash=sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394 \
--hash=sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89 \
--hash=sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86 \
--hash=sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f \
--hash=sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8 \
--hash=sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161 \
--hash=sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152 \
--hash=sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72 \
--hash=sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4 \
--hash=sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e \
--hash=sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3 \
--hash=sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c \
--hash=sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2 \
--hash=sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44 \
--hash=sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26 \
--hash=sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016 \
--hash=sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede \
--hash=sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf \
--hash=sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc \
--hash=sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0 \
--hash=sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1 \
--hash=sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed \
--hash=sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8 \
--hash=sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133 \
--hash=sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e \
--hash=sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef \
--hash=sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14 \
--hash=sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0 \
--hash=sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828 \
--hash=sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f \
--hash=sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328 \
--hash=sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090 \
--hash=sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381 \
--hash=sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c \
--hash=sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb \
--hash=sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc \
--hash=sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a \
--hash=sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec \
--hash=sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc \
--hash=sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac \
--hash=sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569 \
--hash=sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3 \
--hash=sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525 \
--hash=sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894 \
--hash=sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a \
--hash=sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9 \
--hash=sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14 \
--hash=sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25 \
--hash=sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1 \
--hash=sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3 \
--hash=sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e \
--hash=sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815 \
--hash=sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6 \
--hash=sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15 \
--hash=sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191 \
--hash=sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0 \
--hash=sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897 \
--hash=sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd \
--hash=sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2 \
--hash=sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794 \
--hash=sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224 \
--hash=sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838 \
--hash=sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a \
--hash=sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d \
--hash=sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490 \
--hash=sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9
# via requests
idna==3.11 \
--hash=sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea \
--hash=sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902
# via requests
requests==2.31.0 \
--hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \
--hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1
# via lock-example
urllib3==2.6.3 \
--hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \
--hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4
# via requests
9 changes: 9 additions & 0 deletions uv/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")

bzl_library(
name = "uv_export",
srcs = ["export.bzl"],
visibility = ["//visibility:public"],
deps = [
"//uv/private:uv_export",
],
)

bzl_library(
name = "pip",
srcs = ["pip.bzl"],
Expand Down
16 changes: 16 additions & 0 deletions uv/private/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,25 @@ exports_files([
"create_venv.sh",
"pip_compile_test.sh",
"pip_compile.sh",
"uv_export_test.sh",
"uv_export.sh",
"sync_venv.sh",
])

bzl_library(
name = "uv_export",
srcs = ["export.bzl"],
visibility = [
"//multitool:__subpackages__",
"//uv:__pkg__",
],
deps = [
":interpreter_path",
":transition_to_target",
"@rules_python//python:defs_bzl",
],
)

bzl_library(
name = "pip",
srcs = ["pip.bzl"],
Expand Down
130 changes: 130 additions & 0 deletions uv/private/uv_export.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
"uv based pip lock rules"

load("@rules_python//python:defs.bzl", "PyRuntimeInfo")
load(":interpreter_path.bzl", "python_interpreter_path")
load(":transition_to_target.bzl", "transition_to_target")

_PY_TOOLCHAIN = "@bazel_tools//tools/python:toolchain_type"

# uv export has different defaults than uv pip compile
_DEFAULT_ARGS = [
"--format",
"requirements.txt",
"--no-header", # Exclude the header comment with uv command that generated the file
"--no-emit-workspace", # Don't emit any workspace structure in the output
]

_COMMON_ATTRS = {
"pyproject_toml": attr.label(mandatory = True, allow_single_file = True),
"requirements_txt": attr.label(mandatory = True, allow_single_file = True),
"py3_runtime": attr.label(),
"data": attr.label_list(allow_files = True),
"uv_args": attr.string_list(default = _DEFAULT_ARGS),
"extra_args": attr.string_list(),
"env": attr.string_dict(),
"_uv": attr.label(default = "@multitool//tools/uv", executable = True, cfg = transition_to_target),
}

def _python_runtime(ctx):
if ctx.attr.py3_runtime:
return ctx.attr.py3_runtime[PyRuntimeInfo]
py_toolchain = ctx.toolchains[_PY_TOOLCHAIN]
return py_toolchain.py3_runtime

def _uv_uv_export(
ctx,
template,
executable,
generator_label,
uv_args,
extra_args):
py3_runtime = _python_runtime(ctx)
compile_command = "bazel run {label}".format(label = str(generator_label))

args = []
args += uv_args
args += extra_args
args.append("--python={python}".format(python = python_interpreter_path(py3_runtime)))

ctx.actions.expand_template(
template = template,
output = executable,
substitutions = {
"{{uv}}": ctx.executable._uv.short_path,
"{{args}}": " \\\n ".join(args),
"{{pyproject_toml}}": ctx.file.pyproject_toml.short_path,
"{{requirements_txt}}": ctx.file.requirements_txt.short_path,
"{{compile_command}}": compile_command,
},
)

def _runfiles(ctx):
py3_runtime = _python_runtime(ctx)
runfiles = ctx.runfiles(
files = [ctx.file.pyproject_toml, ctx.file.requirements_txt] + ctx.files.data,
transitive_files = py3_runtime.files,
)
runfiles = runfiles.merge(ctx.attr._uv[0].default_runfiles)
return runfiles

def _uv_export_impl(ctx):
executable = ctx.actions.declare_file(ctx.attr.name)
_uv_uv_export(
ctx = ctx,
template = ctx.file._template,
executable = executable,
generator_label = ctx.label,
uv_args = ctx.attr.uv_args,
extra_args = ctx.attr.extra_args,
)
return [
DefaultInfo(
executable = executable,
runfiles = _runfiles(ctx),
),
RunEnvironmentInfo(
environment = ctx.attr.env,
),
]

uv_export = rule(
attrs = _COMMON_ATTRS | {
"_template": attr.label(default = "//uv/private:uv_export.sh", allow_single_file = True),
},
toolchains = [_PY_TOOLCHAIN],
implementation = _uv_export_impl,
executable = True,
)

def _uv_export_test_impl(ctx):
executable = ctx.actions.declare_file(ctx.attr.name)
_uv_uv_export(
ctx = ctx,
template = ctx.file._template,
executable = executable,
generator_label = ctx.attr.generator_label.label,
uv_args = ctx.attr.uv_args,
extra_args = ctx.attr.extra_args,
)
return [
DefaultInfo(
executable = executable,
runfiles = _runfiles(ctx),
),
RunEnvironmentInfo(
environment = ctx.attr.env,
# Ensures that .netrc can be detected by uv
# See https://github.com/theoremlp/rules_uv/issues/103
inherited_environment = ["HOME"],
),
]

uv_export_test = rule(
attrs = _COMMON_ATTRS | {
"generator_label": attr.label(mandatory = True),
"_template": attr.label(default = "//uv/private:uv_export_test.sh", allow_single_file = True),
},
toolchains = [_PY_TOOLCHAIN],
implementation = _uv_export_test_impl,
test = True,
)
14 changes: 14 additions & 0 deletions uv/private/uv_export.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/usr/bin/env bash

set -euo pipefail

# inputs from Bazel
PYPROJECT_TOML="{{pyproject_toml}}"
REQUIREMENTS_TXT="{{requirements_txt}}"
COMPILE_COMMAND="{{compile_command}}"

{{uv}} export \
{{args}} \
--project="$(dirname "$PYPROJECT_TOML")" \
--output-file="$REQUIREMENTS_TXT" \
"$@"
30 changes: 30 additions & 0 deletions uv/private/uv_export_test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/usr/bin/env bash

set -euo pipefail

# inputs from Bazel
PYPROJECT_TOML="{{pyproject_toml}}"
REQUIREMENTS_TXT="{{requirements_txt}}"
COMPILE_COMMAND="{{compile_command}}"

# make a writable copy of incoming requirements
updated_file=$(mktemp)
trap 'rm -f "$updated_file"' EXIT
cp "$REQUIREMENTS_TXT" "$updated_file"

{{uv}} export \
--quiet \
--no-cache \
{{args}} \
--project="$(dirname "$PYPROJECT_TOML")" \
--output-file="$updated_file" \
"$@"

# check files match
DIFF="$(diff "$REQUIREMENTS_TXT" "$updated_file" || true)"
if [ "$DIFF" != "" ]
then
echo >&2 "FAIL: $REQUIREMENTS_TXT is out-of-date. Run '$COMPILE_COMMAND' to update."
echo >&2 "$DIFF"
exit 1
fi
Loading