Skip to content

Commit c191ed4

Browse files
authored
Pypi updates to the bzlmod migration script (#6584)
This PR involves: * Ignore repos starting with `pypi_` from direct deps since they are usually not referenced directly. If referenced directly, these repos will be fixed in the second part of the script ("parsing the error" part). * If referenced repo starts with `pypi_<name>`, raise an error since it needs to be changed to `pypi//<name>`. * Update of a python test to cover these new cases. * Update of golden files in the test environment. * Smaller updates to the script, usually due to readability.
1 parent 5141a0b commit c191ed4

File tree

10 files changed

+253
-89
lines changed

10 files changed

+253
-89
lines changed

.bazelci/migration_tool_presubmit.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ tasks:
1616
name: python
1717
platform: ${{ platform }}
1818
shell_commands:
19-
- cd tools/bzlmod_migration_test_examples/py_extension && python3 ../migration_test.py
19+
- cd tools/bzlmod_migration_test_examples/py_extension && python3 migration_test.py
2020
simple_deps_extension:
2121
name: simple_deps
2222
platform: ${{ platform }}

tools/bzlmod_migration_test_examples/migration_test.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
class BazelBuildTest(unittest.TestCase):
99
"""
10-
A test suite for verifying Bzlmod migration tool for maven and python extensions.
10+
A test suite for verifying Bzlmod migration tool for maven and module extensions.
1111
"""
1212

1313
_CREATED_FILES = [
@@ -17,16 +17,21 @@ class BazelBuildTest(unittest.TestCase):
1717
"migration_info.md",
1818
"query_direct_deps",
1919
"resolved_deps.py",
20-
"extension_for_",
2120
]
2221

2322
def _cleanup_created_files(self):
2423
"""
2524
Remove files which were created by migration tool.
2625
"""
26+
for file_name in self._CREATED_FILES:
27+
file_path = os.path.join(os.getcwd(), file_name)
28+
if os.path.exists(file_path):
29+
os.remove(file_path)
30+
31+
# Remove all created extensions.
2732
my_dir = os.getcwd()
2833
for fname in os.listdir(my_dir):
29-
if any(fname.startswith(p) for p in self._CREATED_FILES):
34+
if fname.startswith("extension_for_"):
3035
os.remove(os.path.join(my_dir, fname))
3136

3237
def _run_command(self, command):

tools/bzlmod_migration_test_examples/py_extension/BUILD

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,8 @@ py_binary(
1616
"@my_deps_2//attrs"
1717
],
1818
)
19+
20+
alias(
21+
name = "yamllint",
22+
actual = "@pypi_yamllint//:rules_python_wheel_entry_point_yamllint",
23+
)
Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,30 @@
11
module(name = "py_extension", version="")
22

3-
bazel_dep(name = "rules_python", version = "1.6.3")
3+
bazel_dep(name = "rules_python", version = "1.7.0")
44
# -- bazel_dep definitions -- #
55

66
# -- use_repo_rule statements -- #
77

88

99
pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip")
1010

11+
pip.parse(
12+
hub_name = "pypi",
13+
requirements_lock = "//:requirements_lock_pypi.txt",
14+
python_version = "3.11",
15+
)
16+
use_repo(pip, "pypi")
17+
python = use_extension("@rules_python//python/extensions:python.bzl", "python")
18+
python.defaults(python_version = "3.11")
19+
python.toolchain(python_version = "3.11")
20+
1121
pip.parse(
1222
hub_name = "my_deps_2",
1323
requirements_lock = "//:requirements_lock_2.txt",
1424
python_version = "3.11",
1525
)
1626
use_repo(pip, "my_deps_2")
1727

18-
python = use_extension("@rules_python//python/extensions:python.bzl", "python")
19-
python.defaults(python_version = "3.11")
20-
python.toolchain(python_version = "3.11")
2128

