Skip to content

Commit 4e034a5

Browse files
authored
feat: locate and extract bazel artifact (#14144)
1 parent 3d207a4 commit 4e034a5

File tree

2 files changed

+98
-1
lines changed

2 files changed

+98
-1
lines changed

.generator/cli.py

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
LIBRARIAN_DIR = "librarian"
2525
GENERATE_REQUEST_FILE = "generate-request.json"
2626
SOURCE_DIR = "source"
27+
OUTPUT_DIR = "output"
2728

2829

2930
def _read_json_file(path):
@@ -97,7 +98,6 @@ def _build_bazel_target(bazel_rule):
9798
subprocess.run(
9899
command,
99100
cwd=f"{SOURCE_DIR}/googleapis",
100-
capture_output=True,
101101
text=True,
102102
check=True,
103103
)
@@ -106,6 +106,53 @@ def _build_bazel_target(bazel_rule):
106106
raise ValueError(f"Bazel build for {bazel_rule} rule failed.") from e
107107

108108

109+
def _locate_and_extract_artifact(bazel_rule: str, library_id: str):
110+
"""Finds and extracts the tarball artifact from a Bazel build.
111+
112+
Args:
113+
bazel_rule (str): The Bazel rule that was built.
114+
library_id (str): The ID of the library being generated.
115+
116+
Raises:
117+
ValueError: If failed to locate or extract artifact.
118+
"""
119+
try:
120+
# 1. Find the bazel-bin output directory.
121+
logger.info("Locating Bazel output directory...")
122+
info_command = ["bazelisk", "info", "bazel-bin"]
123+
result = subprocess.run(
124+
info_command,
125+
cwd=f"{SOURCE_DIR}/googleapis",
126+
text=True,
127+
check=True,
128+
capture_output=True,
129+
)
130+
bazel_bin_path = result.stdout.strip()
131+
132+
# 2. Construct the path to the generated tarball.
133+
rule_path, rule_name = bazel_rule.split(":")
134+
tarball_name = f"{rule_name}.tar.gz"
135+
tarball_path = os.path.join(bazel_bin_path, rule_path.strip("/"), tarball_name)
136+
logger.info(f"Found artifact at: {tarball_path}")
137+
138+
# 3. Create a staging directory.
139+
staging_dir = os.path.join(OUTPUT_DIR, "owl-bot-staging", library_id)
140+
os.makedirs(staging_dir, exist_ok=True)
141+
logger.info(f"Preparing staging directory: {staging_dir}")
142+
143+
# 4. Extract the artifact.
144+
extract_command = ["tar", "-xvf", tarball_path, "--strip-components=1"]
145+
subprocess.run(
146+
extract_command, cwd=staging_dir, capture_output=True, text=True, check=True
147+
)
148+
logger.info(f"Artifact {tarball_path} extracted successfully.")
149+
150+
except Exception as e:
151+
raise ValueError(
152+
f"Failed to locate or extract artifact for {bazel_rule} rule"
153+
) from e
154+
155+
109156
def handle_generate():
110157
"""The main coordinator for the code generation process.
111158
@@ -129,6 +176,7 @@ def handle_generate():
129176
if api_path:
130177
bazel_rule = _determine_bazel_rule(api_path)
131178
_build_bazel_target(bazel_rule)
179+
_locate_and_extract_artifact(bazel_rule, library_id)
132180

133181
logger.info(json.dumps(request_data, indent=2))
134182
except Exception as e:

.generator/test_cli.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
_read_json_file,
2525
_determine_bazel_rule,
2626
_build_bazel_target,
27+
_locate_and_extract_artifact,
2728
handle_generate,
2829
handle_build,
2930
handle_configure,
@@ -118,6 +119,52 @@ def test_determine_bazel_rule_command_fails(mocker, caplog):
118119
assert "Found Bazel rule" not in caplog.text
119120

120121

122+
def test_locate_and_extract_artifact_success(mocker, caplog):
123+
"""
124+
Tests that the artifact helper calls the correct sequence of commands.
125+
"""
126+
caplog.set_level(logging.INFO)
127+
mock_info_result = MagicMock(stdout="/path/to/bazel-bin\n")
128+
mock_tar_result = MagicMock(returncode=0)
129+
mocker.patch("cli.subprocess.run", side_effect=[mock_info_result, mock_tar_result])
130+
mock_makedirs = mocker.patch("cli.os.makedirs")
131+
_locate_and_extract_artifact(
132+
"//google/cloud/language/v1:rule-py",
133+
"google-cloud-language",
134+
)
135+
136+
assert (
137+
"Found artifact at: /path/to/bazel-bin/google/cloud/language/v1/rule-py.tar.gz"
138+
in caplog.text
139+
)
140+
assert (
141+
"Preparing staging directory: output/owl-bot-staging/google-cloud-language"
142+
in caplog.text
143+
)
144+
assert (
145+
"Artifact /path/to/bazel-bin/google/cloud/language/v1/rule-py.tar.gz extracted successfully"
146+
in caplog.text
147+
)
148+
mock_makedirs.assert_called_once()
149+
150+
151+
def test_locate_and_extract_artifact_fails(mocker, caplog):
152+
"""
153+
Tests that an exception is raised if the subprocess command fails.
154+
"""
155+
caplog.set_level(logging.INFO)
156+
mocker.patch(
157+
"cli.subprocess.run",
158+
side_effect=subprocess.CalledProcessError(1, "cmd", stderr="Bazel error"),
159+
)
160+
161+
with pytest.raises(ValueError):
162+
_locate_and_extract_artifact(
163+
"//google/cloud/language/v1:rule-py",
164+
"google-cloud-language",
165+
)
166+
167+
121168
def test_handle_generate_success(caplog, mock_generate_request_file, mocker):
122169
"""
123170
Tests the successful execution path of handle_generate.
@@ -129,6 +176,8 @@ def test_handle_generate_success(caplog, mock_generate_request_file, mocker):
129176
)
130177
mock_build_target = mocker.patch("cli._build_bazel_target")
131178

179+
mock_locate_and_extract_artifact = mocker.patch("cli._locate_and_extract_artifact")
180+
132181
handle_generate()
133182

134183
mock_determine_rule.assert_called_once_with("google/cloud/language/v1")

0 commit comments

Comments
 (0)