Skip to content
Merged
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
28 changes: 28 additions & 0 deletions .bazelci/presubmit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -289,21 +289,49 @@ tasks:
name: compile_pip_requirements integration tests on Ubuntu
working_directory: tests/compile_pip_requirements
platform: ubuntu2004
shell_commands:
# Make a change to the locked requirements and then assert that //:requirements.update does the
# right thing.
- "echo '' > requirements_lock.txt"
- "! git diff --exit-code"
- "bazel run //:requirements.update"
- "git diff --exit-code"
integration_test_compile_pip_requirements_debian:
<<: *reusable_build_test_all
name: compile_pip_requirements integration tests on Debian
working_directory: tests/compile_pip_requirements
platform: debian11
shell_commands:
# Make a change to the locked requirements and then assert that //:requirements.update does the
# right thing.
- "echo '' > requirements_lock.txt"
- "! git diff --exit-code"
- "bazel run //:requirements.update"
- "git diff --exit-code"
integration_test_compile_pip_requirements_macos:
<<: *reusable_build_test_all
name: compile_pip_requirements integration tests on macOS
working_directory: tests/compile_pip_requirements
platform: macos
shell_commands:
# Make a change to the locked requirements and then assert that //:requirements.update does the
# right thing.
- "echo '' > requirements_lock.txt"
- "! git diff --exit-code"
- "bazel run //:requirements.update"
- "git diff --exit-code"
integration_test_compile_pip_requirements_windows:
<<: *reusable_build_test_all
name: compile_pip_requirements integration tests on Windows
working_directory: tests/compile_pip_requirements
platform: windows
shell_commands:
# Make a change to the locked requirements and then assert that //:requirements.update does the
# right thing.
- "echo '' > requirements_lock.txt"
- "! git diff --exit-code"
- "bazel run //:requirements.update"
- "git diff --exit-code"