2229
pip.parse(
2330
hub_name = "my_deps_1",
@@ -26,7 +33,6 @@ pip.parse(
2633
)
2734
use_repo(pip, "my_deps_1")
2835

29-
3036
# -- End of pip extensions -- #
3137

3238
# -- repo definitions -- #

tools/bzlmod_migration_test_examples/py_extension/WORKSPACE

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,21 @@ pip_parse(
3333
requirements_lock = "@py_extension//:requirements_lock_2.txt",
3434
)
3535

36+
pip_parse(
37+
name = "pypi",
38+
python_interpreter_target = "@python_3_11_host//:python",
39+
requirements_lock = "//:requirements_lock_pypi.txt",
40+
)
41+
3642
load("@my_deps_1//:requirements.bzl", install_deps_1 = "install_deps")
3743
install_deps_1()
3844

3945
load("@my_deps_2//:requirements.bzl", install_deps_2 = "install_deps")
4046
install_deps_2()
4147

48+
load("@pypi//:requirements.bzl", install_deps_pypi = "install_deps")
49+
install_deps_pypi()
50+
4251
# Python toolchains
4352
# -------------------
4453
load("@rules_python//python:repositories.bzl", "python_register_toolchains", "python_register_multi_toolchains")

tools/bzlmod_migration_test_examples/py_extension/migration_info.md.golden

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ Command for local testing:
44
bazel build --enable_bzlmod --noenable_workspace //...
55
```
66
## Direct dependencies:
7+
* pypi
8+
* rules_python
9+
* pypi_yamllint
710
* my_deps_2
811
* my_deps_1
9-
* rules_python
1012
## Migration of `rules_python`:
1113

1214
<details>
@@ -15,9 +17,9 @@ bazel build --enable_bzlmod --noenable_workspace //...
1517
#### Location
1618
```python
1719
Repository rules_python instantiated at:
18-
/usr/local/google/home/kotlaja/github/bazel-central-registry/tools/bzlmod_migration_test_examples/py_extension/WORKSPACE:9:13: in <toplevel>
20+
/usr/local/google/home/kotlaja/github/bazel_central_registry_at_head/bazel-central-registry/tools/bzlmod_migration_test_examples/py_extension/WORKSPACE:9:13: in <toplevel>
1921
Repository rule http_archive defined at:
20-
/usr/local/google/home/kotlaja/.cache/bazel/_bazel_kotlaja/5f3795b531c0dd1f2cc34c69d9c35fed/external/bazel_tools/tools/build_defs/repo/http.bzl:387:31: in <toplevel>
22+
/usr/local/google/home/kotlaja/.cache/bazel/_bazel_kotlaja/1236e0e34acc2577fcfb7bd9f570e739/external/bazel_tools/tools/build_defs/repo/http.bzl:387:31: in <toplevel>
2123

2224
```
2325

@@ -41,35 +43,22 @@ Found partially name matches in BCR: `rules_python_gazelle_plugin`
4143

4244
It has been introduced as a Bazel module:
4345

44-
bazel_dep(name = "rules_python", version = "1.6.3")
45-
## Migration of `my_deps_2`
46+
bazel_dep(name = "rules_python", version = "1.7.0")
47+
## Migration of `pypi`
4648
It has been introduced as a python extension, with python_version=3.11:
4749

4850
```
4951
pip.parse(
50-
hub_name = "my_deps_2",
51-
requirements_lock = "//:requirements_lock_2.txt",
52+
hub_name = "pypi",
53+
requirements_lock = "//:requirements_lock_pypi.txt",
5254
python_version = "3.11",
5355
)
54-
use_repo(pip, "my_deps_2")
55-
56+
use_repo(pip, "pypi")
5657
python = use_extension("@rules_python//python/extensions:python.bzl", "python")
5758
python.defaults(python_version = "3.11")
5859
python.toolchain(python_version = "3.11")
5960
```
60-
## Migration of `my_deps_1`
61-
It has been introduced as a python extension, with python_version=3.11:
6261

63-
```
64-
pip.parse(
65-
hub_name = "my_deps_1",
66-
requirements_lock = "//:requirements_lock_1.txt",
67-
python_version = "3.11",
68-
)
69-
use_repo(pip, "my_deps_1")
70-
71-
72-
```
7362
## Migration of `rules_python`:
7463

7564
<details>
@@ -78,9 +67,9 @@ use_repo(pip, "my_deps_1")
7867
#### Location
7968
```python
8069
Repository rules_python instantiated at:
81-
/usr/local/google/home/kotlaja/github/bazel-central-registry/tools/bzlmod_migration_test_examples/py_extension/WORKSPACE:9:13: in <toplevel>
70+
/usr/local/google/home/kotlaja/github/bazel_central_registry_at_head/bazel-central-registry/tools/bzlmod_migration_test_examples/py_extension/WORKSPACE:9:13: in <toplevel>
8271
Repository rule http_archive defined at:
83-
/usr/local/google/home/kotlaja/.cache/bazel/_bazel_kotlaja/5f3795b531c0dd1f2cc34c69d9c35fed/external/bazel_tools/tools/build_defs/repo/http.bzl:387:31: in <toplevel>
72+
/usr/local/google/home/kotlaja/.cache/bazel/_bazel_kotlaja/1236e0e34acc2577fcfb7bd9f570e739/external/bazel_tools/tools/build_defs/repo/http.bzl:387:31: in <toplevel>
8473

8574
```
8675

@@ -103,3 +92,29 @@ Found perfect name match in BCR: `rules_python`
10392
Found partially name matches in BCR: `rules_python_gazelle_plugin`
10493

10594
This module has already been added inside the MODULE.bazel file
95+
## Migration of `my_deps_2`
96+
It has been introduced as a python extension, with python_version=3.11:
97+
98+
```
99+
pip.parse(
100+
hub_name = "my_deps_2",
101+
requirements_lock = "//:requirements_lock_2.txt",
102+
python_version = "3.11",
103+
)
104+
use_repo(pip, "my_deps_2")
105+
106+
```
107+
108+
## Migration of `my_deps_1`
109+
It has been introduced as a python extension, with python_version=3.11:
110+
111+
```
112+
pip.parse(
113+
hub_name = "my_deps_1",
114+
requirements_lock = "//:requirements_lock_1.txt",
115+
python_version = "3.11",
116+
)
117+
use_repo(pip, "my_deps_1")
118+
119+
```
120+
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import unittest
2+
import subprocess
3+
import os
4+
import sys
5+
from unittest import main
6+
7+
8+
class BazelBuildTest(unittest.TestCase):
9+
"""
10+
A test suite for verifying Bzlmod migration tool for python extensions.
11+
"""
12+
13+
_CREATED_FILES = [
14+
"MODULE.bazel",
15+
"MODULE.bazel.lock",
16+
"WORKSPACE.bzlmod",
17+
"migration_info.md",
18+
"query_direct_deps",
19+
"resolved_deps.py",
20+
]
21+
22+
def _cleanup_created_files(self):
23+
"""
24+
Remove files which were created by migration tool.
25+
"""
26+
for file_name in self._CREATED_FILES:
27+
file_path = os.path.join(os.getcwd(), file_name)
28+
if os.path.exists(file_path):
29+
os.remove(file_path)
30+
31+
def _run_command(self, command, expected_failure=False):
32+
"""
33+
Helper function to run a command and return its result.
34+
It captures `stdout`, `stderr` and `returncode` for debugging.
35+
"""
36+
try:
37+
result = subprocess.run(command, capture_output=True, text=True, check=True)
38+
return result
39+
except FileNotFoundError:
40+
self.fail("Command not found.")
41+
except subprocess.CalledProcessError as e:
42+
if expected_failure:
43+
return e
44+
self.fail(f"Command failed with exit code {e.returncode}:\nSTDOUT:\n{e.stdout}\nSTDERR:\n{e.stderr}")
45+
46+
def _print_message(self, message):
47+
GREEN = "\033[92m"
48+
RESET = "\033[0m"
49+
print(f"{GREEN}{message}{RESET}")
50+
51+
def modify_build_file(self, old, new):
52+
with open("BUILD", "r") as f:
53+
original_content = f.read()
54+
with open("BUILD", "w") as f:
55+
modified_content = str(original_content).replace(old, new)
56+
f.write(modified_content)
57+
58+
def test_migration_of_module_deps(self):
59+
self._cleanup_created_files()
60+
try:
61+
targets = "//..."
62+
63+
# Verify bazel build is successful with enabled workspace
64+
print("\n--- Running bazel build with enabled workspace ---")
65+
result = self._run_command(
66+
["bazel", "build", "--nobuild", "--enable_workspace", "--noenable_bzlmod", targets]
67+
)
68+
assert result.returncode == 0
69+
self._print_message("Success.")
70+
71+
# Run migration script
72+
print("\n--- Running migration script ---")
73+
result = self._run_command(
74+
[sys.executable, "../../migrate_to_bzlmod.py", "-t=//..."], expected_failure=True
75+
)
76+
assert result.returncode == 1
77+
assert "Update pip dependency reference from" in result.stderr
78+
assert os.path.exists(
79+
"migration_info.md"
80+
), "File 'migration_info.md' should be created during migration, but it doesn't exist."
81+
82+
self._print_message("Expected error: User need to modify pypi reference.")
83+
84+
# Verify Bzlmod have error
85+
print("\n--- Running bazel build with enabled bzlmod ---")
86+
result = self._run_command(
87+
["bazel", "build", "--noenable_workspace", "--enable_bzlmod", "//..."], expected_failure=True
88+
)
89+
assert result.returncode == 1
90+
self._print_message("Expected error: Manual change for pypi reference is needed.")
91+
92+
# Modify BUILD file
93+
self.modify_build_file("@pypi_yamllint//:rules_python_wheel_entry_point_yamllint", "@pypi//yamllint")
94+
print("\n--- Modifying BUILD file ---")
95+
self._print_message("Success.")
96+
97+
# Rerun migration script
98+
print("\n--- Running migration script ---")
99+
result = self._run_command([sys.executable, "../../migrate_to_bzlmod.py", "-t=//..."])
100+
assert result.returncode == 0
101+
self._print_message("Success.")
102+
103+
# Verify MODULE.bazel was created successfully
104+
print("\n--- Running bazel build with enabled bzlmod ---")
105+
result = self._run_command(["bazel", "build", "--noenable_workspace", "--enable_bzlmod", "//..."])
106+
assert result.returncode == 0
107+
self._print_message("Success.")
108+
finally:
109+
# Restore BUILD file to the initial content
110+
self.modify_build_file("@pypi//yamllint", "@pypi_yamllint//:rules_python_wheel_entry_point_yamllint")
111+
self._cleanup_created_files()
112+
113+
114+
if __name__ == "__main__":
115+
main()
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
yamllint==1.37.1
2+
PyYAML==6.0.2
3+
pathspec==0.12.1

0 commit comments

Comments
 (0)