diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index dae69d701..0ed18e0b3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,7 +14,7 @@ repos: - id: isort additional_dependencies: [toml] - repo: https://github.com/asottile/pyupgrade - rev: v3.9.0 + rev: v3.10.1 hooks: - id: pyupgrade args: [--py38-plus] diff --git a/changes/1397.feature.rst b/changes/1397.feature.rst new file mode 100644 index 000000000..fa95440f2 --- /dev/null +++ b/changes/1397.feature.rst @@ -0,0 +1 @@ +The Android SDK's Command-Line Tools is now version 9.0. If an externally-managed Android SDK is being used, it must provide this version of Command-Line Tools. Use the SDK Manager in Android Studio to ensure it is installed. diff --git a/changes/1398.feature.rst b/changes/1398.feature.rst new file mode 100644 index 000000000..25d790abd --- /dev/null +++ b/changes/1398.feature.rst @@ -0,0 +1 @@ +The Java JDK version was upgraded from 17.0.7+7 to 17.0.8+7. diff --git a/changes/1400.misc.rst b/changes/1400.misc.rst new file mode 100644 index 000000000..9eb46067a --- /dev/null +++ b/changes/1400.misc.rst @@ -0,0 +1 @@ +The ``pre-commit`` hook for ``pyupgrade`` was updated to its latest version. diff --git a/changes/1401.misc.rst b/changes/1401.misc.rst new file mode 100644 index 000000000..38404a718 --- /dev/null +++ b/changes/1401.misc.rst @@ -0,0 +1 @@ +Updated sphinx from 7.1.1 to 7.1.2. diff --git a/docs/reference/platforms/android.rst b/docs/reference/platforms/android.rst index 17ac5259e..d08ed8c80 100644 --- a/docs/reference/platforms/android.rst +++ b/docs/reference/platforms/android.rst @@ -9,7 +9,9 @@ Gradle requires an install of the Android SDK and a Java 17 JDK. If you have an existing install of the Android SDK, it will be used by Briefcase if the ``ANDROID_HOME`` environment variable is set. If ``ANDROID_HOME`` is not present in the environment, Briefcase will honor the deprecated -``ANDROID_SDK_ROOT`` environment variable. +``ANDROID_SDK_ROOT`` environment variable. Additionally, an existing SDK install +must have version 9.0 of Command-line Tools installed; this version can be +installed in the SDK Manager in Android Studio. If you have an existing install of a Java 17 JDK, it will be used by Briefcase if the ``JAVA_HOME`` environment variable is set. On macOS, if ``JAVA_HOME`` is diff --git a/setup.cfg b/setup.cfg index a01cdc2e3..f72e164fe 100644 --- a/setup.cfg +++ b/setup.cfg @@ -98,7 +98,7 @@ dev = docs = furo == 2023.7.26 pyenchant == 3.2.2 - sphinx == 7.1.1 + sphinx == 7.1.2 sphinx_tabs == 3.4.1 sphinx-autobuild == 2021.3.14 sphinx-copybutton == 0.5.2 diff --git a/src/briefcase/integrations/android_sdk.py b/src/briefcase/integrations/android_sdk.py index 97b4f9056..ef3d7813d 100644 --- a/src/briefcase/integrations/android_sdk.py +++ b/src/briefcase/integrations/android_sdk.py @@ -48,6 +48,11 @@ class AndroidSDK(ManagedTool): name = "android_sdk" full_name = "Android SDK" + # Latest version for Command-Line Tools download as of August 2023 + # **Be sure the android.rst docs stay in sync with version updates here** + SDK_MANAGER_DOWNLOAD_VER = "9477386" + SDK_MANAGER_VER = "9.0" + def __init__(self, tools: ToolCache, root_path: Path): super().__init__(tools=tools) self.dot_android_path = self.tools.home_path / ".android" @@ -84,33 +89,22 @@ def cmdline_tools_url(self) -> str: ) from e return ( - f"https://dl.google.com/android/repository/commandlinetools-" - f"{platform_name}-{self.cmdline_tools_version}_latest.zip" + f"https://dl.google.com/android/repository/" + f"commandlinetools-{platform_name}-{self.SDK_MANAGER_DOWNLOAD_VER}_latest.zip" ) @property def cmdline_tools_path(self) -> Path: - return self.root_path / "cmdline-tools" / "latest" - - @property - def cmdline_tools_version(self) -> str: - # This is the version of the Android SDK Command-line tools that - # are current as of May 2022. These tools can generally self-update, - # so using a fixed download URL isn't a problem. - # However, if/when this version number is changed, ensure that the - # checks done during verification include any required upgrade steps. - return "8092744" + """Version-specific Command-line tools install root directory.""" + return self.root_path / "cmdline-tools" / self.SDK_MANAGER_VER @property - def cmdline_tools_version_path(self) -> Path: - return self.root_path / "cmdline-tools" / self.cmdline_tools_version + def sdkmanager_filename(self) -> str: + return "sdkmanager.bat" if self.tools.host_os == "Windows" else "sdkmanager" @property def sdkmanager_path(self) -> Path: - sdkmanager = ( - "sdkmanager.bat" if self.tools.host_os == "Windows" else "sdkmanager" - ) - return self.cmdline_tools_path / "bin" / sdkmanager + return self.cmdline_tools_path / "bin" / self.sdkmanager_filename @property def adb_path(self) -> Path: @@ -247,15 +241,16 @@ def verify_install( JDK.verify(tools=tools, install=install) sdk = None - sdk_root, sdk_env_source = cls.sdk_path_from_env(tools=tools) - if sdk_root: + # Verify externally-managed Android SDK + sdk_root_env, sdk_source_env = cls.sdk_path_from_env(tools=tools) + if sdk_root_env: tools.logger.debug("Evaluating ANDROID_HOME...", prefix=cls.full_name) - tools.logger.debug(f"{sdk_env_source}={sdk_root}") - sdk = AndroidSDK(tools=tools, root_path=Path(sdk_root)) + tools.logger.debug(f"{sdk_source_env}={sdk_root_env}") + sdk = AndroidSDK(tools=tools, root_path=Path(sdk_root_env)) if sdk.exists(): - if sdk_env_source == "ANDROID_SDK_ROOT": + if sdk_source_env == "ANDROID_SDK_ROOT": tools.logger.warning( """ ************************************************************************* @@ -274,26 +269,49 @@ def verify_install( ************************************************************************* """ ) - sdk.verify_license() + elif sdk.cmdline_tools_path.parent.exists(): + # a cmdline-tools directory exists but the required version isn't installed. + # try to install the required version using the 'latest' version. + if not sdk.install_cmdline_tools(): + sdk = None + tools.logger.warning( + f""" +************************************************************************* +** WARNING: Incompatible Command-Line Tools Version ** +************************************************************************* + + The Android SDK specified by {sdk_source_env} at: + + {sdk_root_env} + + does not contain Command-Line Tools version {cls.SDK_MANAGER_VER}. Briefcase requires + this version to be installed to use an external Android SDK. + + Use Android Studio's SDK Manager to install Command-Line Tools {cls.SDK_MANAGER_VER}. + + Briefcase will proceed using its own SDK instance. + +************************************************************************* +""" + ) else: - sdk = None tools.logger.warning( f""" ************************************************************************* -** {f"WARNING: {sdk_env_source} does not point to an Android SDK":67} ** +** {f"WARNING: {sdk_source_env} does not point to an Android SDK":67} ** ************************************************************************* - The location pointed to by the {sdk_env_source} environment + The location pointed to by the {sdk_source_env} environment variable: - {sdk_root} + {sdk_root_env} doesn't appear to contain an Android SDK. - If {sdk_env_source} is an Android SDK, ensure it is the root directory + If {sdk_source_env} is an Android SDK, ensure it is the root directory of the Android SDK instance such that - ${sdk_env_source}/cmdline-tools/latest/bin/sdkmanager + ${sdk_source_env}{os.sep}{sdk.sdkmanager_path.relative_to(sdk.root_path)} is a valid filepath. @@ -302,53 +320,35 @@ def verify_install( ************************************************************************* """ ) + sdk = None + # Verify Briefcase-managed Android SDK if sdk is None: - # Build an SDK wrapper for the Briefcase SDK instance. sdk_root_path = tools.base_path / "android_sdk" sdk = AndroidSDK(tools=tools, root_path=sdk_root_path) - if sdk.exists(): - # NOTE: For now, all known versions of the cmdline-tools are compatible. - # If/when that ever changes, do a verification check here. - sdk.verify_license() - else: - # The legacy SDK Tools exist. Delete them. - if (sdk_root_path / "tools").exists(): - tools.logger.warning( - f""" -************************************************************************* -** WARNING: Upgrading Android SDK tools ** -************************************************************************* - - Briefcase needs to replace the older Android SDK Tools with the - newer Android SDK Command-Line Tools. This will involve some large - downloads, as well as re-accepting the licenses for the Android - SDKs. - - Any emulators created with the older Android SDK Tools will not be - compatible with the new tools. You will need to create new - emulators. Old emulators can be removed by deleting the files - in {sdk.avd_path} matching the emulator name. + if not sdk.exists(): + if not install: + raise MissingToolError("Android SDK") -************************************************************************* -""" - ) - tools.shutil.rmtree(sdk_root_path) + sdk.delete_legacy_sdk_tools() - if install: + if sdk.cmdline_tools_path.parent.exists(): + tools.logger.info("Upgrading Android SDK...", prefix=cls.name) + else: tools.logger.info( "The Android SDK was not found; downloading and installing...", prefix=cls.name, ) tools.logger.info( - "To use an existing Android SDK instance, " - "specify its root directory path in the ANDROID_HOME environment variable." + "To use an existing Android SDK instance, specify its root " + "directory path in the ANDROID_HOME environment variable." ) tools.logger.info() - sdk.install() - else: - raise MissingToolError("Android SDK") + sdk.install() + + # Licences must be accepted to use the SDK + sdk.verify_license() tools.logger.debug(f"Using Android SDK at {sdk.root_path}") tools.android_sdk = sdk @@ -374,7 +374,6 @@ def managed_install(self) -> bool: def uninstall(self): """The Android SDK is upgraded in-place instead of being reinstalled.""" - pass def install(self): """Download and install the Android SDK.""" @@ -385,18 +384,18 @@ def install(self): ) # The cmdline-tools package *must* be installed as: - # /cmdline-tools/latest + # /cmdline-tools/ # # However, the zip file unpacks a top-level folder named `cmdline-tools`. # So, the unpacking process is: # # 1. Make a /cmdline-tools folder # 2. Unpack the zip file into that folder, creating /cmdline-tools/cmdline-tools - # 3. Move /cmdline-tools/cmdline-tools to /cmdline-tools/latest - # 4. Drop a marker file named /cmdline-tools/ so we can track - # the version that was installed. + # 3. Move /cmdline-tools/cmdline-tools to /cmdline-tools/ - with self.tools.input.wait_bar("Installing Android SDK Command-Line Tools..."): + with self.tools.input.wait_bar( + f"Installing Android SDK Command-Line Tools {self.SDK_MANAGER_VER}..." + ): self.cmdline_tools_path.parent.mkdir(parents=True, exist_ok=True) try: self.tools.shutil.unpack_archive( @@ -412,18 +411,14 @@ def install(self): """ ) from e - # If there's an existing version of the cmdline tools (or the version marker), delete them. + # If there's an existing version of the cmdline tools, delete them. if self.cmdline_tools_path.exists(): self.tools.shutil.rmtree(self.cmdline_tools_path) - if self.cmdline_tools_version_path.exists(): - self.tools.os.unlink(self.cmdline_tools_version_path) # Rename the top level zip content to the final name (self.cmdline_tools_path.parent / "cmdline-tools").rename( self.cmdline_tools_path ) - # Touch a file with the version that was installed. - self.cmdline_tools_version_path.touch() # Zip file no longer needed once unpacked. cmdline_tools_zip_path.unlink() @@ -437,8 +432,8 @@ def install(self): if not self.tools.os.access(binpath, self.tools.os.X_OK): binpath.chmod(0o755) - # Licences must be accepted. - self.verify_license() + with self.tools.input.wait_bar("Removing older Android SDK packages..."): + self.cleanup_old_installs() def upgrade(self): """Upgrade the Android SDK.""" @@ -461,6 +456,92 @@ def upgrade(self): """ ) from e + def install_cmdline_tools(self) -> bool: + """Attempt to use 'latest' cmdline-tools to install the currently required + version of the Command-Line Tools. + + The Briefcase-managed SDK should always have the required version of cmdline- + tools installed; however, user-provided SDKs may not have it. + + :returns: True if successfully installed; False otherwise + """ + self.tools.logger.info( + f"Installing Android Command-Line Tools {self.SDK_MANAGER_VER}...", + prefix=self.full_name, + ) + self.tools.logger.info(f"Using Android SDK at {self.root_path}") + latest_sdkmanager_path = ( + self.root_path + / "cmdline-tools" + / "latest" + / "bin" + / self.sdkmanager_filename + ) + try: + self.tools.subprocess.run( + [ + latest_sdkmanager_path, + f"cmdline-tools;{self.SDK_MANAGER_VER}", + ], + check=True, + stream_output=False, + ) + except (OSError, subprocess.CalledProcessError) as e: + self.tools.logger.debug(str(e)) + self.tools.logger.warning( + f"Failed to install cmdline-tools;{self.SDK_MANAGER_VER}" + ) + return False + return True + + def delete_legacy_sdk_tools(self): + """Delete any legacy Android SDK tools that are installed. + + If no versions of the Command-Line Tools are installed but the 'tools' directory + exists, the legacy SDK Tools are probably installed. Since they have been + deprecated by more recent releases of SDK Manager, delete them and perform a + fresh install. + + The Android SDK Tools were deprecated in Sept 2017. + """ + if ( + not self.cmdline_tools_path.parent.exists() + and (self.root_path / "tools").exists() + ): + self.tools.logger.warning( + f""" +************************************************************************* +** WARNING: Upgrading Android SDK tools ** +************************************************************************* + + Briefcase needs to replace the older Android SDK Tools with the + newer Android SDK Command-Line Tools. This will involve some large + downloads, as well as re-accepting the licenses for the Android + SDKs. + + Any emulators created with the older Android SDK Tools will not be + compatible with the new tools. You will need to create new + emulators. Old emulators can be removed by deleting the files + in {self.avd_path} matching the emulator name. + +************************************************************************* +""" + ) + self.tools.shutil.rmtree(self.root_path) + + def cleanup_old_installs(self): + """Remove old versions of Android SDK packages and version markers. + + When the Android SDK is upgraded, old versions of packages should be removed to + keep the SDK tidy. This is namely the Command-line Tools that are used to manage + the SDK and AVDs. Additionally, previous version of Briefcase created a version + marker file that needs to be deleted. + """ + if (ver_file := self.cmdline_tools_path.parent / "8092744").is_file(): + self.tools.os.unlink(ver_file) + if (latest := self.cmdline_tools_path.parent / "latest").is_dir(): + self.tools.shutil.rmtree(latest) + def list_packages(self): """In debug output, list the packages currently managed by the SDK.""" try: diff --git a/src/briefcase/integrations/java.py b/src/briefcase/integrations/java.py index d8eb6234b..a9e3b4cd7 100644 --- a/src/briefcase/integrations/java.py +++ b/src/briefcase/integrations/java.py @@ -17,10 +17,9 @@ class JDK(ManagedTool): name = "java" full_name = "Java JDK" - # As of 12 May 2023, 17.0.7+7 is the current OpenJDK - # https://adoptium.net/temurin/releases/ + # Latest OpenJDK as of August 2023: https://adoptium.net/temurin/releases/ JDK_MAJOR_VER = "17" - JDK_RELEASE = "17.0.7" + JDK_RELEASE = "17.0.8" JDK_BUILD = "7" JDK_INSTALL_DIR_NAME = f"java{JDK_MAJOR_VER}" @@ -75,7 +74,7 @@ def version_from_path(cls, tools: ToolCache, java_path: str | Path) -> str: :param tools: ToolCache of available tools :param java_path: File path to a candidate JDK install - :return: JDK release version; e.g. "17.0.7" + :return: JDK release version; e.g. "17.0.X" """ output = tools.subprocess.check_output( [ @@ -83,7 +82,7 @@ def version_from_path(cls, tools: ToolCache, java_path: str | Path) -> str: "-version", ], ) - # javac's output should look like "javac 17.0.7\n" + # javac's output should look like "javac 17.0.X\n" return output.strip("\n").split(" ")[1] @classmethod @@ -303,7 +302,7 @@ def install(self): jdk_zip_path.unlink() # Zip file no longer needed once unpacked. - # The tarball will unpack into /tools/jdk-17.0.7+7 + # The tarball will unpack into /tools/jdk-17.0.X+7 # (or whatever name matches the current release). # We turn this into /tools/java so we have a consistent name. java_unpack_path = ( diff --git a/tests/integrations/android_sdk/AndroidSDK/test_properties.py b/tests/integrations/android_sdk/AndroidSDK/test_properties.py index 29f1ad787..c37e6e744 100644 --- a/tests/integrations/android_sdk/AndroidSDK/test_properties.py +++ b/tests/integrations/android_sdk/AndroidSDK/test_properties.py @@ -5,6 +5,8 @@ from briefcase.exceptions import BriefcaseCommandError +from ..conftest import SDK_MGR_DL_VER, SDK_MGR_VER + @pytest.mark.parametrize( "host_os, host_arch, name", @@ -21,7 +23,7 @@ def test_cmdline_tools_url(mock_tools, android_sdk, host_os, host_arch, name): mock_tools.host_arch = host_arch assert android_sdk.cmdline_tools_url == ( - f"https://dl.google.com/android/repository/commandlinetools-{name}-8092744_latest.zip" + f"https://dl.google.com/android/repository/commandlinetools-{name}-{SDK_MGR_DL_VER}_latest.zip" ) @@ -36,7 +38,7 @@ def test_sdkmanager_path(mock_tools, android_sdk, host_os, sdkmanager_name): mock_tools.host_os = host_os assert android_sdk.sdkmanager_path == ( - android_sdk.root_path / "cmdline-tools" / "latest" / "bin" / sdkmanager_name + android_sdk.root_path / "cmdline-tools" / SDK_MGR_VER / "bin" / sdkmanager_name ) @@ -63,7 +65,7 @@ def test_avdmanager_path(mock_tools, android_sdk, host_os, avdmanager_name): mock_tools.host_os = host_os assert android_sdk.avdmanager_path == ( - android_sdk.root_path / "cmdline-tools" / "latest" / "bin" / avdmanager_name + android_sdk.root_path / "cmdline-tools" / SDK_MGR_VER / "bin" / avdmanager_name ) diff --git a/tests/integrations/android_sdk/AndroidSDK/test_verify.py b/tests/integrations/android_sdk/AndroidSDK/test_verify.py index dc673dcb2..84c1ccb4c 100644 --- a/tests/integrations/android_sdk/AndroidSDK/test_verify.py +++ b/tests/integrations/android_sdk/AndroidSDK/test_verify.py @@ -15,6 +15,12 @@ from briefcase.integrations.android_sdk import AndroidSDK from briefcase.integrations.base import ToolCache +from ..conftest import SDK_MGR_DL_VER, SDK_MGR_VER + +SDKMANAGER_FILENAME = ( + "sdkmanager.bat" if platform.system() == "Windows" else "sdkmanager" +) + @pytest.fixture def mock_tools(mock_tools) -> ToolCache: @@ -23,6 +29,7 @@ def mock_tools(mock_tools) -> ToolCache: mock_tools.os.fsdecode = os.fsdecode mock_tools.os.access = os.access mock_tools.os.X_OK = os.X_OK + mock_tools.os.unlink = os.unlink # Identify the host platform mock_tools._test_download_tag = { @@ -106,7 +113,7 @@ def test_supported_os_arch(mock_tools, host_os, host_arch, tmp_path): # Create `sdkmanager` and the license file. android_sdk_root_path = tmp_path / "tools" / "android_sdk" - tools_bin = android_sdk_root_path / "cmdline-tools" / "latest" / "bin" + tools_bin = android_sdk_root_path / "cmdline-tools" / "9.0" / "bin" tools_bin.mkdir(parents=True, mode=0o755) if host_os == "Windows": sdk_manager = tools_bin / "sdkmanager.bat" @@ -127,19 +134,11 @@ def test_succeeds_immediately_in_happy_path(mock_tools, tmp_path): # exists, verify() should succeed, create no subprocesses, make no requests, and # return an SDK wrapper. - # On Windows, this requires `sdkmanager.bat`; on non-Windows, it requires - # `sdkmanager`. - # Create `sdkmanager` and the license file. android_sdk_root_path = tmp_path / "tools" / "android_sdk" - tools_bin = android_sdk_root_path / "cmdline-tools" / "latest" / "bin" + tools_bin = android_sdk_root_path / "cmdline-tools" / SDK_MGR_VER / "bin" tools_bin.mkdir(parents=True, mode=0o755) - if platform.system() == "Windows": - sdk_manager = tools_bin / "sdkmanager.bat" - sdk_manager.touch() - else: - sdk_manager = tools_bin / "sdkmanager" - sdk_manager.touch(mode=0o755) + (tools_bin / SDKMANAGER_FILENAME).touch(mode=0o755) # Pre-accept the license accept_license(android_sdk_root_path)() @@ -164,14 +163,9 @@ def test_user_provided_sdk(mock_tools, env_var, tmp_path, capsys): # Create `sdkmanager` and the license file. existing_android_sdk_root_path = tmp_path / "other_sdk" - tools_bin = existing_android_sdk_root_path / "cmdline-tools" / "latest" / "bin" + tools_bin = existing_android_sdk_root_path / "cmdline-tools" / SDK_MGR_VER / "bin" tools_bin.mkdir(parents=True, mode=0o755) - if platform.system() == "Windows": - sdk_manager = tools_bin / "sdkmanager.bat" - sdk_manager.touch() - else: - sdk_manager = tools_bin / "sdkmanager" - sdk_manager.touch(mode=0o755) + (tools_bin / SDKMANAGER_FILENAME).touch(mode=0o755) # Pre-accept the license accept_license(existing_android_sdk_root_path)() @@ -208,14 +202,9 @@ def test_consistent_user_provided_sdk(mock_tools, tmp_path, capsys): # Create `sdkmanager` and the license file. existing_android_sdk_root_path = tmp_path / "other_sdk" - tools_bin = existing_android_sdk_root_path / "cmdline-tools" / "latest" / "bin" + tools_bin = existing_android_sdk_root_path / "cmdline-tools" / SDK_MGR_VER / "bin" tools_bin.mkdir(parents=True, mode=0o755) - if platform.system() == "Windows": - sdk_manager = tools_bin / "sdkmanager.bat" - sdk_manager.touch() - else: - sdk_manager = tools_bin / "sdkmanager" - sdk_manager.touch(mode=0o755) + (tools_bin / SDKMANAGER_FILENAME).touch(mode=0o755) # Pre-accept the license accept_license(existing_android_sdk_root_path)() @@ -251,14 +240,9 @@ def test_inconsistent_user_provided_sdk(mock_tools, tmp_path, capsys): # Create `sdkmanager` and the license file. existing_android_sdk_root_path = tmp_path / "other_sdk" - tools_bin = existing_android_sdk_root_path / "cmdline-tools" / "latest" / "bin" + tools_bin = existing_android_sdk_root_path / "cmdline-tools" / SDK_MGR_VER / "bin" tools_bin.mkdir(parents=True, mode=0o755) - if platform.system() == "Windows": - sdk_manager = tools_bin / "sdkmanager.bat" - sdk_manager.touch() - else: - sdk_manager = tools_bin / "sdkmanager" - sdk_manager.touch(mode=0o755) + (tools_bin / SDKMANAGER_FILENAME).touch(mode=0o755) # Pre-accept the license accept_license(existing_android_sdk_root_path)() @@ -288,18 +272,12 @@ def test_inconsistent_user_provided_sdk(mock_tools, tmp_path, capsys): @pytest.mark.parametrize("env_var", ["ANDROID_HOME", "ANDROID_SDK_ROOT"]) def test_invalid_user_provided_sdk(mock_tools, env_var, tmp_path, capsys): """If the user's environment specifies an invalid Android SDK, it is ignored.""" - # Create `sdkmanager` and the license file # for the *briefcase* managed version of the SDK. android_sdk_root_path = tmp_path / "tools" / "android_sdk" - tools_bin = android_sdk_root_path / "cmdline-tools" / "latest" / "bin" + tools_bin = android_sdk_root_path / "cmdline-tools" / SDK_MGR_VER / "bin" tools_bin.mkdir(parents=True, mode=0o755) - if platform.system() == "Windows": - sdk_manager = tools_bin / "sdkmanager.bat" - sdk_manager.touch() - else: - sdk_manager = tools_bin / "sdkmanager" - sdk_manager.touch(mode=0o755) + (tools_bin / SDKMANAGER_FILENAME).touch(mode=0o755) # Pre-accept the license accept_license(android_sdk_root_path)() @@ -322,6 +300,115 @@ def test_invalid_user_provided_sdk(mock_tools, env_var, tmp_path, capsys): assert f"{env_var} does not point to an Android SDK" in capsys.readouterr().out +@pytest.mark.parametrize("env_var", ["ANDROID_HOME", "ANDROID_SDK_ROOT"]) +def test_user_provided_sdk_wrong_cmdline_tools_ver( + mock_tools, + env_var, + tmp_path, + capsys, +): + """If the user's environment specifies an Android SDK without the expected cmdline + tools and the cmdline-tools install fails, the Briefcase SDK is used.""" + # Create `sdkmanager` and the license file for the *user's* version of the SDK + user_sdk_path = tmp_path / "other_sdk" + user_tools_bin = user_sdk_path / "cmdline-tools" / "6.0" / "bin" + user_tools_bin.mkdir(parents=True, mode=0o755) + (user_tools_bin / SDKMANAGER_FILENAME).touch(mode=0o755) + + # Create `sdkmanager` and the license file + # for the *briefcase* managed version of the SDK. + android_sdk_root_path = tmp_path / "tools" / "android_sdk" + tools_bin = android_sdk_root_path / "cmdline-tools" / SDK_MGR_VER / "bin" + tools_bin.mkdir(parents=True, mode=0o755) + (tools_bin / SDKMANAGER_FILENAME).touch(mode=0o755) + + # Pre-accept the license + accept_license(android_sdk_root_path)() + + # Set the environment to specify an ANDROID_SDK_ROOT that doesn't exist + mock_tools.os.environ = {env_var: os.fsdecode(user_sdk_path)} + + # Mock `cmdline-tools/latest` directory not existing + mock_tools.subprocess.run.side_effect = OSError + + # Expect verify() to succeed + sdk = AndroidSDK.verify(mock_tools) + + # No calls to download and nothing unpacked + mock_tools.download.file.assert_not_called() + mock_tools.shutil.unpack_archive.assert_not_called() + + # Required Command-line Tools installed + mock_tools.subprocess.run.assert_called_once_with( + [ + tmp_path + / "other_sdk" + / "cmdline-tools" + / "latest" + / "bin" + / SDKMANAGER_FILENAME, + f"cmdline-tools;{SDK_MGR_VER}", + ], + check=True, + stream_output=False, + ) + + # The returned SDK has the expected root path. + assert sdk.root_path == android_sdk_root_path + + # User is informed about failed install and invalid SDK + output = capsys.readouterr().out + assert f"Failed to install cmdline-tools;{SDK_MGR_VER}" in output + assert "Incompatible Command-Line Tools Version" in output + + +@pytest.mark.parametrize("env_var", ["ANDROID_HOME", "ANDROID_SDK_ROOT"]) +def test_user_provided_sdk_with_latest_cmdline_tools( + mock_tools, + env_var, + tmp_path, + capsys, +): + """If the user's environment specifies an Android SDK without the expected cmdline + tools, the required cmdline-tools is installed in to it.""" + # Create `sdkmanager` and the license file for the *user's* version of the SDK + user_sdk_path = tmp_path / "other_sdk" + user_tools_bin = user_sdk_path / "cmdline-tools" / "latest" / "bin" + user_tools_bin.mkdir(parents=True, mode=0o755) + (user_tools_bin / SDKMANAGER_FILENAME).touch(mode=0o755) + + # Pre-accept the license + accept_license(user_sdk_path)() + + # Set the environment to specify an ANDROID_SDK_ROOT that doesn't exist + mock_tools.os.environ = {env_var: os.fsdecode(user_sdk_path)} + + # Expect verify() to succeed + sdk = AndroidSDK.verify(mock_tools) + + # No calls to download and nothing unpacked + mock_tools.download.file.assert_not_called() + mock_tools.shutil.unpack_archive.assert_not_called() + + # Required Command-line Tools installed + mock_tools.subprocess.run.assert_called_once_with( + [ + tmp_path + / "other_sdk" + / "cmdline-tools" + / "latest" + / "bin" + / SDKMANAGER_FILENAME, + f"cmdline-tools;{SDK_MGR_VER}", + ], + check=True, + stream_output=False, + ) + + # The returned SDK has the expected root path. + assert sdk.root_path == user_sdk_path + + def test_consistent_invalid_user_provided_sdk(mock_tools, tmp_path, capsys): """If the user's environment specifies an invalid Android SDK in both ANDROID_HOME and ANDROID_SDK_ROOT, they are ignored.""" @@ -329,14 +416,9 @@ def test_consistent_invalid_user_provided_sdk(mock_tools, tmp_path, capsys): # Create `sdkmanager` and the license file # for the *briefcase* managed version of the SDK. android_sdk_root_path = tmp_path / "tools" / "android_sdk" - tools_bin = android_sdk_root_path / "cmdline-tools" / "latest" / "bin" + tools_bin = android_sdk_root_path / "cmdline-tools" / SDK_MGR_VER / "bin" tools_bin.mkdir(parents=True, mode=0o755) - if platform.system() == "Windows": - sdk_manager = tools_bin / "sdkmanager.bat" - sdk_manager.touch() - else: - sdk_manager = tools_bin / "sdkmanager" - sdk_manager.touch(mode=0o755) + (tools_bin / SDKMANAGER_FILENAME).touch(mode=0o755) # Pre-accept the license accept_license(android_sdk_root_path)() @@ -369,14 +451,9 @@ def test_inconsistent_invalid_user_provided_sdk(mock_tools, tmp_path, capsys): # Create `sdkmanager` and the license file # for the *briefcase* managed version of the SDK. android_sdk_root_path = tmp_path / "tools" / "android_sdk" - tools_bin = android_sdk_root_path / "cmdline-tools" / "latest" / "bin" + tools_bin = android_sdk_root_path / "cmdline-tools" / SDK_MGR_VER / "bin" tools_bin.mkdir(parents=True, mode=0o755) - if platform.system() == "Windows": - sdk_manager = tools_bin / "sdkmanager.bat" - sdk_manager.touch() - else: - sdk_manager = tools_bin / "sdkmanager" - sdk_manager.touch(mode=0o755) + (tools_bin / SDKMANAGER_FILENAME).touch(mode=0o755) # Pre-accept the license accept_license(android_sdk_root_path)() @@ -404,7 +481,7 @@ def test_inconsistent_invalid_user_provided_sdk(mock_tools, tmp_path, capsys): assert "ANDROID_HOME does not point to an Android SDK" in output -def test_download_sdk(mock_tools, tmp_path): +def test_download_sdk(mock_tools, tmp_path, capsys): """If an SDK is not available, one will be downloaded.""" android_sdk_root_path = tmp_path / "tools" / "android_sdk" cmdline_tools_base_path = android_sdk_root_path / "cmdline-tools" @@ -425,7 +502,7 @@ def test_download_sdk(mock_tools, tmp_path): # Validate that the SDK was downloaded and unpacked url = ( "https://dl.google.com/android/repository/" - f"commandlinetools-{mock_tools._test_download_tag}-8092744_latest.zip" + f"commandlinetools-{mock_tools._test_download_tag}-{SDK_MGR_DL_VER}_latest.zip" ) mock_tools.download.file.assert_called_once_with( url=url, @@ -440,18 +517,73 @@ def test_download_sdk(mock_tools, tmp_path): # The cached file will be deleted cache_file.unlink.assert_called_once_with() - # The commandline tools path exists, in both "latest" and versioned form - assert sdk.cmdline_tools_path.exists() - assert sdk.cmdline_tools_version_path.exists() + # The commandline tools path exists + assert sdk.cmdline_tools_path.is_dir() + + if platform.system() != "Windows": + # On non-Windows, ensure the unpacked binary was made executable + assert os.access( + cmdline_tools_base_path / SDK_MGR_VER / "bin" / "sdkmanager", + os.X_OK, + ) + + # The license has been accepted + assert (android_sdk_root_path / "licenses" / "android-sdk-license").exists() + + # The returned SDK has the expected root path. + assert sdk.root_path == android_sdk_root_path + + assert "The Android SDK was not found" in capsys.readouterr().out + + +def test_upgrade_existing_sdk(mock_tools, tmp_path, capsys): + """An existing SDK is upgraded if the required version of cmdline-tools isn't + installed.""" + android_sdk_root_path = tmp_path / "tools" / "android_sdk" + cmdline_tools_base_path = android_sdk_root_path / "cmdline-tools" + + # Mock an existing cmdline-tools install + (cmdline_tools_base_path / "latest").mkdir(parents=True) + (cmdline_tools_base_path / "8092744").touch() - # The versioned form is a marker file; the tools path is a live directory + # The download will produce a cached file. + cache_file = MagicMock() + mock_tools.download.file.return_value = cache_file + + # Calling unpack will create files + mock_tools.shutil.unpack_archive.side_effect = mock_unpack + + # Set up a side effect for accepting the license + mock_tools.subprocess.run.side_effect = accept_license(android_sdk_root_path) + + # Call `verify()` + sdk = AndroidSDK.verify(mock_tools) + + # Validate that the SDK was downloaded and unpacked + url = ( + "https://dl.google.com/android/repository/" + f"commandlinetools-{mock_tools._test_download_tag}-{SDK_MGR_DL_VER}_latest.zip" + ) + mock_tools.download.file.assert_called_once_with( + url=url, + download_path=mock_tools.base_path, + role="Android SDK Command-Line Tools", + ) + + mock_tools.shutil.unpack_archive.assert_called_once_with( + cache_file, extract_dir=cmdline_tools_base_path + ) + + # The cached file will be deleted + cache_file.unlink.assert_called_once_with() + + # The commandline tools path exists assert sdk.cmdline_tools_path.is_dir() - assert sdk.cmdline_tools_version_path.is_file() if platform.system() != "Windows": # On non-Windows, ensure the unpacked binary was made executable assert os.access( - cmdline_tools_base_path / "latest" / "bin" / "sdkmanager", + cmdline_tools_base_path / SDK_MGR_VER / "bin" / "sdkmanager", os.X_OK, ) @@ -461,6 +593,12 @@ def test_download_sdk(mock_tools, tmp_path): # The returned SDK has the expected root path. assert sdk.root_path == android_sdk_root_path + # Ensure old cmdline-tools installs are cleaned up + assert not (cmdline_tools_base_path / "latest").exists() + assert not (cmdline_tools_base_path / "8092744").exists() + + assert "Upgrading Android SDK..." in capsys.readouterr().out + def test_download_sdk_legacy_install(mock_tools, tmp_path): """If the legacy SDK tools are present, they will be deleted.""" @@ -495,7 +633,7 @@ def test_download_sdk_legacy_install(mock_tools, tmp_path): # Validate that the SDK was downloaded and unpacked url = ( "https://dl.google.com/android/repository/" - f"commandlinetools-{mock_tools._test_download_tag}-8092744_latest.zip" + f"commandlinetools-{mock_tools._test_download_tag}-{SDK_MGR_DL_VER}_latest.zip" ) mock_tools.download.file.assert_called_once_with( url=url, @@ -511,17 +649,12 @@ def test_download_sdk_legacy_install(mock_tools, tmp_path): cache_file.unlink.assert_called_once_with() # The commandline tools path exists, in both "latest" and versioned form - assert sdk.cmdline_tools_path.exists() - assert sdk.cmdline_tools_version_path.exists() - - # The versioned form is a marker file; the tools path is a live directory assert sdk.cmdline_tools_path.is_dir() - assert sdk.cmdline_tools_version_path.is_file() if platform.system() != "Windows": # On non-Windows, ensure the unpacked binary was made executable assert os.access( - cmdline_tools_base_path / "latest" / "bin" / "sdkmanager", os.X_OK + cmdline_tools_base_path / SDK_MGR_VER / "bin" / "sdkmanager", os.X_OK ) # The legacy SDK tools have been removed @@ -552,9 +685,9 @@ def test_download_sdk_if_sdkmanager_not_executable(mock_tools, tmp_path): cmdline_tools_base_path = android_sdk_root_path / "cmdline-tools" # Create pre-existing non-executable `sdkmanager`. - (cmdline_tools_base_path / "latest" / "bin").mkdir(parents=True) - (cmdline_tools_base_path / "latest" / "bin" / "sdkmanager").touch(mode=0o644) - (cmdline_tools_base_path / "8092744").touch() + (cmdline_tools_base_path / SDK_MGR_VER / "bin").mkdir(parents=True) + (cmdline_tools_base_path / SDK_MGR_VER / "bin" / "sdkmanager").touch(mode=0o644) + (cmdline_tools_base_path / SDK_MGR_DL_VER).touch() # The download will produce a cached file cache_file = MagicMock() @@ -572,7 +705,7 @@ def test_download_sdk_if_sdkmanager_not_executable(mock_tools, tmp_path): # Validate that the SDK was downloaded and unpacked url = ( "https://dl.google.com/android/repository/" - f"commandlinetools-{mock_tools._test_download_tag}-8092744_latest.zip" + f"commandlinetools-{mock_tools._test_download_tag}-{SDK_MGR_DL_VER}_latest.zip" ) mock_tools.download.file.assert_called_once_with( url=url, @@ -605,7 +738,7 @@ def test_raises_networkfailure_on_connectionerror(mock_tools): # The download was attempted url = ( "https://dl.google.com/android/repository/" - f"commandlinetools-{mock_tools._test_download_tag}-8092744_latest.zip" + f"commandlinetools-{mock_tools._test_download_tag}-{SDK_MGR_DL_VER}_latest.zip" ) mock_tools.download.file.assert_called_once_with( url=url, @@ -632,7 +765,7 @@ def test_detects_bad_zipfile(mock_tools, tmp_path): # The download attempt was made. url = ( "https://dl.google.com/android/repository/" - f"commandlinetools-{mock_tools._test_download_tag}-8092744_latest.zip" + f"commandlinetools-{mock_tools._test_download_tag}-{SDK_MGR_DL_VER}_latest.zip" ) mock_tools.download.file.assert_called_once_with( url=url, diff --git a/tests/integrations/android_sdk/conftest.py b/tests/integrations/android_sdk/conftest.py index c56b4859c..d63ea0e05 100644 --- a/tests/integrations/android_sdk/conftest.py +++ b/tests/integrations/android_sdk/conftest.py @@ -9,6 +9,10 @@ from briefcase.integrations.java import JDK from briefcase.integrations.subprocess import Subprocess +# current versions of Android SDK Manager +SDK_MGR_VER = "9.0" +SDK_MGR_DL_VER = "9477386" + @pytest.fixture def mock_tools(mock_tools, tmp_path) -> ToolCache: diff --git a/tests/integrations/java/conftest.py b/tests/integrations/java/conftest.py index af3c3c49f..71ec7dfba 100644 --- a/tests/integrations/java/conftest.py +++ b/tests/integrations/java/conftest.py @@ -6,6 +6,8 @@ from briefcase.integrations.download import Download from briefcase.integrations.subprocess import Subprocess +JDK_RELEASE = "17.0.8" + @pytest.fixture def mock_tools(mock_tools, tmp_path) -> ToolCache: diff --git a/tests/integrations/java/test_JDK__upgrade.py b/tests/integrations/java/test_JDK__upgrade.py index b9c86fbcd..920b1dfbd 100644 --- a/tests/integrations/java/test_JDK__upgrade.py +++ b/tests/integrations/java/test_JDK__upgrade.py @@ -13,6 +13,8 @@ from briefcase.integrations.base import ToolCache from briefcase.integrations.java import JDK +from .conftest import JDK_RELEASE + @pytest.fixture def mock_tools(mock_tools) -> ToolCache: @@ -65,7 +67,7 @@ def rmtree(path): mock_tools.download.file.return_value = archive # Create a directory to make it look like Java was downloaded and unpacked. - (tmp_path / "tools" / "jdk-17.0.7+7").mkdir(parents=True) + (tmp_path / "tools" / f"jdk-{JDK_RELEASE}+7").mkdir(parents=True) # Create an SDK wrapper jdk = JDK(mock_tools, java_home=java_home) @@ -79,7 +81,7 @@ def rmtree(path): # A download was initiated mock_tools.download.file.assert_called_with( url="https://github.com/adoptium/temurin17-binaries/releases/download/" - "jdk-17.0.7+7/OpenJDK17U-jdk_x64_linux_hotspot_17.0.7_7.tar.gz", + f"jdk-{JDK_RELEASE}+7/OpenJDK17U-jdk_x64_linux_hotspot_{JDK_RELEASE}_7.tar.gz", download_path=tmp_path / "tools", role="Java 17 JDK", ) @@ -114,7 +116,7 @@ def rmtree(path): mock_tools.download.file.return_value = archive # Create a directory to make it look like Java was downloaded and unpacked. - (tmp_path / "tools" / "jdk-17.0.7+7").mkdir(parents=True) + (tmp_path / "tools" / f"jdk-{JDK_RELEASE}+7").mkdir(parents=True) # Create an SDK wrapper jdk = JDK(mock_tools, java_home=java_home) @@ -128,7 +130,7 @@ def rmtree(path): # A download was initiated mock_tools.download.file.assert_called_with( url="https://github.com/adoptium/temurin17-binaries/releases/download/" - "jdk-17.0.7+7/OpenJDK17U-jdk_x64_mac_hotspot_17.0.7_7.tar.gz", + f"jdk-{JDK_RELEASE}+7/OpenJDK17U-jdk_x64_mac_hotspot_{JDK_RELEASE}_7.tar.gz", download_path=tmp_path / "tools", role="Java 17 JDK", ) @@ -170,7 +172,7 @@ def rmtree(path): # A download was initiated mock_tools.download.file.assert_called_with( url="https://github.com/adoptium/temurin17-binaries/releases/download/" - "jdk-17.0.7+7/OpenJDK17U-jdk_x64_linux_hotspot_17.0.7_7.tar.gz", + f"jdk-{JDK_RELEASE}+7/OpenJDK17U-jdk_x64_linux_hotspot_{JDK_RELEASE}_7.tar.gz", download_path=tmp_path / "tools", role="Java 17 JDK", ) @@ -212,7 +214,7 @@ def rmtree(path): # A download was initiated mock_tools.download.file.assert_called_with( url="https://github.com/adoptium/temurin17-binaries/releases/download/" - "jdk-17.0.7+7/OpenJDK17U-jdk_x64_linux_hotspot_17.0.7_7.tar.gz", + f"jdk-{JDK_RELEASE}+7/OpenJDK17U-jdk_x64_linux_hotspot_{JDK_RELEASE}_7.tar.gz", download_path=tmp_path / "tools", role="Java 17 JDK", ) diff --git a/tests/integrations/java/test_JDK__verify.py b/tests/integrations/java/test_JDK__verify.py index 6c87851a0..b38175109 100644 --- a/tests/integrations/java/test_JDK__verify.py +++ b/tests/integrations/java/test_JDK__verify.py @@ -15,6 +15,8 @@ ) from briefcase.integrations.java import JDK +from .conftest import JDK_RELEASE + CALL_JAVA_HOME = mock.call(["/usr/libexec/java_home"]) @@ -58,7 +60,7 @@ def test_macos_tool_java_home(mock_tools, capsys): # Mock 2 calls to check_output. mock_tools.subprocess.check_output.side_effect = [ "/path/to/java", - "javac 17.0.7\n", + f"javac {JDK_RELEASE}\n", ] # Create a JDK wrapper by verification @@ -179,7 +181,7 @@ def test_macos_provided_overrides_tool_java_home(mock_tools, capsys): mock_tools.os.environ = {"JAVA_HOME": "/path/to/java"} # Mock return value from javac. libexec won't be invoked. - mock_tools.subprocess.check_output.return_value = "javac 17.0.7\n" + mock_tools.subprocess.check_output.return_value = f"javac {JDK_RELEASE}\n" # Create a JDK wrapper by verification JDK.verify(mock_tools) @@ -205,7 +207,7 @@ def test_valid_provided_java_home(mock_tools, capsys): mock_tools.os.environ = {"JAVA_HOME": "/path/to/java"} # Mock return value from javac. - mock_tools.subprocess.check_output.return_value = "javac 17.0.7\n" + mock_tools.subprocess.check_output.return_value = f"javac {JDK_RELEASE}\n" # Create a JDK wrapper by verification JDK.verify(mock_tools) @@ -404,7 +406,7 @@ def test_unparseable_javac_version(mock_tools, host_os, java_home, tmp_path, cap "Darwin", "x86_64", "https://github.com/adoptium/temurin17-binaries/releases/download/" - "jdk-17.0.7+7/OpenJDK17U-jdk_x64_mac_hotspot_17.0.7_7.tar.gz", + f"jdk-{JDK_RELEASE}+7/OpenJDK17U-jdk_x64_mac_hotspot_{JDK_RELEASE}_7.tar.gz", "java17/Contents/Home", False, ), @@ -412,7 +414,7 @@ def test_unparseable_javac_version(mock_tools, host_os, java_home, tmp_path, cap "Darwin", "arm64", "https://github.com/adoptium/temurin17-binaries/releases/download/" - "jdk-17.0.7+7/OpenJDK17U-jdk_aarch64_mac_hotspot_17.0.7_7.tar.gz", + f"jdk-{JDK_RELEASE}+7/OpenJDK17U-jdk_aarch64_mac_hotspot_{JDK_RELEASE}_7.tar.gz", "java17/Contents/Home", False, ), @@ -420,7 +422,7 @@ def test_unparseable_javac_version(mock_tools, host_os, java_home, tmp_path, cap "Linux", "x86_64", "https://github.com/adoptium/temurin17-binaries/releases/download/" - "jdk-17.0.7+7/OpenJDK17U-jdk_x64_linux_hotspot_17.0.7_7.tar.gz", + f"jdk-{JDK_RELEASE}+7/OpenJDK17U-jdk_x64_linux_hotspot_{JDK_RELEASE}_7.tar.gz", "java17", False, ), @@ -428,7 +430,7 @@ def test_unparseable_javac_version(mock_tools, host_os, java_home, tmp_path, cap "Linux", "aarch64", "https://github.com/adoptium/temurin17-binaries/releases/download/" - "jdk-17.0.7+7/OpenJDK17U-jdk_aarch64_linux_hotspot_17.0.7_7.tar.gz", + f"jdk-{JDK_RELEASE}+7/OpenJDK17U-jdk_aarch64_linux_hotspot_{JDK_RELEASE}_7.tar.gz", "java17", False, ), @@ -436,7 +438,7 @@ def test_unparseable_javac_version(mock_tools, host_os, java_home, tmp_path, cap "Linux", "aarch64", "https://github.com/adoptium/temurin17-binaries/releases/download/" - "jdk-17.0.7+7/OpenJDK17U-jdk_arm_linux_hotspot_17.0.7_7.tar.gz", + f"jdk-{JDK_RELEASE}+7/OpenJDK17U-jdk_arm_linux_hotspot_{JDK_RELEASE}_7.tar.gz", "java17", True, ), @@ -444,7 +446,7 @@ def test_unparseable_javac_version(mock_tools, host_os, java_home, tmp_path, cap "Linux", "armv7l", "https://github.com/adoptium/temurin17-binaries/releases/download/" - "jdk-17.0.7+7/OpenJDK17U-jdk_arm_linux_hotspot_17.0.7_7.tar.gz", + f"jdk-{JDK_RELEASE}+7/OpenJDK17U-jdk_arm_linux_hotspot_{JDK_RELEASE}_7.tar.gz", "java17", False, ), @@ -452,7 +454,7 @@ def test_unparseable_javac_version(mock_tools, host_os, java_home, tmp_path, cap "Linux", "armv8l", "https://github.com/adoptium/temurin17-binaries/releases/download/" - "jdk-17.0.7+7/OpenJDK17U-jdk_arm_linux_hotspot_17.0.7_7.tar.gz", + f"jdk-{JDK_RELEASE}+7/OpenJDK17U-jdk_arm_linux_hotspot_{JDK_RELEASE}_7.tar.gz", "java17", False, ), @@ -460,7 +462,7 @@ def test_unparseable_javac_version(mock_tools, host_os, java_home, tmp_path, cap "Windows", "AMD64", "https://github.com/adoptium/temurin17-binaries/releases/download/" - "jdk-17.0.7+7/OpenJDK17U-jdk_x64_windows_hotspot_17.0.7_7.zip", + f"jdk-{JDK_RELEASE}+7/OpenJDK17U-jdk_x64_windows_hotspot_{JDK_RELEASE}_7.zip", "java17", False, ), @@ -492,7 +494,7 @@ def test_successful_jdk_download( mock_tools.download.file.return_value = archive # Create a directory to make it look like Java was downloaded and unpacked. - (tmp_path / "tools" / "jdk-17.0.7+7").mkdir(parents=True) + (tmp_path / "tools" / f"jdk-{JDK_RELEASE}+7").mkdir(parents=True) # Invoke the verify call JDK.verify(mock_tools) @@ -547,7 +549,7 @@ def test_jdk_download_failure(mock_tools, tmp_path): # That download was attempted mock_tools.download.file.assert_called_with( url="https://github.com/adoptium/temurin17-binaries/releases/download/" - "jdk-17.0.7+7/OpenJDK17U-jdk_x64_linux_hotspot_17.0.7_7.tar.gz", + f"jdk-{JDK_RELEASE}+7/OpenJDK17U-jdk_x64_linux_hotspot_{JDK_RELEASE}_7.tar.gz", download_path=tmp_path / "tools", role="Java 17 JDK", ) @@ -575,7 +577,7 @@ def test_invalid_jdk_archive(mock_tools, tmp_path): # The download occurred mock_tools.download.file.assert_called_with( url="https://github.com/adoptium/temurin17-binaries/releases/download/" - "jdk-17.0.7+7/OpenJDK17U-jdk_x64_linux_hotspot_17.0.7_7.tar.gz", + f"jdk-{JDK_RELEASE}+7/OpenJDK17U-jdk_x64_linux_hotspot_{JDK_RELEASE}_7.tar.gz", download_path=tmp_path / "tools", role="Java 17 JDK", ) diff --git a/tests/platforms/android/gradle/test_open.py b/tests/platforms/android/gradle/test_open.py index a36bdd14e..cc36287f4 100644 --- a/tests/platforms/android/gradle/test_open.py +++ b/tests/platforms/android/gradle/test_open.py @@ -7,6 +7,7 @@ from briefcase.console import Console, Log from briefcase.exceptions import BriefcaseCommandError +from briefcase.integrations.android_sdk import AndroidSDK from briefcase.integrations.download import Download from briefcase.integrations.subprocess import Subprocess from briefcase.platforms.android.gradle import GradleOpenCommand @@ -21,7 +22,7 @@ def create_sdk_manager(tmp_path, extension=""): / "tools" / "android_sdk" / "cmdline-tools" - / "latest" + / AndroidSDK.SDK_MANAGER_VER / "bin" / f"sdkmanager{extension}", "Android SDK manager", diff --git a/tests/platforms/android/gradle/test_run.py b/tests/platforms/android/gradle/test_run.py index bc83f5df6..3549bbcfd 100644 --- a/tests/platforms/android/gradle/test_run.py +++ b/tests/platforms/android/gradle/test_run.py @@ -542,7 +542,7 @@ def test_log_file_extra(run_command, monkeypatch): run_command.tools.logger.save_log = True run_command.tools.logger.save_log_to_file(run_command) - sdk_manager = "/path/to/android_sdk/cmdline-tools/latest/bin/sdkmanager" + sdk_manager = f"/path/to/android_sdk/cmdline-tools/{AndroidSDK.SDK_MANAGER_VER}/bin/sdkmanager" if platform.system() == "Windows": sdk_manager += ".bat" run_command.tools.subprocess.check_output.assert_called_once_with(