integration_test_pip_repository_entry_points_ubuntu:
<<: *reusable_build_test_all
Expand Down
2 changes: 2 additions & 0 deletions python/pip_install/requirements.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ def compile_pip_requirements(

tags = tags or []
tags.append("requires-network")
tags.append("no-remote-exec")
tags.append("no-sandbox")
attrs = {
"args": args,
"data": data,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,39 @@

"Set defaults for the pip-compile command to run it under Bazel"

import atexit
import os
import re
import shutil
import sys
from pathlib import Path
from shutil import copyfile

import piptools.writer as piptools_writer
from piptools.scripts.compile import cli

# Replace the os.replace function with shutil.copy to work around os.replace not being able to
# replace or move files across filesystems.
os.replace = shutil.copy

# Next, we override the annotation_style_split and annotation_style_line functions to replace the
# backslashes in the paths with forward slashes. This is so that we can have the same requirements
# file on Windows and Unix-like.
original_annotation_style_split = piptools_writer.annotation_style_split
original_annotation_style_line = piptools_writer.annotation_style_line


def annotation_style_split(required_by) -> str:
required_by = set([v.replace("\\", "/") for v in required_by])
return original_annotation_style_split(required_by)


def annotation_style_line(required_by) -> str:
required_by = set([v.replace("\\", "/") for v in required_by])
return original_annotation_style_line(required_by)


piptools_writer.annotation_style_split = annotation_style_split
piptools_writer.annotation_style_line = annotation_style_line


def _select_golden_requirements_file(
requirements_txt, requirements_linux, requirements_darwin, requirements_windows
Expand All @@ -41,19 +66,6 @@ def _select_golden_requirements_file(
return requirements_txt


def _fix_up_requirements_in_path(absolute_prefix, output_file):
"""Fix up references to the input file inside of the generated requirements file.

We don't want fully resolved, absolute paths in the generated requirements file.
The paths could differ for every invocation. Replace them with a predictable path.
"""
output_file = Path(output_file)
contents = output_file.read_text()
contents = contents.replace(absolute_prefix, "")
contents = re.sub(r"\\(?!(\n|\r\n))", "/", contents)
output_file.write_text(contents)


if __name__ == "__main__":
if len(sys.argv) < 4:
print(
Expand All @@ -75,7 +87,6 @@ def _fix_up_requirements_in_path(absolute_prefix, output_file):
# absolute prefixes in the locked requirements output file.
requirements_in_path = Path(requirements_in)
resolved_requirements_in = str(requirements_in_path.resolve())
absolute_prefix = resolved_requirements_in[: -len(str(requirements_in_path))]

# Before loading click, set the locale for its parser.
# If it leaks through to the system setting, it may fail:
Expand All @@ -86,7 +97,7 @@ def _fix_up_requirements_in_path(absolute_prefix, output_file):
os.environ["LANG"] = "C.UTF-8"

UPDATE = True
# Detect if we are running under `bazel test`
# Detect if we are running under `bazel test`.
if "TEST_TMPDIR" in os.environ:
UPDATE = False
# pip-compile wants the cache files to be writeable, but if we point
Expand All @@ -95,31 +106,13 @@ def _fix_up_requirements_in_path(absolute_prefix, output_file):
# In theory this makes the test more hermetic as well.
sys.argv.append("--cache-dir")
sys.argv.append(os.environ["TEST_TMPDIR"])
# Make a copy for pip-compile to read and mutate
# Make a copy for pip-compile to read and mutate.
requirements_out = os.path.join(
os.environ["TEST_TMPDIR"], os.path.basename(requirements_txt) + ".out"
)
copyfile(requirements_txt, requirements_out)

elif "BUILD_WORKSPACE_DIRECTORY" in os.environ:
# This value, populated when running under `bazel run`, is a path to the
# "root of the workspace where the build was run."
# This matches up with the values passed in via the macro using the 'rootpath' Make variable,
# which for source files provides a path "relative to your workspace root."
#
# Changing to the WORKSPACE root avoids 'file not found' errors when the `.update` target is run
# from different directories within the WORKSPACE.
os.chdir(os.environ["BUILD_WORKSPACE_DIRECTORY"])
else:
err_msg = (
"Expected to find BUILD_WORKSPACE_DIRECTORY (running under `bazel run`) or "
"TEST_TMPDIR (running under `bazel test`) in environment."
)
print(
err_msg,
file=sys.stderr,
)
sys.exit(1)
# Those two files won't necessarily be on the same filesystem, so we can't use os.replace
# or shutil.copyfile, as they will fail with OSError: [Errno 18] Invalid cross-device link.
shutil.copy(requirements_txt, requirements_out)

update_command = os.getenv("CUSTOM_COMPILE_COMMAND") or "bazel run %s" % (
update_target_label,
Expand All @@ -137,12 +130,17 @@ def _fix_up_requirements_in_path(absolute_prefix, output_file):

if UPDATE:
print("Updating " + requirements_txt)
try:
cli()
except SystemExit as e:
if e.code == 0:
_fix_up_requirements_in_path(absolute_prefix, requirements_txt)
raise
if "BUILD_WORKSPACE_DIRECTORY" in os.environ:
workspace = os.environ["BUILD_WORKSPACE_DIRECTORY"]
requirements_txt_tree = os.path.join(workspace, requirements_txt)
# In most cases, requirements_txt will be a symlink to the real file in the source tree.
# If symlinks are not enabled (e.g. on Windows), then requirements_txt will be a copy,
# and we should copy the updated requirements back to the source tree.
if not os.path.samefile(requirements_txt, requirements_txt_tree):
atexit.register(
lambda: shutil.copy(requirements_txt, requirements_txt_tree)
)
cli()
else:
# cli will exit(0) on success
try:
Expand All @@ -160,7 +158,6 @@ def _fix_up_requirements_in_path(absolute_prefix, output_file):
)
sys.exit(1)
elif e.code == 0:
_fix_up_requirements_in_path(absolute_prefix, requirements_out)
golden_filename = _select_golden_requirements_file(
requirements_txt,
requirements_linux,
Expand Down
3 changes: 2 additions & 1 deletion tests/compile_pip_requirements/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@ EOF
compile_pip_requirements(
name = "requirements",
data = [
"requirements.in",
"requirements_extra.in",
],
extra_args = [
"--allow-unsafe",
"--resolver=backtracking",
],
requirements_in = "requirements.in",
requirements_in = "requirements.txt",
requirements_txt = "requirements_lock.txt",
)
1 change: 1 addition & 0 deletions tests/compile_pip_requirements/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-r requirements.in
8 changes: 0 additions & 8 deletions tools/publish/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,4 @@ compile_pip_requirements(
name = "requirements",
requirements_darwin = "requirements_darwin.txt",
requirements_windows = "requirements_windows.txt",
# This fails on RBE right now, and we don't need coverage there:
# WARNING: Retrying (Retry(total=0, connect=None, read=None, redirect=None, status=None))
# after connection broken by 'NewConnectionError('<pip._vendor.urllib3.connection.HTTPSConnection object at 0x7f3784e08110>:
# Failed to establish a new connection: [Errno -3] Temporary failure in name resolution')': /simple/twine/
#
# ERROR: Could not find a version that satisfies the requirement twine==4.0.2
# (from -r tools/publish/requirements.in (line 1)) (from versions: none)
tags = ["no-remote-exec"],
)