diff --git a/src/launchpad/utils/android/bundletool.py b/src/launchpad/utils/android/bundletool.py index f709c9b1..bb419bff 100644 --- a/src/launchpad/utils/android/bundletool.py +++ b/src/launchpad/utils/android/bundletool.py @@ -170,6 +170,10 @@ def build_apks( with tempfile.TemporaryDirectory(prefix="bundletool-") as temp_dir: temp_dir_path = Path(temp_dir) temp_apks_path = temp_dir_path / "apks.apks" + device_spec_path = temp_dir_path / "device-spec.json" + with open(device_spec_path, "w", encoding="utf-8") as device_spec_file: + json.dump(device_spec.model_dump(by_alias=True), device_spec_file) + build_apks_command = [ "build-apks", f"--bundle={bundle_path}", @@ -178,6 +182,10 @@ def build_apks( if universal_apk: build_apks_command.append("--mode=universal") + else: + # Restrict bundletool to APKs matching the target device instead + # of generating every possible split before extract-apks filters them. + build_apks_command.append(f"--device-spec={device_spec_path}") # Generate keystore and sign APKs if requested if sign_apks: @@ -195,10 +203,6 @@ def build_apks( logger.debug("APKs will be signed with generated keystore") - device_spec_path = temp_dir_path / "device-spec.json" - with open(device_spec_path, "w", encoding="utf-8") as device_spec_file: - json.dump(device_spec.model_dump(by_alias=True), device_spec_file) - self._run_command(build_apks_command) # Extract APKs for the specified device diff --git a/tests/unit/utils/android/test_bundletool.py b/tests/unit/utils/android/test_bundletool.py new file mode 100644 index 00000000..5afd2f02 --- /dev/null +++ b/tests/unit/utils/android/test_bundletool.py @@ -0,0 +1,45 @@ +from pathlib import Path + +import pytest + +from launchpad.utils.android.bundletool import Bundletool, DeviceSpec + + +@pytest.fixture +def bundletool(mocker) -> Bundletool: + mocker.patch("launchpad.utils.android.bundletool.shutil.which", return_value="/usr/local/bin/bundletool") + tool = Bundletool() + mocker.patch.object(tool, "_generate_keystore", return_value=("password", "alias")) + mocker.patch.object(tool, "_run_command") + return tool + + +def test_build_apks_targets_device_during_build(bundletool: Bundletool, tmp_path: Path) -> None: + bundletool.build_apks( + bundle_path=tmp_path / "app.aab", + output_dir=tmp_path / "output", + device_spec=DeviceSpec(), + ) + + build_command = bundletool._run_command.call_args_list[0].args[0] + extract_command = bundletool._run_command.call_args_list[1].args[0] + + assert "--mode=universal" not in build_command + device_spec_arg = next(argument for argument in build_command if argument.startswith("--device-spec=")) + assert device_spec_arg in extract_command + + +def test_build_universal_apk_does_not_target_device_during_build(bundletool: Bundletool, tmp_path: Path) -> None: + bundletool.build_apks( + bundle_path=tmp_path / "app.aab", + output_dir=tmp_path / "output", + device_spec=DeviceSpec(), + universal_apk=True, + ) + + build_command = bundletool._run_command.call_args_list[0].args[0] + extract_command = bundletool._run_command.call_args_list[1].args[0] + + assert "--mode=universal" in build_command + assert not any(argument.startswith("--device-spec=") for argument in build_command) + assert any(argument.startswith("--device-spec=") for argument in extract_command)