From 215467f5008dfd4c70e063df5a4e1a5aa5dcc422 Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Wed, 27 Nov 2024 00:44:05 +0200 Subject: [PATCH 01/48] adds simple implementation of adding and removing android sdk packages --- plugins/module_utils/sdkmanager.py | 18 ++++++++++ plugins/modules/android_sdk.py | 58 ++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 plugins/module_utils/sdkmanager.py create mode 100644 plugins/modules/android_sdk.py diff --git a/plugins/module_utils/sdkmanager.py b/plugins/module_utils/sdkmanager.py new file mode 100644 index 00000000000..96b94e5eb37 --- /dev/null +++ b/plugins/module_utils/sdkmanager.py @@ -0,0 +1,18 @@ +from ansible_collections.community.general.plugins.module_utils import cmd_runner_fmt +from ansible_collections.community.general.plugins.module_utils.cmd_runner import CmdRunner + +_state_map = { + "present": "--install", + "absent": "--uninstall" +} + + +def sdkmanager_runner(module, **kwargs): + return CmdRunner( + module, + command='sdkmanager', + arg_formats=dict( + state=cmd_runner_fmt.as_map(_state_map), + name=cmd_runner_fmt.as_list() + ) + ) diff --git a/plugins/modules/android_sdk.py b/plugins/modules/android_sdk.py new file mode 100644 index 00000000000..0466116f3ff --- /dev/null +++ b/plugins/modules/android_sdk.py @@ -0,0 +1,58 @@ +import re + +from ansible_collections.community.general.plugins.module_utils.mh.module_helper import StateModuleHelper +from ansible_collections.community.general.plugins.module_utils.sdkmanager import sdkmanager_runner + + +class AndroidSdk(StateModuleHelper): + module = dict( + argument_spec=dict( + state=dict(type='str', default='present', choices=['present', 'absent', 'latest']), + package=dict(type='list', elements='str', aliases=['pkg', 'name']) + ) + ) + use_old_vardict = False + + def _get_formatted_packages(self): + arg_pkgs = self.vars.package + packages = [] + for arg_pkg in arg_pkgs: + pkg, version = package_split(arg_pkg) + fmt_pkg = format_cmdline_package(pkg, version) + packages.append(fmt_pkg) + return packages + + def state_present(self): + packages = self._get_formatted_packages() + with self.sdkmanager('state name') as ctx: + ctx.run(name=packages) + + def state_absent(self): + packages = self._get_formatted_packages() + with self.sdkmanager('state name') as ctx: + ctx.run(name=packages) + + def __init_module__(self): + self.sdkmanager = sdkmanager_runner(self.module) + + +def format_cmdline_package(package, version): + if version is None: + return package + else: + return "%s;%s" % (package, version) + + +def package_split(package): + parts = re.split(r'=', package, maxsplit=1) + if len(parts) > 1: + return parts + return parts[0], None + + +def main(): + AndroidSdk.execute() + + +if __name__ == '__main__': + main() From 25264203b137d5f95c36ea5ef6e63f39fdbff650 Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Wed, 27 Nov 2024 19:40:36 +0200 Subject: [PATCH 02/48] adds package update --- plugins/module_utils/sdkmanager.py | 5 +++-- plugins/modules/android_sdk.py | 20 +++++++++++++------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/plugins/module_utils/sdkmanager.py b/plugins/module_utils/sdkmanager.py index 96b94e5eb37..2e66a1e2de1 100644 --- a/plugins/module_utils/sdkmanager.py +++ b/plugins/module_utils/sdkmanager.py @@ -13,6 +13,7 @@ def sdkmanager_runner(module, **kwargs): command='sdkmanager', arg_formats=dict( state=cmd_runner_fmt.as_map(_state_map), - name=cmd_runner_fmt.as_list() - ) + name=cmd_runner_fmt.as_list(), + update=cmd_runner_fmt.as_bool("--update") + ), ) diff --git a/plugins/modules/android_sdk.py b/plugins/modules/android_sdk.py index 0466116f3ff..e5a33455b73 100644 --- a/plugins/modules/android_sdk.py +++ b/plugins/modules/android_sdk.py @@ -8,7 +8,8 @@ class AndroidSdk(StateModuleHelper): module = dict( argument_spec=dict( state=dict(type='str', default='present', choices=['present', 'absent', 'latest']), - package=dict(type='list', elements='str', aliases=['pkg', 'name']) + package=dict(type='list', elements='str', aliases=['pkg', 'name']), + update=dict(type='bool', default=False) ) ) use_old_vardict = False @@ -22,19 +23,24 @@ def _get_formatted_packages(self): packages.append(fmt_pkg) return packages - def state_present(self): + def __state_fallback__(self): packages = self._get_formatted_packages() with self.sdkmanager('state name') as ctx: ctx.run(name=packages) - def state_absent(self): - packages = self._get_formatted_packages() - with self.sdkmanager('state name') as ctx: - ctx.run(name=packages) + def update_packages(self): + with self.sdkmanager('update') as ctx: + ctx.run() def __init_module__(self): self.sdkmanager = sdkmanager_runner(self.module) + def __run__(self): + super().__run__() + + if self.vars.update: + self.update_packages() + def format_cmdline_package(package, version): if version is None: @@ -44,7 +50,7 @@ def format_cmdline_package(package, version): def package_split(package): - parts = re.split(r'=', package, maxsplit=1) + parts = package.split('=', maxsplit=1) if len(parts) > 1: return parts return parts[0], None From ec674cdcbb2e7c58795c61dbe43a143acbd25071 Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Thu, 28 Nov 2024 16:59:29 +0200 Subject: [PATCH 03/48] adds simple installed packages parsing --- plugins/module_utils/sdkmanager.py | 4 +++- plugins/modules/android_sdk.py | 31 ++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/plugins/module_utils/sdkmanager.py b/plugins/module_utils/sdkmanager.py index 2e66a1e2de1..04d35f044e0 100644 --- a/plugins/module_utils/sdkmanager.py +++ b/plugins/module_utils/sdkmanager.py @@ -14,6 +14,8 @@ def sdkmanager_runner(module, **kwargs): arg_formats=dict( state=cmd_runner_fmt.as_map(_state_map), name=cmd_runner_fmt.as_list(), - update=cmd_runner_fmt.as_bool("--update") + update=cmd_runner_fmt.as_fixed("--update"), + installed=cmd_runner_fmt.as_fixed("--list_installed") ), + **kwargs ) diff --git a/plugins/modules/android_sdk.py b/plugins/modules/android_sdk.py index e5a33455b73..5456695f2ec 100644 --- a/plugins/modules/android_sdk.py +++ b/plugins/modules/android_sdk.py @@ -5,6 +5,8 @@ class AndroidSdk(StateModuleHelper): + _RE_INSTALLED_PACKAGES_HEADER = re.compile(r'^\s+Path\s+|\s+Version\s+|\s+Description\s+|\s+Location\s+$') + _RE_INSTALLED_PACKAGES = re.compile(r'^\s+(\S+)\s+\|\s+(\S+)\s+\|\s(.+)\s\|\s+(\S+)$') module = dict( argument_spec=dict( state=dict(type='str', default='present', choices=['present', 'absent', 'latest']), @@ -13,6 +15,7 @@ class AndroidSdk(StateModuleHelper): ) ) use_old_vardict = False + output_params = ('installed') def _get_formatted_packages(self): arg_pkgs = self.vars.package @@ -23,8 +26,36 @@ def _get_formatted_packages(self): packages.append(fmt_pkg) return packages + def _get_installed_packages(self): + with self.sdkmanager('installed') as ctx: + rc, stdout, stderr = ctx.run() + + packages = [] + data = stdout.split('\n') + + lines_count = len(data) + + i = 0 + + while i < lines_count: + line = data[i] + if self._RE_INSTALLED_PACKAGES_HEADER.match(line): + i += 1 + else: + p = self._RE_INSTALLED_PACKAGES.search(line) + if p: + name = p.group(1) + version = p.group(2) + description = p.group(3) + packages.append((name, version, description)) + i += 1 + return packages + def __state_fallback__(self): packages = self._get_formatted_packages() + + installed = self._get_installed_packages() + self.vars.installed = installed with self.sdkmanager('state name') as ctx: ctx.run(name=packages) From ca3d11aade0b44cc94d830c3e9374e4c17ddef05 Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Tue, 3 Dec 2024 14:16:04 +0200 Subject: [PATCH 04/48] moves parsing logic to a separate class --- plugins/module_utils/sdkmanager.py | 62 +++++++++++++++++++++++++ plugins/modules/android_sdk.py | 72 ++++++++++-------------------- 2 files changed, 85 insertions(+), 49 deletions(-) diff --git a/plugins/module_utils/sdkmanager.py b/plugins/module_utils/sdkmanager.py index 04d35f044e0..a3dc081878f 100644 --- a/plugins/module_utils/sdkmanager.py +++ b/plugins/module_utils/sdkmanager.py @@ -1,3 +1,5 @@ +import re + from ansible_collections.community.general.plugins.module_utils import cmd_runner_fmt from ansible_collections.community.general.plugins.module_utils.cmd_runner import CmdRunner @@ -19,3 +21,63 @@ def sdkmanager_runner(module, **kwargs): ), **kwargs ) + + +class Package: + def __init__(self, name, version, description=''): + self.name = name + self.version = version + self.description = description + + def __str__(self): + return "%s;%s (%s)" % (self.name, self.version, self.description) + + def __eq__(self, other): + if not isinstance(other, Package): + return False + + return self.name == other.name and self.version == other.version and self.description == other.description + + def get_formatted(self): + if self.version is None: + return self.name + else: + return "%s;%s" % (self.name, self.version) + + +class AndroidSdkManager(object): + _RE_INSTALLED_PACKAGES_HEADER = re.compile(r'^\s+Path\s+|\s+Version\s+|\s+Description\s+|\s+Location\s+$') + _RE_INSTALLED_PACKAGES = ( + re.compile(r'^\s+(?P\S+)\s+\|\s+(?P\S+)\s+\|\s(?P.+)\s\|\s+(\S+)$') + ) + + def __init__(self, runner): + self.runner = runner + + def get_installed_packages(self): + with self.runner('installed') as ctx: + rc, stdout, stderr = ctx.run() + + packages = [] + data = stdout.split('\n') + + lines_count = len(data) + + i = 0 + + while i < lines_count: + line = data[i] + if self._RE_INSTALLED_PACKAGES_HEADER.match(line): + i += 1 + else: + p = self._RE_INSTALLED_PACKAGES.search(line) + if p: + package = Package(p.group('name'), p.group('version'), p.group('description')) + packages.append(package) + i += 1 + return packages + + def install_packages(self, packages): + install_command_arg = ''.join(x.get_formatted() for x in packages) + with self.runner('state name') as ctx: + ctx.run(name=install_command_arg) diff --git a/plugins/modules/android_sdk.py b/plugins/modules/android_sdk.py index 5456695f2ec..6c7c0f3b114 100644 --- a/plugins/modules/android_sdk.py +++ b/plugins/modules/android_sdk.py @@ -1,12 +1,9 @@ -import re - from ansible_collections.community.general.plugins.module_utils.mh.module_helper import StateModuleHelper -from ansible_collections.community.general.plugins.module_utils.sdkmanager import sdkmanager_runner +from ansible_collections.community.general.plugins.module_utils.sdkmanager import sdkmanager_runner, Package, \ + AndroidSdkManager class AndroidSdk(StateModuleHelper): - _RE_INSTALLED_PACKAGES_HEADER = re.compile(r'^\s+Path\s+|\s+Version\s+|\s+Description\s+|\s+Location\s+$') - _RE_INSTALLED_PACKAGES = re.compile(r'^\s+(\S+)\s+\|\s+(\S+)\s+\|\s(.+)\s\|\s+(\S+)$') module = dict( argument_spec=dict( state=dict(type='str', default='present', choices=['present', 'absent', 'latest']), @@ -17,54 +14,38 @@ class AndroidSdk(StateModuleHelper): use_old_vardict = False output_params = ('installed') - def _get_formatted_packages(self): + def __init_module__(self): + self.sdkmanager = AndroidSdkManager(sdkmanager_runner(self.module)) + + def _parse_packages(self): arg_pkgs = self.vars.package packages = [] for arg_pkg in arg_pkgs: pkg, version = package_split(arg_pkg) - fmt_pkg = format_cmdline_package(pkg, version) - packages.append(fmt_pkg) + package = Package(pkg, version) + packages.append(package) return packages - def _get_installed_packages(self): - with self.sdkmanager('installed') as ctx: - rc, stdout, stderr = ctx.run() - - packages = [] - data = stdout.split('\n') - - lines_count = len(data) - - i = 0 - - while i < lines_count: - line = data[i] - if self._RE_INSTALLED_PACKAGES_HEADER.match(line): - i += 1 - else: - p = self._RE_INSTALLED_PACKAGES.search(line) - if p: - name = p.group(1) - version = p.group(2) - description = p.group(3) - packages.append((name, version, description)) - i += 1 - return packages - def __state_fallback__(self): - packages = self._get_formatted_packages() + packages = self._parse_packages() + installed = self.sdkmanager.get_installed_packages() + pending_installation = [] + for package in packages: + for existing in installed: + if existing.name == package.name: + if existing.version == package.version: + pass#do nothing, package exists + # else: + # package exists, but needs to be updated/downgraded + else: + pending_installation.append(package) - installed = self._get_installed_packages() self.vars.installed = installed - with self.sdkmanager('state name') as ctx: - ctx.run(name=packages) def update_packages(self): - with self.sdkmanager('update') as ctx: - ctx.run() - - def __init_module__(self): - self.sdkmanager = sdkmanager_runner(self.module) + pass + # with self.sdkmanager('update') as ctx: + # ctx.run() def __run__(self): super().__run__() @@ -73,13 +54,6 @@ def __run__(self): self.update_packages() -def format_cmdline_package(package, version): - if version is None: - return package - else: - return "%s;%s" % (package, version) - - def package_split(package): parts = package.split('=', maxsplit=1) if len(parts) > 1: From 7349127609e757f2d1360cd08c90a687c1cca705 Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Tue, 3 Dec 2024 19:36:22 +0200 Subject: [PATCH 05/48] adds absent state for sdkmanager packages and setup for tests --- plugins/module_utils/sdkmanager.py | 14 +++-- plugins/modules/android_sdk.py | 33 +++++++----- tests/integration/targets/android_sdk/aliases | 7 +++ .../targets/android_sdk/tasks/tasks/setup.yml | 52 +++++++++++++++++++ .../targets/android_sdk/vars/main.yml | 7 +++ 5 files changed, 98 insertions(+), 15 deletions(-) create mode 100644 tests/integration/targets/android_sdk/aliases create mode 100644 tests/integration/targets/android_sdk/tasks/tasks/setup.yml create mode 100644 tests/integration/targets/android_sdk/vars/main.yml diff --git a/plugins/module_utils/sdkmanager.py b/plugins/module_utils/sdkmanager.py index a3dc081878f..4cc7492437f 100644 --- a/plugins/module_utils/sdkmanager.py +++ b/plugins/module_utils/sdkmanager.py @@ -36,7 +36,7 @@ def __eq__(self, other): if not isinstance(other, Package): return False - return self.name == other.name and self.version == other.version and self.description == other.description + return self.name == other.name and self.version == other.version def get_formatted(self): if self.version is None: @@ -78,6 +78,14 @@ def get_installed_packages(self): return packages def install_packages(self, packages): - install_command_arg = ''.join(x.get_formatted() for x in packages) + self.apply_packages_changes(packages, 'present') + + def uninstall_packages(self, packages): + self.apply_packages_changes(packages, 'absent') + + def apply_packages_changes(self, packages, state): + if len(packages) == 0: + return + command_arg = ''.join(x.get_formatted() for x in packages) with self.runner('state name') as ctx: - ctx.run(name=install_command_arg) + ctx.run(name=command_arg, state=state) diff --git a/plugins/modules/android_sdk.py b/plugins/modules/android_sdk.py index 6c7c0f3b114..08e75f6a887 100644 --- a/plugins/modules/android_sdk.py +++ b/plugins/modules/android_sdk.py @@ -14,6 +14,13 @@ class AndroidSdk(StateModuleHelper): use_old_vardict = False output_params = ('installed') + @staticmethod + def package_split(package): + parts = package.split('=', maxsplit=1) + if len(parts) > 1: + return parts + return parts[0], None + def __init_module__(self): self.sdkmanager = AndroidSdkManager(sdkmanager_runner(self.module)) @@ -21,12 +28,12 @@ def _parse_packages(self): arg_pkgs = self.vars.package packages = [] for arg_pkg in arg_pkgs: - pkg, version = package_split(arg_pkg) + pkg, version = AndroidSdk.package_split(arg_pkg) package = Package(pkg, version) packages.append(package) return packages - def __state_fallback__(self): + def state_present(self): packages = self._parse_packages() installed = self.sdkmanager.get_installed_packages() pending_installation = [] @@ -34,13 +41,22 @@ def __state_fallback__(self): for existing in installed: if existing.name == package.name: if existing.version == package.version: - pass#do nothing, package exists + pass # do nothing, package exists # else: - # package exists, but needs to be updated/downgraded + # package exists, but needs to be updated/downgraded else: pending_installation.append(package) + self.sdkmanager.install_packages(pending_installation) - self.vars.installed = installed + def state_absent(self): + packages = self._parse_packages() + installed = self.sdkmanager.get_installed_packages() + to_be_deleted = [] + for package in packages: + for existing in installed: + if existing == package: + to_be_deleted.append(package) + self.sdkmanager.uninstall_packages(to_be_deleted) def update_packages(self): pass @@ -54,13 +70,6 @@ def __run__(self): self.update_packages() -def package_split(package): - parts = package.split('=', maxsplit=1) - if len(parts) > 1: - return parts - return parts[0], None - - def main(): AndroidSdk.execute() diff --git a/tests/integration/targets/android_sdk/aliases b/tests/integration/targets/android_sdk/aliases new file mode 100644 index 00000000000..bb79889366a --- /dev/null +++ b/tests/integration/targets/android_sdk/aliases @@ -0,0 +1,7 @@ +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +azp/posix/3 +destructive +needs/root \ No newline at end of file diff --git a/tests/integration/targets/android_sdk/tasks/tasks/setup.yml b/tests/integration/targets/android_sdk/tasks/tasks/setup.yml new file mode 100644 index 00000000000..87bbe59c62c --- /dev/null +++ b/tests/integration/targets/android_sdk/tasks/tasks/setup.yml @@ -0,0 +1,52 @@ +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + + +- name: Install dependencies + become: true + package: + name: + - java-21-openjdk + state: present + +- name: Create Android SDK directory + become: true + file: + path: "{{ android_sdk_location }}" + state: directory + owner: vagrant + group: vagrant + +- name: Check that sdkmanager is installed + stat: + path: "{{ android_sdk_location }}/cmdline-tools/latest/bin/sdkmanager" + register: sdkmanager_installed + + +- name: Install Android command line tools + when: not sdkmanager_installed.stat.exists + block: + - name: Create Android SDK dir structure + file: + path: "{{ item.path }}" + state: "{{ item.state }}" + with_items: + - { path: "{{ android_cmdline_temp_dir }}", state: "directory" } + - { path: "{{ android_sdk_location }}/cmdline-tools/latest", state: "directory" } + + - name: Download Android command line tools + unarchive: + src: "{{ commandline_tools_link }}" + dest: "{{ android_cmdline_temp_dir }}" + remote_src: yes + creates: "{{ android_cmdline_temp_dir }}/cmdline-tools" + when: not sdkmanager_installed.stat.exists + + + - name: Fix directory structure + copy: + src: "{{ android_cmdline_temp_dir }}/cmdline-tools/" + dest: "{{ android_sdk_location }}/cmdline-tools/latest" + remote_src: yes + diff --git a/tests/integration/targets/android_sdk/vars/main.yml b/tests/integration/targets/android_sdk/vars/main.yml new file mode 100644 index 00000000000..dc836838580 --- /dev/null +++ b/tests/integration/targets/android_sdk/vars/main.yml @@ -0,0 +1,7 @@ +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +android_cmdline_temp_dir: "/tmp/cmdlinetools" +android_sdk_location: /usr/local/android/sdk +commandline_tools_link: https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip From 01c36742dd6c12893478d072090a122358405f2d Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Tue, 3 Dec 2024 22:21:30 +0200 Subject: [PATCH 06/48] adds output for installing and removing packages --- plugins/module_utils/sdkmanager.py | 27 ++++++++++++++++++---- plugins/modules/android_sdk.py | 36 +++++++++++++----------------- 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/plugins/module_utils/sdkmanager.py b/plugins/module_utils/sdkmanager.py index 4cc7492437f..50bf22e234a 100644 --- a/plugins/module_utils/sdkmanager.py +++ b/plugins/module_utils/sdkmanager.py @@ -32,6 +32,14 @@ def __init__(self, name, version, description=''): def __str__(self): return "%s;%s (%s)" % (self.name, self.version, self.description) + def __hash__(self): + return hash((self.name, self.version)) + + def __ne__(self, other): + if not isinstance(other, Package): + return True + return self.name != other.name or self.version != other + def __eq__(self, other): if not isinstance(other, Package): return False @@ -51,6 +59,13 @@ class AndroidSdkManager(object): re.compile(r'^\s+(?P\S+)\s+\|\s+(?P\S+)\s+\|\s(?P.+)\s\|\s+(\S+)$') ) + @staticmethod + def package_split(package): + parts = package.split(';', maxsplit=1) + if len(parts) > 1: + return parts + return parts[0], None + def __init__(self, runner): self.runner = runner @@ -58,7 +73,7 @@ def get_installed_packages(self): with self.runner('installed') as ctx: rc, stdout, stderr = ctx.run() - packages = [] + packages = set() data = stdout.split('\n') lines_count = len(data) @@ -72,8 +87,9 @@ def get_installed_packages(self): else: p = self._RE_INSTALLED_PACKAGES.search(line) if p: - package = Package(p.group('name'), p.group('version'), p.group('description')) - packages.append(package) + name = AndroidSdkManager.package_split(p.group('name'))[0] + package = Package(name, p.group('version'), p.group('description')) + packages.add(package) i += 1 return packages @@ -86,6 +102,9 @@ def uninstall_packages(self, packages): def apply_packages_changes(self, packages, state): if len(packages) == 0: return - command_arg = ''.join(x.get_formatted() for x in packages) + command_arg = [x.get_formatted() for x in packages] + # ValueError: ['build-tools;34.0.0', 'build-tools;35.0.0'] + # raise ValueError(command_arg) with self.runner('state name') as ctx: ctx.run(name=command_arg, state=state) + diff --git a/plugins/modules/android_sdk.py b/plugins/modules/android_sdk.py index 08e75f6a887..9c136e929f4 100644 --- a/plugins/modules/android_sdk.py +++ b/plugins/modules/android_sdk.py @@ -12,10 +12,10 @@ class AndroidSdk(StateModuleHelper): ) ) use_old_vardict = False - output_params = ('installed') + output_params = ('installed', 'removed') @staticmethod - def package_split(package): + def arg_package_split(package): parts = package.split('=', maxsplit=1) if len(parts) > 1: return parts @@ -26,43 +26,39 @@ def __init_module__(self): def _parse_packages(self): arg_pkgs = self.vars.package - packages = [] + packages = set() for arg_pkg in arg_pkgs: - pkg, version = AndroidSdk.package_split(arg_pkg) + pkg, version = AndroidSdk.arg_package_split(arg_pkg) package = Package(pkg, version) - packages.append(package) + packages.add(package) + + if len(packages) < len(arg_pkgs): + self.do_raise("Packages may not repeat") return packages def state_present(self): packages = self._parse_packages() installed = self.sdkmanager.get_installed_packages() - pending_installation = [] - for package in packages: - for existing in installed: - if existing.name == package.name: - if existing.version == package.version: - pass # do nothing, package exists - # else: - # package exists, but needs to be updated/downgraded - else: - pending_installation.append(package) + pending_installation = packages.difference(installed) self.sdkmanager.install_packages(pending_installation) + self.vars.installed = AndroidSdk._packages_to_str(pending_installation) def state_absent(self): packages = self._parse_packages() installed = self.sdkmanager.get_installed_packages() - to_be_deleted = [] - for package in packages: - for existing in installed: - if existing == package: - to_be_deleted.append(package) + to_be_deleted = packages.intersection(installed) self.sdkmanager.uninstall_packages(to_be_deleted) + self.vars.removed = AndroidSdk._packages_to_str(to_be_deleted) def update_packages(self): pass # with self.sdkmanager('update') as ctx: # ctx.run() + @staticmethod + def _packages_to_str(packages): + return [{'name': x.name, 'version': x.version} for x in packages] + def __run__(self): super().__run__() From d7f6451535c16463ac6251dd5c41cd3908679d5c Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Wed, 4 Dec 2024 15:17:54 +0200 Subject: [PATCH 07/48] removes version from Package object since it is not possible to specify version for a package while using sdkmanager --- plugins/module_utils/sdkmanager.py | 31 ++++++------------------------ plugins/modules/android_sdk.py | 12 ++---------- 2 files changed, 8 insertions(+), 35 deletions(-) diff --git a/plugins/module_utils/sdkmanager.py b/plugins/module_utils/sdkmanager.py index 50bf22e234a..16131881ce2 100644 --- a/plugins/module_utils/sdkmanager.py +++ b/plugins/module_utils/sdkmanager.py @@ -24,33 +24,22 @@ def sdkmanager_runner(module, **kwargs): class Package: - def __init__(self, name, version, description=''): + def __init__(self, name): self.name = name - self.version = version - self.description = description - - def __str__(self): - return "%s;%s (%s)" % (self.name, self.version, self.description) def __hash__(self): - return hash((self.name, self.version)) + return hash(self.name) def __ne__(self, other): if not isinstance(other, Package): return True - return self.name != other.name or self.version != other + return self.name != other.name def __eq__(self, other): if not isinstance(other, Package): return False - return self.name == other.name and self.version == other.version - - def get_formatted(self): - if self.version is None: - return self.name - else: - return "%s;%s" % (self.name, self.version) + return self.name == other.name class AndroidSdkManager(object): @@ -59,13 +48,6 @@ class AndroidSdkManager(object): re.compile(r'^\s+(?P\S+)\s+\|\s+(?P\S+)\s+\|\s(?P.+)\s\|\s+(\S+)$') ) - @staticmethod - def package_split(package): - parts = package.split(';', maxsplit=1) - if len(parts) > 1: - return parts - return parts[0], None - def __init__(self, runner): self.runner = runner @@ -87,8 +69,7 @@ def get_installed_packages(self): else: p = self._RE_INSTALLED_PACKAGES.search(line) if p: - name = AndroidSdkManager.package_split(p.group('name'))[0] - package = Package(name, p.group('version'), p.group('description')) + package = Package(p.group('name')) packages.add(package) i += 1 return packages @@ -102,7 +83,7 @@ def uninstall_packages(self, packages): def apply_packages_changes(self, packages, state): if len(packages) == 0: return - command_arg = [x.get_formatted() for x in packages] + command_arg = [x.name for x in packages] # ValueError: ['build-tools;34.0.0', 'build-tools;35.0.0'] # raise ValueError(command_arg) with self.runner('state name') as ctx: diff --git a/plugins/modules/android_sdk.py b/plugins/modules/android_sdk.py index 9c136e929f4..ddf6e537c91 100644 --- a/plugins/modules/android_sdk.py +++ b/plugins/modules/android_sdk.py @@ -14,13 +14,6 @@ class AndroidSdk(StateModuleHelper): use_old_vardict = False output_params = ('installed', 'removed') - @staticmethod - def arg_package_split(package): - parts = package.split('=', maxsplit=1) - if len(parts) > 1: - return parts - return parts[0], None - def __init_module__(self): self.sdkmanager = AndroidSdkManager(sdkmanager_runner(self.module)) @@ -28,8 +21,7 @@ def _parse_packages(self): arg_pkgs = self.vars.package packages = set() for arg_pkg in arg_pkgs: - pkg, version = AndroidSdk.arg_package_split(arg_pkg) - package = Package(pkg, version) + package = Package(arg_pkg) packages.add(package) if len(packages) < len(arg_pkgs): @@ -57,7 +49,7 @@ def update_packages(self): @staticmethod def _packages_to_str(packages): - return [{'name': x.name, 'version': x.version} for x in packages] + return [x.name for x in packages] def __run__(self): super().__run__() From 0434249881f4fa2c423206deb196ffa64e53b767 Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Wed, 4 Dec 2024 19:17:18 +0200 Subject: [PATCH 08/48] adds 'latest' state --- plugins/module_utils/sdkmanager.py | 44 ++++++++++++++++++++++++++++-- plugins/modules/android_sdk.py | 31 +++++++++++++++++---- 2 files changed, 67 insertions(+), 8 deletions(-) diff --git a/plugins/module_utils/sdkmanager.py b/plugins/module_utils/sdkmanager.py index 16131881ce2..293504fabbd 100644 --- a/plugins/module_utils/sdkmanager.py +++ b/plugins/module_utils/sdkmanager.py @@ -17,7 +17,9 @@ def sdkmanager_runner(module, **kwargs): state=cmd_runner_fmt.as_map(_state_map), name=cmd_runner_fmt.as_list(), update=cmd_runner_fmt.as_fixed("--update"), - installed=cmd_runner_fmt.as_fixed("--list_installed") + installed=cmd_runner_fmt.as_fixed("--list_installed"), + list=cmd_runner_fmt.as_fixed('--list'), + newer=cmd_runner_fmt.as_fixed("--newer") ), **kwargs ) @@ -47,6 +49,12 @@ class AndroidSdkManager(object): _RE_INSTALLED_PACKAGES = ( re.compile(r'^\s+(?P\S+)\s+\|\s+(?P\S+)\s+\|\s(?P.+)\s\|\s+(\S+)$') ) + _RE_UPDATABLE_PACKAGES_HEADER = re.compile(r'^Available Updates:$') + + # Example: ' platform-tools | 27.0.0 | 35.0.2' + _RE_UPDATABLE_PACKAGE = ( + re.compile(r'^\s+(?P\S+)\s+\|\s+(?P\S+)\s+\|\s+(?P\S+)\s*$') + ) def __init__(self, runner): self.runner = runner @@ -74,6 +82,39 @@ def get_installed_packages(self): i += 1 return packages + def get_updatable_packages(self): + with self.runner('list newer') as ctx: + rc, stdout, stderr = ctx.run() + data = stdout.split('\n') + + updatable_section_found = False + i = 0 + lines_count = len(data) + packages = set() + + dbg = [] + while i < lines_count: + if not updatable_section_found: + updatable_section_found = self._RE_UPDATABLE_PACKAGES_HEADER.match(data[i]) + if updatable_section_found: + # Table has the following structure. Once it is found, 2 lines need to be skipped + # + # Available Updates: <--- we are here + # ID | Installed | Available + # ------- | ------- | ------- + # platform-tools | 27.0.0 | 35.0.2 <--- skip to here + i += 3 # skip table header + else: + i += 1 # just iterate next until we find the section's header + continue + else: + p = self._RE_UPDATABLE_PACKAGE.match(data[i]) + dbg.append((data[i], p is not None)) + if p: + packages.add(Package(p.group('name'))) + i += 1 + return packages + def install_packages(self, packages): self.apply_packages_changes(packages, 'present') @@ -88,4 +129,3 @@ def apply_packages_changes(self, packages, state): # raise ValueError(command_arg) with self.runner('state name') as ctx: ctx.run(name=command_arg, state=state) - diff --git a/plugins/modules/android_sdk.py b/plugins/modules/android_sdk.py index ddf6e537c91..f99dd0b6d7b 100644 --- a/plugins/modules/android_sdk.py +++ b/plugins/modules/android_sdk.py @@ -9,13 +9,18 @@ class AndroidSdk(StateModuleHelper): state=dict(type='str', default='present', choices=['present', 'absent', 'latest']), package=dict(type='list', elements='str', aliases=['pkg', 'name']), update=dict(type='bool', default=False) - ) + ), + supports_check_mode=True ) use_old_vardict = False output_params = ('installed', 'removed') + change_params = ('installed', 'removed') + diff_params = ('installed', 'removed') def __init_module__(self): self.sdkmanager = AndroidSdkManager(sdkmanager_runner(self.module)) + self.vars.set('installed', [], change=True) + self.vars.set('removed', [], change=True) def _parse_packages(self): arg_pkgs = self.vars.package @@ -32,15 +37,29 @@ def state_present(self): packages = self._parse_packages() installed = self.sdkmanager.get_installed_packages() pending_installation = packages.difference(installed) - self.sdkmanager.install_packages(pending_installation) - self.vars.installed = AndroidSdk._packages_to_str(pending_installation) + + self.vars.installed = AndroidSdk._map_packages_to_names(pending_installation) + if not self.check_mode: + self.sdkmanager.install_packages(pending_installation) def state_absent(self): packages = self._parse_packages() installed = self.sdkmanager.get_installed_packages() to_be_deleted = packages.intersection(installed) - self.sdkmanager.uninstall_packages(to_be_deleted) - self.vars.removed = AndroidSdk._packages_to_str(to_be_deleted) + self.vars.removed = AndroidSdk._map_packages_to_names(to_be_deleted) + if not self.check_mode: + self.sdkmanager.uninstall_packages(to_be_deleted) + + def state_latest(self): + packages = self._parse_packages() + installed = self.sdkmanager.get_installed_packages() + updatable = self.sdkmanager.get_updatable_packages() + not_installed = packages.difference(installed) + to_be_installed = not_installed.intersection(updatable) + self.vars.installed = AndroidSdk._map_packages_to_names(to_be_installed) + + if not self.check_mode: + self.sdkmanager.install_packages(packages) def update_packages(self): pass @@ -48,7 +67,7 @@ def update_packages(self): # ctx.run() @staticmethod - def _packages_to_str(packages): + def _map_packages_to_names(packages): return [x.name for x in packages] def __run__(self): From 3edfac2d37b710661cb281a30a5cf2562c411ed3 Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Fri, 6 Dec 2024 00:30:16 +0200 Subject: [PATCH 09/48] adds tests --- plugins/module_utils/sdkmanager.py | 47 ++++++------- plugins/modules/android_sdk.py | 12 +++- .../targets/android_sdk/meta/main.yml | 7 ++ .../targets/android_sdk/tasks/main.yml | 69 +++++++++++++++++++ .../android_sdk/tasks/{tasks => }/setup.yml | 13 ++-- .../targets/android_sdk/vars/main.yml | 2 +- 6 files changed, 117 insertions(+), 33 deletions(-) create mode 100644 tests/integration/targets/android_sdk/meta/main.yml create mode 100644 tests/integration/targets/android_sdk/tasks/main.yml rename tests/integration/targets/android_sdk/tasks/{tasks => }/setup.yml (91%) diff --git a/plugins/module_utils/sdkmanager.py b/plugins/module_utils/sdkmanager.py index 293504fabbd..8cbb31a0feb 100644 --- a/plugins/module_utils/sdkmanager.py +++ b/plugins/module_utils/sdkmanager.py @@ -45,17 +45,17 @@ def __eq__(self, other): class AndroidSdkManager(object): - _RE_INSTALLED_PACKAGES_HEADER = re.compile(r'^\s+Path\s+|\s+Version\s+|\s+Description\s+|\s+Location\s+$') - _RE_INSTALLED_PACKAGES = ( - re.compile(r'^\s+(?P\S+)\s+\|\s+(?P\S+)\s+\|\s(?P.+)\s\|\s+(\S+)$') - ) + _RE_INSTALLED_PACKAGES_HEADER = re.compile(r'^Installed packages:$') _RE_UPDATABLE_PACKAGES_HEADER = re.compile(r'^Available Updates:$') - # Example: ' platform-tools | 27.0.0 | 35.0.2' - _RE_UPDATABLE_PACKAGE = ( - re.compile(r'^\s+(?P\S+)\s+\|\s+(?P\S+)\s+\|\s+(?P\S+)\s*$') + # Example: ' platform-tools | 27.0.0 | Android SDK Platform-Tools 27 | platform-tools ' + _RE_INSTALLED_PACKAGE = ( + re.compile(r'^\s+(?P\S+)\s+\|\s+(?P\S+)\s+\|\s(?P.+)\s\|\s+(\S+)\s*$') ) + # Example: ' platform-tools | 27.0.0 | 35.0.2' + _RE_UPDATABLE_PACKAGE = re.compile(r'^\s+(?P\S+)\s+\|\s+(?P\S+)\s+\|\s+(?P\S+)\s*$') + def __init__(self, runner): self.runner = runner @@ -63,24 +63,29 @@ def get_installed_packages(self): with self.runner('installed') as ctx: rc, stdout, stderr = ctx.run() - packages = set() data = stdout.split('\n') - lines_count = len(data) - + installed_section_found = False i = 0 + lines_count = len(data) + packages = set() while i < lines_count: - line = data[i] - if self._RE_INSTALLED_PACKAGES_HEADER.match(line): - i += 1 + + if not installed_section_found: + installed_section_found = self._RE_INSTALLED_PACKAGES_HEADER.match(data[i]) + if installed_section_found: + i += 3 + else: + i += 1 + continue else: - p = self._RE_INSTALLED_PACKAGES.search(line) + p = self._RE_INSTALLED_PACKAGE.search(data[i]) if p: package = Package(p.group('name')) packages.add(package) i += 1 - return packages + return packages def get_updatable_packages(self): with self.runner('list newer') as ctx: @@ -92,7 +97,6 @@ def get_updatable_packages(self): lines_count = len(data) packages = set() - dbg = [] while i < lines_count: if not updatable_section_found: updatable_section_found = self._RE_UPDATABLE_PACKAGES_HEADER.match(data[i]) @@ -109,23 +113,20 @@ def get_updatable_packages(self): continue else: p = self._RE_UPDATABLE_PACKAGE.match(data[i]) - dbg.append((data[i], p is not None)) if p: packages.add(Package(p.group('name'))) i += 1 return packages def install_packages(self, packages): - self.apply_packages_changes(packages, 'present') + return self.apply_packages_changes(packages, 'present') def uninstall_packages(self, packages): - self.apply_packages_changes(packages, 'absent') + return self.apply_packages_changes(packages, 'absent') def apply_packages_changes(self, packages, state): if len(packages) == 0: - return + return 0, '', '' command_arg = [x.name for x in packages] - # ValueError: ['build-tools;34.0.0', 'build-tools;35.0.0'] - # raise ValueError(command_arg) with self.runner('state name') as ctx: - ctx.run(name=command_arg, state=state) + return ctx.run(name=command_arg, state=state) diff --git a/plugins/modules/android_sdk.py b/plugins/modules/android_sdk.py index f99dd0b6d7b..c002aae87d0 100644 --- a/plugins/modules/android_sdk.py +++ b/plugins/modules/android_sdk.py @@ -40,7 +40,9 @@ def state_present(self): self.vars.installed = AndroidSdk._map_packages_to_names(pending_installation) if not self.check_mode: - self.sdkmanager.install_packages(pending_installation) + rc, stdout, stderr = self.sdkmanager.install_packages(packages) + if rc != 0: + self.do_raise("Could not install packages: %s" % stderr) def state_absent(self): packages = self._parse_packages() @@ -48,7 +50,9 @@ def state_absent(self): to_be_deleted = packages.intersection(installed) self.vars.removed = AndroidSdk._map_packages_to_names(to_be_deleted) if not self.check_mode: - self.sdkmanager.uninstall_packages(to_be_deleted) + rc, stdout, stderr = self.sdkmanager.uninstall_packages(packages) + if rc != 0: + self.do_raise("Could not uninstall packages: %s" % stderr) def state_latest(self): packages = self._parse_packages() @@ -59,7 +63,9 @@ def state_latest(self): self.vars.installed = AndroidSdk._map_packages_to_names(to_be_installed) if not self.check_mode: - self.sdkmanager.install_packages(packages) + rc, stdout, stderr = self.sdkmanager.install_packages(packages) + if rc != 0: + self.do_raise("Could not install packages: %s" % stderr) def update_packages(self): pass diff --git a/tests/integration/targets/android_sdk/meta/main.yml b/tests/integration/targets/android_sdk/meta/main.yml new file mode 100644 index 00000000000..2fcd152f95c --- /dev/null +++ b/tests/integration/targets/android_sdk/meta/main.yml @@ -0,0 +1,7 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +dependencies: + - setup_pkg_mgr diff --git a/tests/integration/targets/android_sdk/tasks/main.yml b/tests/integration/targets/android_sdk/tasks/main.yml new file mode 100644 index 00000000000..3966ed0c641 --- /dev/null +++ b/tests/integration/targets/android_sdk/tasks/main.yml @@ -0,0 +1,69 @@ +- import_tasks: setup.yml + +- name: Setup environment + environment: + PATH: '{{ ansible_env.PATH }}:{{ android_sdk_location }}/cmdline-tools/latest/bin' + ANDROID_HOME: "{{ android_sdk_location }}" + block: + + - name: Accept all licenses + shell: 'yes | sdkmanager --licenses' + changed_when: false + + - name: Install build-tools;34.0.0 + android_sdk: + name: build-tools;34.0.0 + state: present + register: build_tools_installed + + - name: Install build-tools;34.0.0 second time + android_sdk: + name: build-tools;34.0.0 + state: present + register: build_tools_installed2 + + - name: Stat build-tools + stat: + path: "{{ android_sdk_location }}/build-tools/34.0.0" + register: build_tools_34_0_0 + + - name: Delete build-tools;34.0.0 + android_sdk: + name: build-tools;34.0.0 + state: absent + register: build_tools_deleted + + - name: Delete build-tools;34.0.0 second time + android_sdk: + name: build-tools;34.0.0 + state: absent + register: build_tools_deleted2 + + - name: Download old platform-tools + unarchive: + src: https://dl.google.com/android/repository/platform-tools_r27.0.0-linux.zip + remote_src: true + dest: "{{ android_sdk_location }}" + + - name: Try installing platform-tools from sdkmanager + android_sdk: + name: platform-tools + state: present + register: platform_tools_present + + - name: Install (update) platform-tools + android_sdk: + name: platform-tools + state: latest + register: platform_tools_updated + + - name: Run tests + assert: + that: + - build_tools_34_0_0.stat.exists + - build_tools_installed.changed + - not build_tools_installed2.changed + - build_tools_deleted.changed + - not build_tools_deleted2.changed + - not platform_tools_present.changed + - platform_tools_updated.changed \ No newline at end of file diff --git a/tests/integration/targets/android_sdk/tasks/tasks/setup.yml b/tests/integration/targets/android_sdk/tasks/setup.yml similarity index 91% rename from tests/integration/targets/android_sdk/tasks/tasks/setup.yml rename to tests/integration/targets/android_sdk/tasks/setup.yml index 87bbe59c62c..5e09884037b 100644 --- a/tests/integration/targets/android_sdk/tasks/tasks/setup.yml +++ b/tests/integration/targets/android_sdk/tasks/setup.yml @@ -7,23 +7,25 @@ become: true package: name: - - java-21-openjdk +# - java-21-openjdk-headless + - openjdk-17-jdk-headless + - unzip state: present +- name: Print env + debug: + var: ansible_env + - name: Create Android SDK directory - become: true file: path: "{{ android_sdk_location }}" state: directory - owner: vagrant - group: vagrant - name: Check that sdkmanager is installed stat: path: "{{ android_sdk_location }}/cmdline-tools/latest/bin/sdkmanager" register: sdkmanager_installed - - name: Install Android command line tools when: not sdkmanager_installed.stat.exists block: @@ -49,4 +51,3 @@ src: "{{ android_cmdline_temp_dir }}/cmdline-tools/" dest: "{{ android_sdk_location }}/cmdline-tools/latest" remote_src: yes - diff --git a/tests/integration/targets/android_sdk/vars/main.yml b/tests/integration/targets/android_sdk/vars/main.yml index dc836838580..41ce57ad71f 100644 --- a/tests/integration/targets/android_sdk/vars/main.yml +++ b/tests/integration/targets/android_sdk/vars/main.yml @@ -3,5 +3,5 @@ # SPDX-License-Identifier: GPL-3.0-or-later android_cmdline_temp_dir: "/tmp/cmdlinetools" -android_sdk_location: /usr/local/android/sdk +android_sdk_location: "/tmp/androidsdk" commandline_tools_link: https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip From ba7f1bf795219b6b4765a42bcf9a5ba8acbea429 Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Fri, 6 Dec 2024 14:55:35 +0200 Subject: [PATCH 10/48] fixes crash when sdkmanager is invoked from python with LC_ALL=C --- plugins/module_utils/sdkmanager.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/module_utils/sdkmanager.py b/plugins/module_utils/sdkmanager.py index 8cbb31a0feb..8ab157de4ae 100644 --- a/plugins/module_utils/sdkmanager.py +++ b/plugins/module_utils/sdkmanager.py @@ -21,6 +21,7 @@ def sdkmanager_runner(module, **kwargs): list=cmd_runner_fmt.as_fixed('--list'), newer=cmd_runner_fmt.as_fixed("--newer") ), + force_lang="C.UTF-8", **kwargs ) From de6f740af867ed7328bb9cf3332303d172f2990b Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Fri, 6 Dec 2024 19:40:04 +0200 Subject: [PATCH 11/48] fixes latest state --- plugins/modules/android_sdk.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/modules/android_sdk.py b/plugins/modules/android_sdk.py index c002aae87d0..5090224a312 100644 --- a/plugins/modules/android_sdk.py +++ b/plugins/modules/android_sdk.py @@ -40,7 +40,7 @@ def state_present(self): self.vars.installed = AndroidSdk._map_packages_to_names(pending_installation) if not self.check_mode: - rc, stdout, stderr = self.sdkmanager.install_packages(packages) + rc, stdout, stderr = self.sdkmanager.install_packages(pending_installation) if rc != 0: self.do_raise("Could not install packages: %s" % stderr) @@ -50,7 +50,7 @@ def state_absent(self): to_be_deleted = packages.intersection(installed) self.vars.removed = AndroidSdk._map_packages_to_names(to_be_deleted) if not self.check_mode: - rc, stdout, stderr = self.sdkmanager.uninstall_packages(packages) + rc, stdout, stderr = self.sdkmanager.uninstall_packages(to_be_deleted) if rc != 0: self.do_raise("Could not uninstall packages: %s" % stderr) @@ -59,11 +59,11 @@ def state_latest(self): installed = self.sdkmanager.get_installed_packages() updatable = self.sdkmanager.get_updatable_packages() not_installed = packages.difference(installed) - to_be_installed = not_installed.intersection(updatable) + to_be_installed = not_installed.union(updatable) self.vars.installed = AndroidSdk._map_packages_to_names(to_be_installed) if not self.check_mode: - rc, stdout, stderr = self.sdkmanager.install_packages(packages) + rc, stdout, stderr = self.sdkmanager.install_packages(to_be_installed) if rc != 0: self.do_raise("Could not install packages: %s" % stderr) From 97b07eebbafdc03efa78113837855ad3f25daaad Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Fri, 6 Dec 2024 20:45:08 +0200 Subject: [PATCH 12/48] adds sdk_root parameter --- plugins/module_utils/sdkmanager.py | 9 ++++--- plugins/modules/android_sdk.py | 3 ++- .../targets/android_sdk/tasks/main.yml | 25 ++++++++++++++++++- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/plugins/module_utils/sdkmanager.py b/plugins/module_utils/sdkmanager.py index 8ab157de4ae..88e4b5c07b8 100644 --- a/plugins/module_utils/sdkmanager.py +++ b/plugins/module_utils/sdkmanager.py @@ -19,7 +19,8 @@ def sdkmanager_runner(module, **kwargs): update=cmd_runner_fmt.as_fixed("--update"), installed=cmd_runner_fmt.as_fixed("--list_installed"), list=cmd_runner_fmt.as_fixed('--list'), - newer=cmd_runner_fmt.as_fixed("--newer") + newer=cmd_runner_fmt.as_fixed("--newer"), + sdk_root=cmd_runner_fmt.as_opt_eq_val("--sdk_root", ignore_none=True) ), force_lang="C.UTF-8", **kwargs @@ -61,7 +62,7 @@ def __init__(self, runner): self.runner = runner def get_installed_packages(self): - with self.runner('installed') as ctx: + with self.runner('installed sdk_root') as ctx: rc, stdout, stderr = ctx.run() data = stdout.split('\n') @@ -89,7 +90,7 @@ def get_installed_packages(self): return packages def get_updatable_packages(self): - with self.runner('list newer') as ctx: + with self.runner('list newer sdk_root') as ctx: rc, stdout, stderr = ctx.run() data = stdout.split('\n') @@ -129,5 +130,5 @@ def apply_packages_changes(self, packages, state): if len(packages) == 0: return 0, '', '' command_arg = [x.name for x in packages] - with self.runner('state name') as ctx: + with self.runner('state name sdk_root') as ctx: return ctx.run(name=command_arg, state=state) diff --git a/plugins/modules/android_sdk.py b/plugins/modules/android_sdk.py index 5090224a312..4e4130a3cf7 100644 --- a/plugins/modules/android_sdk.py +++ b/plugins/modules/android_sdk.py @@ -8,7 +8,8 @@ class AndroidSdk(StateModuleHelper): argument_spec=dict( state=dict(type='str', default='present', choices=['present', 'absent', 'latest']), package=dict(type='list', elements='str', aliases=['pkg', 'name']), - update=dict(type='bool', default=False) + update=dict(type='bool', default=False), + sdk_root=dict(type='path') ), supports_check_mode=True ) diff --git a/tests/integration/targets/android_sdk/tasks/main.yml b/tests/integration/targets/android_sdk/tasks/main.yml index 3966ed0c641..bf7d1de31e4 100644 --- a/tests/integration/targets/android_sdk/tasks/main.yml +++ b/tests/integration/targets/android_sdk/tasks/main.yml @@ -57,6 +57,27 @@ state: latest register: platform_tools_updated + - name: Create new sdk root + file: + path: "{{ ansible_env.HOME }}/newroot" + state: directory + register: newroot + + - name: Accept licenses in the new root + shell: "yes | sdkmanager --sdk_root={{ newroot.path }} --licenses" + + - name: Install a package to a new root + android_sdk: + name: build-tools;34.0.0 + state: present + sdk_root: "{{ newroot.path }}" + register: new_root_package + + - name: Check package is installed + stat: + path: "{{ newroot.path }}/build-tools/34.0.0" + register: new_root_package_stat + - name: Run tests assert: that: @@ -66,4 +87,6 @@ - build_tools_deleted.changed - not build_tools_deleted2.changed - not platform_tools_present.changed - - platform_tools_updated.changed \ No newline at end of file + - platform_tools_updated.changed + - new_root_package.changed + - new_root_package_stat.stat.exists \ No newline at end of file From b41eb5744e985aa04a4e97d02f6972803e0c7571 Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Sat, 7 Dec 2024 16:24:58 +0200 Subject: [PATCH 13/48] adds channel parameter --- plugins/module_utils/sdkmanager.py | 27 ++++++++++++++++--- plugins/modules/android_sdk.py | 7 ++++- .../targets/android_sdk/handlers/main.yml | 4 +++ .../targets/android_sdk/tasks/main.yml | 11 +++++++- 4 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 tests/integration/targets/android_sdk/handlers/main.yml diff --git a/plugins/module_utils/sdkmanager.py b/plugins/module_utils/sdkmanager.py index 88e4b5c07b8..60b2b63afde 100644 --- a/plugins/module_utils/sdkmanager.py +++ b/plugins/module_utils/sdkmanager.py @@ -1,3 +1,7 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + import re from ansible_collections.community.general.plugins.module_utils import cmd_runner_fmt @@ -8,6 +12,20 @@ "absent": "--uninstall" } +# sdkmanager --help 2>&1 | grep -A 2 -- --channel +_channel_map = { + "stable": 0, + "beta": 1, + "dev": 2, + "canary": 3 +} + + +def map_channel(channel_name): + if channel_name not in _channel_map: + raise ValueError("Unknown channel name '%s'" % channel_name) + return _channel_map[channel_name] + def sdkmanager_runner(module, **kwargs): return CmdRunner( @@ -20,7 +38,8 @@ def sdkmanager_runner(module, **kwargs): installed=cmd_runner_fmt.as_fixed("--list_installed"), list=cmd_runner_fmt.as_fixed('--list'), newer=cmd_runner_fmt.as_fixed("--newer"), - sdk_root=cmd_runner_fmt.as_opt_eq_val("--sdk_root", ignore_none=True) + sdk_root=cmd_runner_fmt.as_opt_eq_val("--sdk_root", ignore_none=True), + channel=cmd_runner_fmt.as_func(lambda x: ["{0}={1}".format("--channel", map_channel(x))]) ), force_lang="C.UTF-8", **kwargs @@ -62,7 +81,7 @@ def __init__(self, runner): self.runner = runner def get_installed_packages(self): - with self.runner('installed sdk_root') as ctx: + with self.runner('installed sdk_root channel') as ctx: rc, stdout, stderr = ctx.run() data = stdout.split('\n') @@ -90,7 +109,7 @@ def get_installed_packages(self): return packages def get_updatable_packages(self): - with self.runner('list newer sdk_root') as ctx: + with self.runner('list newer sdk_root channel') as ctx: rc, stdout, stderr = ctx.run() data = stdout.split('\n') @@ -130,5 +149,5 @@ def apply_packages_changes(self, packages, state): if len(packages) == 0: return 0, '', '' command_arg = [x.name for x in packages] - with self.runner('state name sdk_root') as ctx: + with self.runner('state name sdk_root channel') as ctx: return ctx.run(name=command_arg, state=state) diff --git a/plugins/modules/android_sdk.py b/plugins/modules/android_sdk.py index 4e4130a3cf7..51ce2702bde 100644 --- a/plugins/modules/android_sdk.py +++ b/plugins/modules/android_sdk.py @@ -1,3 +1,7 @@ +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + from ansible_collections.community.general.plugins.module_utils.mh.module_helper import StateModuleHelper from ansible_collections.community.general.plugins.module_utils.sdkmanager import sdkmanager_runner, Package, \ AndroidSdkManager @@ -9,7 +13,8 @@ class AndroidSdk(StateModuleHelper): state=dict(type='str', default='present', choices=['present', 'absent', 'latest']), package=dict(type='list', elements='str', aliases=['pkg', 'name']), update=dict(type='bool', default=False), - sdk_root=dict(type='path') + sdk_root=dict(type='path'), + channel=dict(type='str', default='stable', choices=['stable', 'beta', 'dev', 'canary']) ), supports_check_mode=True ) diff --git a/tests/integration/targets/android_sdk/handlers/main.yml b/tests/integration/targets/android_sdk/handlers/main.yml new file mode 100644 index 00000000000..6a9dc696968 --- /dev/null +++ b/tests/integration/targets/android_sdk/handlers/main.yml @@ -0,0 +1,4 @@ +- name: delete_newroot + file: + path: "{{ newroot.path }}" + state: absent \ No newline at end of file diff --git a/tests/integration/targets/android_sdk/tasks/main.yml b/tests/integration/targets/android_sdk/tasks/main.yml index bf7d1de31e4..2f282443580 100644 --- a/tests/integration/targets/android_sdk/tasks/main.yml +++ b/tests/integration/targets/android_sdk/tasks/main.yml @@ -62,6 +62,7 @@ path: "{{ ansible_env.HOME }}/newroot" state: directory register: newroot + notify: delete_newroot - name: Accept licenses in the new root shell: "yes | sdkmanager --sdk_root={{ newroot.path }} --licenses" @@ -78,6 +79,13 @@ path: "{{ newroot.path }}/build-tools/34.0.0" register: new_root_package_stat + - name: Install a package from canary channel + android_sdk: + name: build-tools;33.0.0 + state: present + channel: canary + register: package_canary + - name: Run tests assert: that: @@ -89,4 +97,5 @@ - not platform_tools_present.changed - platform_tools_updated.changed - new_root_package.changed - - new_root_package_stat.stat.exists \ No newline at end of file + - new_root_package_stat.stat.exists + - package_canary.changed \ No newline at end of file From 6d756e2271168657180c21436cf9f2322f87762d Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Sat, 7 Dec 2024 16:30:23 +0200 Subject: [PATCH 14/48] simplifies regexps, removes unused named groups --- plugins/module_utils/sdkmanager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/module_utils/sdkmanager.py b/plugins/module_utils/sdkmanager.py index 60b2b63afde..f7090b76241 100644 --- a/plugins/module_utils/sdkmanager.py +++ b/plugins/module_utils/sdkmanager.py @@ -71,11 +71,11 @@ class AndroidSdkManager(object): # Example: ' platform-tools | 27.0.0 | Android SDK Platform-Tools 27 | platform-tools ' _RE_INSTALLED_PACKAGE = ( - re.compile(r'^\s+(?P\S+)\s+\|\s+(?P\S+)\s+\|\s(?P.+)\s\|\s+(\S+)\s*$') + re.compile(r'^\s*(?P\S+)\s*\|\s*\S+\s*\|\s*.+\s*\|\s*(\S+)\s*$') ) # Example: ' platform-tools | 27.0.0 | 35.0.2' - _RE_UPDATABLE_PACKAGE = re.compile(r'^\s+(?P\S+)\s+\|\s+(?P\S+)\s+\|\s+(?P\S+)\s*$') + _RE_UPDATABLE_PACKAGE = re.compile(r'^\s*(?P\S+)\s*\|\s*\S+\s*\|\s*\S+\s*$') def __init__(self, runner): self.runner = runner From 4441be779f337c2f09918e2c16f9f5668af0a019 Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Sat, 7 Dec 2024 16:54:22 +0200 Subject: [PATCH 15/48] minor refactoring of sdkmanager parsing --- plugins/module_utils/sdkmanager.py | 79 ++++++++++++------------------ 1 file changed, 30 insertions(+), 49 deletions(-) diff --git a/plugins/module_utils/sdkmanager.py b/plugins/module_utils/sdkmanager.py index f7090b76241..a4c6103d679 100644 --- a/plugins/module_utils/sdkmanager.py +++ b/plugins/module_utils/sdkmanager.py @@ -83,60 +83,41 @@ def __init__(self, runner): def get_installed_packages(self): with self.runner('installed sdk_root channel') as ctx: rc, stdout, stderr = ctx.run() - - data = stdout.split('\n') - - installed_section_found = False - i = 0 - lines_count = len(data) - packages = set() - - while i < lines_count: - - if not installed_section_found: - installed_section_found = self._RE_INSTALLED_PACKAGES_HEADER.match(data[i]) - if installed_section_found: - i += 3 - else: - i += 1 - continue - else: - p = self._RE_INSTALLED_PACKAGE.search(data[i]) - if p: - package = Package(p.group('name')) - packages.add(package) - i += 1 - return packages + return self._parse_packages(stdout, self._RE_INSTALLED_PACKAGES_HEADER, self._RE_INSTALLED_PACKAGE) def get_updatable_packages(self): with self.runner('list newer sdk_root channel') as ctx: rc, stdout, stderr = ctx.run() - data = stdout.split('\n') - - updatable_section_found = False - i = 0 - lines_count = len(data) - packages = set() - - while i < lines_count: - if not updatable_section_found: - updatable_section_found = self._RE_UPDATABLE_PACKAGES_HEADER.match(data[i]) - if updatable_section_found: - # Table has the following structure. Once it is found, 2 lines need to be skipped - # - # Available Updates: <--- we are here - # ID | Installed | Available - # ------- | ------- | ------- - # platform-tools | 27.0.0 | 35.0.2 <--- skip to here - i += 3 # skip table header - else: - i += 1 # just iterate next until we find the section's header - continue + return self._parse_packages(stdout, self._RE_UPDATABLE_PACKAGES_HEADER, self._RE_UPDATABLE_PACKAGE) + + @staticmethod + def _parse_packages(stdout, header_regexp, row_regexp): + data = stdout.split('\n') + + updatable_section_found = False + i = 0 + lines_count = len(data) + packages = set() + + while i < lines_count: + if not updatable_section_found: + updatable_section_found = header_regexp.match(data[i]) + if updatable_section_found: + # Table has the following structure. Once it is found, 2 lines need to be skipped + # + # Available Updates: <--- we are here + # ID | Installed | Available + # ------- | ------- | ------- + # platform-tools | 27.0.0 | 35.0.2 <--- skip to here + i += 3 # skip table header else: - p = self._RE_UPDATABLE_PACKAGE.match(data[i]) - if p: - packages.add(Package(p.group('name'))) - i += 1 + i += 1 # just iterate next until we find the section's header + continue + else: + p = row_regexp.match(data[i]) + if p: + packages.add(Package(p.group('name'))) + i += 1 return packages def install_packages(self, packages): From 100a91fdebdf2d570abd1dc2a5838b148941f43c Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Sat, 7 Dec 2024 17:56:20 +0200 Subject: [PATCH 16/48] adds java dependency variable for different distributions --- .../integration/targets/android_sdk/handlers/main.yml | 5 +++++ tests/integration/targets/android_sdk/tasks/main.yml | 5 +++++ tests/integration/targets/android_sdk/tasks/setup.yml | 10 ++++------ tests/integration/targets/android_sdk/vars/Alpine.yml | 6 ++++++ .../integration/targets/android_sdk/vars/Archlinux.yml | 6 ++++++ tests/integration/targets/android_sdk/vars/Debian.yml | 6 ++++++ tests/integration/targets/android_sdk/vars/FreeBSD.yml | 6 ++++++ tests/integration/targets/android_sdk/vars/RedHat.yml | 6 ++++++ tests/integration/targets/android_sdk/vars/Suse.yml | 6 ++++++ tests/integration/targets/android_sdk/vars/main.yml | 1 + 10 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 tests/integration/targets/android_sdk/vars/Alpine.yml create mode 100644 tests/integration/targets/android_sdk/vars/Archlinux.yml create mode 100644 tests/integration/targets/android_sdk/vars/Debian.yml create mode 100644 tests/integration/targets/android_sdk/vars/FreeBSD.yml create mode 100644 tests/integration/targets/android_sdk/vars/RedHat.yml create mode 100644 tests/integration/targets/android_sdk/vars/Suse.yml diff --git a/tests/integration/targets/android_sdk/handlers/main.yml b/tests/integration/targets/android_sdk/handlers/main.yml index 6a9dc696968..7be9b2436df 100644 --- a/tests/integration/targets/android_sdk/handlers/main.yml +++ b/tests/integration/targets/android_sdk/handlers/main.yml @@ -1,3 +1,8 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + - name: delete_newroot file: path: "{{ newroot.path }}" diff --git a/tests/integration/targets/android_sdk/tasks/main.yml b/tests/integration/targets/android_sdk/tasks/main.yml index 2f282443580..2e00346d51e 100644 --- a/tests/integration/targets/android_sdk/tasks/main.yml +++ b/tests/integration/targets/android_sdk/tasks/main.yml @@ -1,3 +1,8 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + - import_tasks: setup.yml - name: Setup environment diff --git a/tests/integration/targets/android_sdk/tasks/setup.yml b/tests/integration/targets/android_sdk/tasks/setup.yml index 5e09884037b..066af3abee0 100644 --- a/tests/integration/targets/android_sdk/tasks/setup.yml +++ b/tests/integration/targets/android_sdk/tasks/setup.yml @@ -1,21 +1,19 @@ +--- # Copyright (c) Ansible Project # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later +- name: Include OS-specific variables + include_vars: '{{ ansible_os_family }}.yml' - name: Install dependencies become: true package: name: -# - java-21-openjdk-headless - - openjdk-17-jdk-headless + - "{{ openjdk_pkg }}" - unzip state: present -- name: Print env - debug: - var: ansible_env - - name: Create Android SDK directory file: path: "{{ android_sdk_location }}" diff --git a/tests/integration/targets/android_sdk/vars/Alpine.yml b/tests/integration/targets/android_sdk/vars/Alpine.yml new file mode 100644 index 00000000000..593925f0432 --- /dev/null +++ b/tests/integration/targets/android_sdk/vars/Alpine.yml @@ -0,0 +1,6 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +openjdk_pkg: openjdk17-jre-headless diff --git a/tests/integration/targets/android_sdk/vars/Archlinux.yml b/tests/integration/targets/android_sdk/vars/Archlinux.yml new file mode 100644 index 00000000000..ff46870671d --- /dev/null +++ b/tests/integration/targets/android_sdk/vars/Archlinux.yml @@ -0,0 +1,6 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +openjdk_pkg: jre17-openjdk-headless diff --git a/tests/integration/targets/android_sdk/vars/Debian.yml b/tests/integration/targets/android_sdk/vars/Debian.yml new file mode 100644 index 00000000000..ddcfaaf1e36 --- /dev/null +++ b/tests/integration/targets/android_sdk/vars/Debian.yml @@ -0,0 +1,6 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +openjdk_pkg: openjdk-17-jre-headless diff --git a/tests/integration/targets/android_sdk/vars/FreeBSD.yml b/tests/integration/targets/android_sdk/vars/FreeBSD.yml new file mode 100644 index 00000000000..61c1858423a --- /dev/null +++ b/tests/integration/targets/android_sdk/vars/FreeBSD.yml @@ -0,0 +1,6 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +openjdk_pkg: openjdk17-jre diff --git a/tests/integration/targets/android_sdk/vars/RedHat.yml b/tests/integration/targets/android_sdk/vars/RedHat.yml new file mode 100644 index 00000000000..40f44bd7735 --- /dev/null +++ b/tests/integration/targets/android_sdk/vars/RedHat.yml @@ -0,0 +1,6 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +openjdk_pkg: java-17-openjdk-headless diff --git a/tests/integration/targets/android_sdk/vars/Suse.yml b/tests/integration/targets/android_sdk/vars/Suse.yml new file mode 100644 index 00000000000..40f44bd7735 --- /dev/null +++ b/tests/integration/targets/android_sdk/vars/Suse.yml @@ -0,0 +1,6 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +openjdk_pkg: java-17-openjdk-headless diff --git a/tests/integration/targets/android_sdk/vars/main.yml b/tests/integration/targets/android_sdk/vars/main.yml index 41ce57ad71f..9ba619a6d5c 100644 --- a/tests/integration/targets/android_sdk/vars/main.yml +++ b/tests/integration/targets/android_sdk/vars/main.yml @@ -1,3 +1,4 @@ +--- # Copyright (c) Ansible Project # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later From fdef9c487774c9c9e772a3fcefbb3b898f7cf2b5 Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Sat, 7 Dec 2024 17:56:56 +0200 Subject: [PATCH 17/48] adds RETURN documentation --- plugins/modules/android_sdk.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/plugins/modules/android_sdk.py b/plugins/modules/android_sdk.py index 51ce2702bde..09a233b5199 100644 --- a/plugins/modules/android_sdk.py +++ b/plugins/modules/android_sdk.py @@ -2,6 +2,20 @@ __metaclass__ = type +RETURN = r""" +installed: + description: a list of packages that have been installed + returned: when packages have changed + type: list + sample: ['build-tools;34.0.0', 'platform-tools'] + +removed: + description: a list of packages that have been removed + returned: when packages have changed + type: list + sample: ['build-tools;34.0.0', 'platform-tools'] +""" + from ansible_collections.community.general.plugins.module_utils.mh.module_helper import StateModuleHelper from ansible_collections.community.general.plugins.module_utils.sdkmanager import sdkmanager_runner, Package, \ AndroidSdkManager From 9b3d4449b93ea347919021909516f57ffd7dee2e Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Sat, 7 Dec 2024 18:24:27 +0200 Subject: [PATCH 18/48] adds check for nonexisting package --- plugins/module_utils/sdkmanager.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/plugins/module_utils/sdkmanager.py b/plugins/module_utils/sdkmanager.py index a4c6103d679..bcad825ee0c 100644 --- a/plugins/module_utils/sdkmanager.py +++ b/plugins/module_utils/sdkmanager.py @@ -65,6 +65,10 @@ def __eq__(self, other): return self.name == other.name +class SdkManagerException(Exception): + pass + + class AndroidSdkManager(object): _RE_INSTALLED_PACKAGES_HEADER = re.compile(r'^Installed packages:$') _RE_UPDATABLE_PACKAGES_HEADER = re.compile(r'^Available Updates:$') @@ -77,6 +81,8 @@ class AndroidSdkManager(object): # Example: ' platform-tools | 27.0.0 | 35.0.2' _RE_UPDATABLE_PACKAGE = re.compile(r'^\s*(?P\S+)\s*\|\s*\S+\s*\|\s*\S+\s*$') + _RE_UNKNOWN_PACKAGE = re.compile(r'^Warning: Failed to find package \'(?P\S+)\'\s*$') + def __init__(self, runner): self.runner = runner @@ -120,6 +126,15 @@ def _parse_packages(stdout, header_regexp, row_regexp): i += 1 return packages + def _try_parse_stderr(self, stderr): + data = stderr.split('\n') + + for line in data: + unknown_package_regex = self._RE_UNKNOWN_PACKAGE.match(line) + if unknown_package_regex: + package = unknown_package_regex.group('package') + raise SdkManagerException("Unknown package %s" % package) + def install_packages(self, packages): return self.apply_packages_changes(packages, 'present') @@ -131,4 +146,9 @@ def apply_packages_changes(self, packages, state): return 0, '', '' command_arg = [x.name for x in packages] with self.runner('state name sdk_root channel') as ctx: - return ctx.run(name=command_arg, state=state) + rc, stdout, stderr = ctx.run(name=command_arg, state=state) + if rc != 0: + err = self._try_parse_stderr(stderr) + if err: + raise err + return rc, stdout, stderr From ece03d17cef320f84f7c9482d327b68fc64d1dbf Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Sat, 7 Dec 2024 19:23:28 +0200 Subject: [PATCH 19/48] adds check for non-accepted licenses --- plugins/module_utils/sdkmanager.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/plugins/module_utils/sdkmanager.py b/plugins/module_utils/sdkmanager.py index bcad825ee0c..cc4715223d3 100644 --- a/plugins/module_utils/sdkmanager.py +++ b/plugins/module_utils/sdkmanager.py @@ -82,6 +82,8 @@ class AndroidSdkManager(object): _RE_UPDATABLE_PACKAGE = re.compile(r'^\s*(?P\S+)\s*\|\s*\S+\s*\|\s*\S+\s*$') _RE_UNKNOWN_PACKAGE = re.compile(r'^Warning: Failed to find package \'(?P\S+)\'\s*$') + _RE_ACCEPT_LICENSE = re.compile(r'^The following packages can not be installed since their licenses or those of ' + r'the packages they depend on were not accepted') def __init__(self, runner): self.runner = runner @@ -128,7 +130,6 @@ def _parse_packages(stdout, header_regexp, row_regexp): def _try_parse_stderr(self, stderr): data = stderr.split('\n') - for line in data: unknown_package_regex = self._RE_UNKNOWN_PACKAGE.match(line) if unknown_package_regex: @@ -145,8 +146,17 @@ def apply_packages_changes(self, packages, state): if len(packages) == 0: return 0, '', '' command_arg = [x.name for x in packages] - with self.runner('state name sdk_root channel') as ctx: - rc, stdout, stderr = ctx.run(name=command_arg, state=state) + + data = 'N' # Answer 'No' in case sdkmanager wants us to accept license + with self.runner('state name sdk_root channel', data=data) as ctx: + rc, stdout, stderr = ctx.run(name=command_arg, state=state, data=data) + + data = stdout.split('\n') + + for line in data: + if self._RE_ACCEPT_LICENSE.match(line): + raise SdkManagerException("Licenses for some packages were not accepted") + if rc != 0: err = self._try_parse_stderr(stderr) if err: From e8ef594c4270b30730235d515e6be2396093070f Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Mon, 9 Dec 2024 14:10:26 +0200 Subject: [PATCH 20/48] removes excessive methods from sdkmanager --- plugins/module_utils/sdkmanager.py | 11 +++-------- plugins/modules/android_sdk.py | 6 +++--- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/plugins/module_utils/sdkmanager.py b/plugins/module_utils/sdkmanager.py index cc4715223d3..34e7cc168b2 100644 --- a/plugins/module_utils/sdkmanager.py +++ b/plugins/module_utils/sdkmanager.py @@ -136,20 +136,15 @@ def _try_parse_stderr(self, stderr): package = unknown_package_regex.group('package') raise SdkManagerException("Unknown package %s" % package) - def install_packages(self, packages): - return self.apply_packages_changes(packages, 'present') - - def uninstall_packages(self, packages): - return self.apply_packages_changes(packages, 'absent') - - def apply_packages_changes(self, packages, state): + def apply_packages_changes(self, packages): + """ Install or delete packages, depending on the `module.vars.state` parameter """ if len(packages) == 0: return 0, '', '' command_arg = [x.name for x in packages] data = 'N' # Answer 'No' in case sdkmanager wants us to accept license with self.runner('state name sdk_root channel', data=data) as ctx: - rc, stdout, stderr = ctx.run(name=command_arg, state=state, data=data) + rc, stdout, stderr = ctx.run(name=command_arg, data=data) data = stdout.split('\n') diff --git a/plugins/modules/android_sdk.py b/plugins/modules/android_sdk.py index 09a233b5199..7923589d0f4 100644 --- a/plugins/modules/android_sdk.py +++ b/plugins/modules/android_sdk.py @@ -60,7 +60,7 @@ def state_present(self): self.vars.installed = AndroidSdk._map_packages_to_names(pending_installation) if not self.check_mode: - rc, stdout, stderr = self.sdkmanager.install_packages(pending_installation) + rc, stdout, stderr = self.sdkmanager.apply_packages_changes(pending_installation) if rc != 0: self.do_raise("Could not install packages: %s" % stderr) @@ -70,7 +70,7 @@ def state_absent(self): to_be_deleted = packages.intersection(installed) self.vars.removed = AndroidSdk._map_packages_to_names(to_be_deleted) if not self.check_mode: - rc, stdout, stderr = self.sdkmanager.uninstall_packages(to_be_deleted) + rc, stdout, stderr = self.sdkmanager.apply_packages_changes(to_be_deleted) if rc != 0: self.do_raise("Could not uninstall packages: %s" % stderr) @@ -83,7 +83,7 @@ def state_latest(self): self.vars.installed = AndroidSdk._map_packages_to_names(to_be_installed) if not self.check_mode: - rc, stdout, stderr = self.sdkmanager.install_packages(to_be_installed) + rc, stdout, stderr = self.sdkmanager.apply_packages_changes(to_be_installed) if rc != 0: self.do_raise("Could not install packages: %s" % stderr) From 1ac1ad546831341210c7ff32fb32a0e2c2ef5eba Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Mon, 9 Dec 2024 14:16:37 +0200 Subject: [PATCH 21/48] removes unused 'update' module parameter, packages may be updated using 'latest' state --- plugins/modules/android_sdk.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/plugins/modules/android_sdk.py b/plugins/modules/android_sdk.py index 7923589d0f4..90f94a29c08 100644 --- a/plugins/modules/android_sdk.py +++ b/plugins/modules/android_sdk.py @@ -26,7 +26,6 @@ class AndroidSdk(StateModuleHelper): argument_spec=dict( state=dict(type='str', default='present', choices=['present', 'absent', 'latest']), package=dict(type='list', elements='str', aliases=['pkg', 'name']), - update=dict(type='bool', default=False), sdk_root=dict(type='path'), channel=dict(type='str', default='stable', choices=['stable', 'beta', 'dev', 'canary']) ), @@ -87,21 +86,10 @@ def state_latest(self): if rc != 0: self.do_raise("Could not install packages: %s" % stderr) - def update_packages(self): - pass - # with self.sdkmanager('update') as ctx: - # ctx.run() - @staticmethod def _map_packages_to_names(packages): return [x.name for x in packages] - def __run__(self): - super().__run__() - - if self.vars.update: - self.update_packages() - def main(): AndroidSdk.execute() From 22b317fd792e289f2520311d96712ee7d3da74cd Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Mon, 9 Dec 2024 14:16:44 +0200 Subject: [PATCH 22/48] minor refactoring --- plugins/module_utils/sdkmanager.py | 78 +++++++++++++++--------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/plugins/module_utils/sdkmanager.py b/plugins/module_utils/sdkmanager.py index 34e7cc168b2..2e1ce9eb492 100644 --- a/plugins/module_utils/sdkmanager.py +++ b/plugins/module_utils/sdkmanager.py @@ -7,13 +7,13 @@ from ansible_collections.community.general.plugins.module_utils import cmd_runner_fmt from ansible_collections.community.general.plugins.module_utils.cmd_runner import CmdRunner -_state_map = { +__state_map = { "present": "--install", "absent": "--uninstall" } # sdkmanager --help 2>&1 | grep -A 2 -- --channel -_channel_map = { +__channel_map = { "stable": 0, "beta": 1, "dev": 2, @@ -21,10 +21,10 @@ } -def map_channel(channel_name): - if channel_name not in _channel_map: +def __map_channel(channel_name): + if channel_name not in __channel_map: raise ValueError("Unknown channel name '%s'" % channel_name) - return _channel_map[channel_name] + return __channel_map[channel_name] def sdkmanager_runner(module, **kwargs): @@ -32,16 +32,16 @@ def sdkmanager_runner(module, **kwargs): module, command='sdkmanager', arg_formats=dict( - state=cmd_runner_fmt.as_map(_state_map), + state=cmd_runner_fmt.as_map(__state_map), name=cmd_runner_fmt.as_list(), update=cmd_runner_fmt.as_fixed("--update"), installed=cmd_runner_fmt.as_fixed("--list_installed"), list=cmd_runner_fmt.as_fixed('--list'), newer=cmd_runner_fmt.as_fixed("--newer"), sdk_root=cmd_runner_fmt.as_opt_eq_val("--sdk_root", ignore_none=True), - channel=cmd_runner_fmt.as_func(lambda x: ["{0}={1}".format("--channel", map_channel(x))]) + channel=cmd_runner_fmt.as_func(lambda x: ["{0}={1}".format("--channel", __map_channel(x))]) ), - force_lang="C.UTF-8", + force_lang="C.UTF-8", # Without this, sdkmanager binary crashes **kwargs ) @@ -98,6 +98,36 @@ def get_updatable_packages(self): rc, stdout, stderr = ctx.run() return self._parse_packages(stdout, self._RE_UPDATABLE_PACKAGES_HEADER, self._RE_UPDATABLE_PACKAGE) + def apply_packages_changes(self, packages): + """ Install or delete packages, depending on the `module.vars.state` parameter """ + if len(packages) == 0: + return 0, '', '' + command_arg = [x.name for x in packages] + + data = 'N' # Answer 'No' in case sdkmanager wants us to accept license + with self.runner('state name sdk_root channel', data=data) as ctx: + rc, stdout, stderr = ctx.run(name=command_arg, data=data) + + data = stdout.split('\n') + + for line in data: + if self._RE_ACCEPT_LICENSE.match(line): + raise SdkManagerException("Licenses for some packages were not accepted") + + if rc != 0: + err = self._try_parse_stderr(stderr) + if err: + raise err + return rc, stdout, stderr + + def _try_parse_stderr(self, stderr): + data = stderr.split('\n') + for line in data: + unknown_package_regex = self._RE_UNKNOWN_PACKAGE.match(line) + if unknown_package_regex: + package = unknown_package_regex.group('package') + raise SdkManagerException("Unknown package %s" % package) + @staticmethod def _parse_packages(stdout, header_regexp, row_regexp): data = stdout.split('\n') @@ -111,7 +141,7 @@ def _parse_packages(stdout, header_regexp, row_regexp): if not updatable_section_found: updatable_section_found = header_regexp.match(data[i]) if updatable_section_found: - # Table has the following structure. Once it is found, 2 lines need to be skipped + # Table has the following structure. Once header is found, 2 lines need to be skipped # # Available Updates: <--- we are here # ID | Installed | Available @@ -127,33 +157,3 @@ def _parse_packages(stdout, header_regexp, row_regexp): packages.add(Package(p.group('name'))) i += 1 return packages - - def _try_parse_stderr(self, stderr): - data = stderr.split('\n') - for line in data: - unknown_package_regex = self._RE_UNKNOWN_PACKAGE.match(line) - if unknown_package_regex: - package = unknown_package_regex.group('package') - raise SdkManagerException("Unknown package %s" % package) - - def apply_packages_changes(self, packages): - """ Install or delete packages, depending on the `module.vars.state` parameter """ - if len(packages) == 0: - return 0, '', '' - command_arg = [x.name for x in packages] - - data = 'N' # Answer 'No' in case sdkmanager wants us to accept license - with self.runner('state name sdk_root channel', data=data) as ctx: - rc, stdout, stderr = ctx.run(name=command_arg, data=data) - - data = stdout.split('\n') - - for line in data: - if self._RE_ACCEPT_LICENSE.match(line): - raise SdkManagerException("Licenses for some packages were not accepted") - - if rc != 0: - err = self._try_parse_stderr(stderr) - if err: - raise err - return rc, stdout, stderr From e499391688c9e51627604f2ab1749ca5da7365a4 Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Mon, 9 Dec 2024 14:50:00 +0200 Subject: [PATCH 23/48] adds EXAMPLES doc section --- plugins/modules/android_sdk.py | 40 ++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/plugins/modules/android_sdk.py b/plugins/modules/android_sdk.py index 90f94a29c08..df6f4659192 100644 --- a/plugins/modules/android_sdk.py +++ b/plugins/modules/android_sdk.py @@ -2,7 +2,43 @@ __metaclass__ = type -RETURN = r""" +EXAMPLES = r''' +- name: Install build-tools;34.0.0 + community.general.android_sdk: + name: build-tools;34.0.0 + state: present + +- name: Install build-tools;34.0.0 and platform-tools + community.general.android_sdk: + name: + - build-tools;34.0.0 + - platform-tools + state: present + +- name: Delete build-tools;34.0.0 + community.general.android_sdk: + name: build-tools;34.0.0 + state: absent + +- name: Install platform-tools or update if installed + community.general.android_sdk: + name: platform-tools + state: latest + +- name: Install build-tools;34.0.0 to a different SDK root + community.general.android_sdk: + name: build-tools;34.0.0 + state: present + sdk_root: "/path/to/new/root" + +- name: Install a package from another channel + community.general.android_sdk: + name: some-package-present-in-canary-channel + state: present + channel: canary +''' + +RETURN = r''' installed: description: a list of packages that have been installed returned: when packages have changed @@ -14,7 +50,7 @@ returned: when packages have changed type: list sample: ['build-tools;34.0.0', 'platform-tools'] -""" +''' from ansible_collections.community.general.plugins.module_utils.mh.module_helper import StateModuleHelper from ansible_collections.community.general.plugins.module_utils.sdkmanager import sdkmanager_runner, Package, \ From 7e5382762a899ef57b0a5642050c037c58033f1d Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Mon, 9 Dec 2024 16:54:42 +0200 Subject: [PATCH 24/48] adds DOCUMENTATION section and license headers --- plugins/module_utils/sdkmanager.py | 7 +++ plugins/modules/android_sdk.py | 81 +++++++++++++++++++++++++++--- 2 files changed, 81 insertions(+), 7 deletions(-) diff --git a/plugins/module_utils/sdkmanager.py b/plugins/module_utils/sdkmanager.py index 2e1ce9eb492..0d9fb6e2dec 100644 --- a/plugins/module_utils/sdkmanager.py +++ b/plugins/module_utils/sdkmanager.py @@ -1,3 +1,10 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright (c) 2024, Stanislav Shamilov +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + from __future__ import absolute_import, division, print_function __metaclass__ = type diff --git a/plugins/modules/android_sdk.py b/plugins/modules/android_sdk.py index df6f4659192..b37427448f8 100644 --- a/plugins/modules/android_sdk.py +++ b/plugins/modules/android_sdk.py @@ -1,36 +1,103 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright (c) 2024, Stanislav Shamilov +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + from __future__ import absolute_import, division, print_function __metaclass__ = type +DOCUMENTATION = r''' +--- +module: android_sdk +short_description: Manages Android SDK packages +description: + - Manages Android SDK packages. + - Allows installation from different channels (stable, beta, dev, canary). + - Allows installation of packages to a non-default SDK root directory. +author: Stanislav Shamilov (@shamilovstas) +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none +options: + name: + description: + - A name of an Android SDK package (for instance, V(build-tools;34.0.0)). + aliases: ['package', 'pkg'] + type: list + elements: str + state: + description: + - Indicates the desired package(s) state. + - V(present) ensures that package(s) is/are present. + - V(absent) ensures that package(s) is/are absent. + - V(latest) ensures that package(s) is/are installed and updated to the latest version(s). + choices: ['present', 'absent', 'latest'] + default: present + type: str + sdk_root: + description: + - Provides path for an alternative directory to install Android SDK packages to. By default, all packages + are installed to the directory where C(sdkmanager) is installed. + type: path + channel: + description: + - Indicates what channel must C(sdkmanager) use for installation of packages. + choices: ['stable', 'beta', 'dev', 'canary'] + default: stable + type: str +requirements: + - C(java) >= 17 + - C(sdkmanager) Command line tool for installing Android SDK packages. +notes: + - It is assumed that all necessary licenses for the packages that are to be installed are accepted before using + this module. If there are any unaccepted licenses, the module will fail. In order to accept all licenses that + have not been already accepted, one may use the C(sdkmanager --licenses) command + (see U(https://developer.android.com/tools/sdkmanager#accept-licenses)). +seealso: + - name: sdkmanager tool documentation + description: Detailed information of how to install and use sdkmanager command line tool. + link: https://developer.android.com/tools/sdkmanager +''' + EXAMPLES = r''' +- name: Before installing Android SDK packages, all licenses must be accepted + shell: "yes | sdkmanager --licenses" + - name: Install build-tools;34.0.0 community.general.android_sdk: name: build-tools;34.0.0 state: present - + - name: Install build-tools;34.0.0 and platform-tools community.general.android_sdk: - name: + name: - build-tools;34.0.0 - platform-tools state: present - + - name: Delete build-tools;34.0.0 community.general.android_sdk: name: build-tools;34.0.0 state: absent - + - name: Install platform-tools or update if installed community.general.android_sdk: name: platform-tools state: latest - + - name: Install build-tools;34.0.0 to a different SDK root community.general.android_sdk: name: build-tools;34.0.0 state: present sdk_root: "/path/to/new/root" - + - name: Install a package from another channel community.general.android_sdk: name: some-package-present-in-canary-channel @@ -44,7 +111,7 @@ returned: when packages have changed type: list sample: ['build-tools;34.0.0', 'platform-tools'] - + removed: description: a list of packages that have been removed returned: when packages have changed From e5a6d027de3fde04391fd686dd527522d5c348db Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Tue, 10 Dec 2024 15:34:25 +0200 Subject: [PATCH 25/48] fixes formatting issues --- plugins/modules/android_sdk.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/android_sdk.py b/plugins/modules/android_sdk.py index b37427448f8..fc400369bdd 100644 --- a/plugins/modules/android_sdk.py +++ b/plugins/modules/android_sdk.py @@ -69,7 +69,7 @@ EXAMPLES = r''' - name: Before installing Android SDK packages, all licenses must be accepted shell: "yes | sdkmanager --licenses" - + - name: Install build-tools;34.0.0 community.general.android_sdk: name: build-tools;34.0.0 From 003bd1e6bdde03b73e1b7b08db580fb18caa53c6 Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Tue, 10 Dec 2024 17:41:59 +0200 Subject: [PATCH 26/48] removes diff_params --- plugins/module_utils/sdkmanager.py | 1 - plugins/modules/android_sdk.py | 1 - 2 files changed, 2 deletions(-) diff --git a/plugins/module_utils/sdkmanager.py b/plugins/module_utils/sdkmanager.py index 0d9fb6e2dec..4280d47023f 100644 --- a/plugins/module_utils/sdkmanager.py +++ b/plugins/module_utils/sdkmanager.py @@ -41,7 +41,6 @@ def sdkmanager_runner(module, **kwargs): arg_formats=dict( state=cmd_runner_fmt.as_map(__state_map), name=cmd_runner_fmt.as_list(), - update=cmd_runner_fmt.as_fixed("--update"), installed=cmd_runner_fmt.as_fixed("--list_installed"), list=cmd_runner_fmt.as_fixed('--list'), newer=cmd_runner_fmt.as_fixed("--newer"), diff --git a/plugins/modules/android_sdk.py b/plugins/modules/android_sdk.py index fc400369bdd..d704526355b 100644 --- a/plugins/modules/android_sdk.py +++ b/plugins/modules/android_sdk.py @@ -137,7 +137,6 @@ class AndroidSdk(StateModuleHelper): use_old_vardict = False output_params = ('installed', 'removed') change_params = ('installed', 'removed') - diff_params = ('installed', 'removed') def __init_module__(self): self.sdkmanager = AndroidSdkManager(sdkmanager_runner(self.module)) From 20eb1aa7cb6e9c92be172bcea9baf966fdb5bbc4 Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Wed, 11 Dec 2024 00:03:43 +0200 Subject: [PATCH 27/48] adds maintainer --- .github/BOTMETA.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/BOTMETA.yml b/.github/BOTMETA.yml index ec9b9b7ddc8..d0cc407f7e2 100644 --- a/.github/BOTMETA.yml +++ b/.github/BOTMETA.yml @@ -375,6 +375,8 @@ files: maintainers: $team_redfish $module_utils/remote_management/lxca/common.py: maintainers: navalkp prabhosa + $module_utils/sdkmanager.py: + maintainers: shamilovstas $module_utils/scaleway.py: labels: cloud scaleway maintainers: $team_scaleway @@ -420,6 +422,8 @@ files: ignore: DavidWittman jiuka labels: alternatives maintainers: mulby + $modules/android_sdk.py: + maintainers: shamilovstas $modules/ansible_galaxy_install.py: maintainers: russoz $modules/apache2_mod_proxy.py: From 3358ac91e9e5a7c0c8abfd66f4fe6a7150d61991 Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Wed, 11 Dec 2024 01:03:29 +0200 Subject: [PATCH 28/48] fixes sanity check issues in sdkmanager --- plugins/module_utils/sdkmanager.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/plugins/module_utils/sdkmanager.py b/plugins/module_utils/sdkmanager.py index 4280d47023f..8db9b5c133b 100644 --- a/plugins/module_utils/sdkmanager.py +++ b/plugins/module_utils/sdkmanager.py @@ -1,4 +1,3 @@ -#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (c) 2024, Stanislav Shamilov @@ -121,9 +120,7 @@ def apply_packages_changes(self, packages): raise SdkManagerException("Licenses for some packages were not accepted") if rc != 0: - err = self._try_parse_stderr(stderr) - if err: - raise err + self._try_parse_stderr(stderr) return rc, stdout, stderr def _try_parse_stderr(self, stderr): From 14f0d6e401ee5711f21a6a98f01d3ae9bf0173ee Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Wed, 11 Dec 2024 16:49:36 +0200 Subject: [PATCH 29/48] adds java dependency for macos and moves some tests to a separate FreeBSD configuration --- .../targets/android_sdk/handlers/main.yml | 2 +- .../android_sdk/tasks/default-tests.yml | 98 ++++++++++++++ .../android_sdk/tasks/freebsd-tests.yml | 78 ++++++++++++ .../targets/android_sdk/tasks/main.yml | 120 +++--------------- .../targets/android_sdk/vars/Darwin.yml | 6 + 5 files changed, 203 insertions(+), 101 deletions(-) create mode 100644 tests/integration/targets/android_sdk/tasks/default-tests.yml create mode 100644 tests/integration/targets/android_sdk/tasks/freebsd-tests.yml create mode 100644 tests/integration/targets/android_sdk/vars/Darwin.yml diff --git a/tests/integration/targets/android_sdk/handlers/main.yml b/tests/integration/targets/android_sdk/handlers/main.yml index 7be9b2436df..316f6839c77 100644 --- a/tests/integration/targets/android_sdk/handlers/main.yml +++ b/tests/integration/targets/android_sdk/handlers/main.yml @@ -5,5 +5,5 @@ - name: delete_newroot file: - path: "{{ newroot.path }}" + path: "{{ ansible_env.HOME }}/newroot" state: absent \ No newline at end of file diff --git a/tests/integration/targets/android_sdk/tasks/default-tests.yml b/tests/integration/targets/android_sdk/tasks/default-tests.yml new file mode 100644 index 00000000000..7eb8819d421 --- /dev/null +++ b/tests/integration/targets/android_sdk/tasks/default-tests.yml @@ -0,0 +1,98 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +- name: Accept all licenses + shell: 'yes | sdkmanager --licenses' + changed_when: false + +- name: Install build-tools;34.0.0 + android_sdk: + name: build-tools;34.0.0 + state: present + register: build_tools_installed + +- name: Install build-tools;34.0.0 second time + android_sdk: + name: build-tools;34.0.0 + state: present + register: build_tools_installed2 + +- name: Stat build-tools + stat: + path: "{{ android_sdk_location }}/build-tools/34.0.0" + register: build_tools_34_0_0 + +- name: Delete build-tools;34.0.0 + android_sdk: + name: build-tools;34.0.0 + state: absent + register: build_tools_deleted + +- name: Delete build-tools;34.0.0 second time + android_sdk: + name: build-tools;34.0.0 + state: absent + register: build_tools_deleted2 + +- name: Download old platform-tools + unarchive: + src: https://dl.google.com/android/repository/platform-tools_r27.0.0-linux.zip + remote_src: true + dest: "{{ android_sdk_location }}" + +- name: Try installing platform-tools from sdkmanager + android_sdk: + name: platform-tools + state: present + register: platform_tools_present + +- name: Install (update) platform-tools + android_sdk: + name: platform-tools + state: latest + register: platform_tools_updated + +- name: Create new sdk root + file: + path: "{{ ansible_env.HOME }}/newroot" + state: directory + register: newroot + notify: delete_newroot + +- name: Accept licenses in the new root + shell: "yes | sdkmanager --sdk_root={{ newroot.path }} --licenses" + +- name: Install a package to a new root + android_sdk: + name: build-tools;34.0.0 + state: present + sdk_root: "{{ newroot.path }}" + register: new_root_package + +- name: Check package is installed + stat: + path: "{{ newroot.path }}/build-tools/34.0.0" + register: new_root_package_stat + +- name: Install a package from canary channel + android_sdk: + name: build-tools;33.0.0 + state: present + channel: canary + register: package_canary + +- name: Run tests + assert: + that: + - build_tools_34_0_0.stat.exists + - build_tools_installed.changed + - not build_tools_installed2.changed + - build_tools_deleted.changed + - not build_tools_deleted2.changed + - not platform_tools_present.changed + - platform_tools_updated.changed + - new_root_package.changed + - new_root_package_stat.stat.exists + - package_canary.changed \ No newline at end of file diff --git a/tests/integration/targets/android_sdk/tasks/freebsd-tests.yml b/tests/integration/targets/android_sdk/tasks/freebsd-tests.yml new file mode 100644 index 00000000000..f64b004012f --- /dev/null +++ b/tests/integration/targets/android_sdk/tasks/freebsd-tests.yml @@ -0,0 +1,78 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +- name: Accept all licenses (FreeBSD) + shell: 'yes | sdkmanager --licenses' + changed_when: false + +- name: Install sources;android-26 (FreeBSD) + android_sdk: + name: sources;android-26 + state: present + register: sources_android_26_installed + +- name: Install sources;android-26 (FreeBSD) + android_sdk: + name: sources;android-26 + state: present + register: sources_android_26_installed2 + +- name: Stat build-tools (FreeBSD) + stat: + path: "{{ android_sdk_location }}/sources/android-26" + register: sources_android_26 + +- name: Delete sources;android-26 (FreeBSD) + android_sdk: + name: sources;android-26 + state: absent + register: sources_android_26_deleted + +- name: sources;android-26 second time (FreeBSD) + android_sdk: + name: sources;android-26 + state: absent + register: sources_android_26_deleted2 + +- name: Create new sdk root (FreeBSD) + file: + path: "{{ ansible_env.HOME }}/newroot" + state: directory + register: newroot + notify: delete_newroot + +- name: Accept licenses in the new root (FreeBSD) + shell: "yes | sdkmanager --sdk_root={{ newroot.path }} --licenses" + +- name: Install a package to a new root (FreeBSD) + android_sdk: + name: sources;android-26 + state: present + sdk_root: "{{ newroot.path }}" + register: new_root_package + +- name: Check package is installed (FreeBSD) + stat: + path: "{{ newroot.path }}/sources/android-26" + register: new_root_package_stat + +- name: Install a package from canary channel (FreeBSD) + android_sdk: + name: sources;android-26 + state: present + channel: canary + register: package_canary + +- name: Run tests (FreeBSD) + assert: + that: + - sources_android_26.stat.exists + - sources_android_26_installed.changed + - not sources_android_26_installed2.changed + - sources_android_26_deleted.changed + - not sources_android_26_deleted2.changed + - new_root_package.changed + - new_root_package_stat.stat.exists + - package_canary.changed diff --git a/tests/integration/targets/android_sdk/tasks/main.yml b/tests/integration/targets/android_sdk/tasks/main.yml index 2e00346d51e..c9c3789f2aa 100644 --- a/tests/integration/targets/android_sdk/tasks/main.yml +++ b/tests/integration/targets/android_sdk/tasks/main.yml @@ -3,104 +3,24 @@ # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later -- import_tasks: setup.yml - -- name: Setup environment - environment: - PATH: '{{ ansible_env.PATH }}:{{ android_sdk_location }}/cmdline-tools/latest/bin' - ANDROID_HOME: "{{ android_sdk_location }}" +- name: Run android_sdk tests block: - - - name: Accept all licenses - shell: 'yes | sdkmanager --licenses' - changed_when: false - - - name: Install build-tools;34.0.0 - android_sdk: - name: build-tools;34.0.0 - state: present - register: build_tools_installed - - - name: Install build-tools;34.0.0 second time - android_sdk: - name: build-tools;34.0.0 - state: present - register: build_tools_installed2 - - - name: Stat build-tools - stat: - path: "{{ android_sdk_location }}/build-tools/34.0.0" - register: build_tools_34_0_0 - - - name: Delete build-tools;34.0.0 - android_sdk: - name: build-tools;34.0.0 - state: absent - register: build_tools_deleted - - - name: Delete build-tools;34.0.0 second time - android_sdk: - name: build-tools;34.0.0 - state: absent - register: build_tools_deleted2 - - - name: Download old platform-tools - unarchive: - src: https://dl.google.com/android/repository/platform-tools_r27.0.0-linux.zip - remote_src: true - dest: "{{ android_sdk_location }}" - - - name: Try installing platform-tools from sdkmanager - android_sdk: - name: platform-tools - state: present - register: platform_tools_present - - - name: Install (update) platform-tools - android_sdk: - name: platform-tools - state: latest - register: platform_tools_updated - - - name: Create new sdk root - file: - path: "{{ ansible_env.HOME }}/newroot" - state: directory - register: newroot - notify: delete_newroot - - - name: Accept licenses in the new root - shell: "yes | sdkmanager --sdk_root={{ newroot.path }} --licenses" - - - name: Install a package to a new root - android_sdk: - name: build-tools;34.0.0 - state: present - sdk_root: "{{ newroot.path }}" - register: new_root_package - - - name: Check package is installed - stat: - path: "{{ newroot.path }}/build-tools/34.0.0" - register: new_root_package_stat - - - name: Install a package from canary channel - android_sdk: - name: build-tools;33.0.0 - state: present - channel: canary - register: package_canary - - - name: Run tests - assert: - that: - - build_tools_34_0_0.stat.exists - - build_tools_installed.changed - - not build_tools_installed2.changed - - build_tools_deleted.changed - - not build_tools_deleted2.changed - - not platform_tools_present.changed - - platform_tools_updated.changed - - new_root_package.changed - - new_root_package_stat.stat.exists - - package_canary.changed \ No newline at end of file + - import_tasks: setup.yml + + - name: Run default tests + environment: + PATH: '{{ ansible_env.PATH }}:{{ android_sdk_location }}/cmdline-tools/latest/bin' + block: + - import_tasks: default-tests.yml + when: ansible_os_family != 'FreeBSD' + + # Most of the important Android SDK packages are not available on FreeBSD (like, build-tools, platform-tools and so on), + # but at least some of the functionality can be tested (like, downloading sources) + - name: Run FreeBSD tests + environment: + PATH: '{{ ansible_env.PATH }}:{{ android_sdk_location }}/cmdline-tools/latest/bin' + block: + - import_tasks: freebsd-tests.yml + when: ansible_os_family == 'FreeBSD' + when: + - "not (ansible_os_family == 'RedHat' and ansible_distribution_version is version('8.0', '<'))" diff --git a/tests/integration/targets/android_sdk/vars/Darwin.yml b/tests/integration/targets/android_sdk/vars/Darwin.yml new file mode 100644 index 00000000000..696bf39a75b --- /dev/null +++ b/tests/integration/targets/android_sdk/vars/Darwin.yml @@ -0,0 +1,6 @@ +--- +# Copyright (c) Ansible Project +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +openjdk_pkg: openjdk@17 From 1ad135537ca6adbc5d2f47e3397a11adc0b02d35 Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Wed, 11 Dec 2024 17:46:11 +0200 Subject: [PATCH 30/48] fixes dependencies setup for OSX --- .../android_sdk/tasks/default-tests.yml | 5 ++++ .../android_sdk/tasks/freebsd-tests.yml | 5 ++++ .../targets/android_sdk/tasks/main.yml | 5 ++++ .../targets/android_sdk/tasks/setup.yml | 29 +++++++++++++++++++ 4 files changed, 44 insertions(+) diff --git a/tests/integration/targets/android_sdk/tasks/default-tests.yml b/tests/integration/targets/android_sdk/tasks/default-tests.yml index 7eb8819d421..61e31177a54 100644 --- a/tests/integration/targets/android_sdk/tasks/default-tests.yml +++ b/tests/integration/targets/android_sdk/tasks/default-tests.yml @@ -1,4 +1,9 @@ --- +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + # Copyright (c) Ansible Project # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later diff --git a/tests/integration/targets/android_sdk/tasks/freebsd-tests.yml b/tests/integration/targets/android_sdk/tasks/freebsd-tests.yml index f64b004012f..9fb1d066927 100644 --- a/tests/integration/targets/android_sdk/tasks/freebsd-tests.yml +++ b/tests/integration/targets/android_sdk/tasks/freebsd-tests.yml @@ -1,4 +1,9 @@ --- +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + # Copyright (c) Ansible Project # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later diff --git a/tests/integration/targets/android_sdk/tasks/main.yml b/tests/integration/targets/android_sdk/tasks/main.yml index c9c3789f2aa..4cb42e0cab1 100644 --- a/tests/integration/targets/android_sdk/tasks/main.yml +++ b/tests/integration/targets/android_sdk/tasks/main.yml @@ -1,4 +1,9 @@ --- +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + # Copyright (c) Ansible Project # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later diff --git a/tests/integration/targets/android_sdk/tasks/setup.yml b/tests/integration/targets/android_sdk/tasks/setup.yml index 066af3abee0..a12ed90ce5c 100644 --- a/tests/integration/targets/android_sdk/tasks/setup.yml +++ b/tests/integration/targets/android_sdk/tasks/setup.yml @@ -1,4 +1,9 @@ --- +#################################################################### +# WARNING: These are designed specifically for Ansible tests # +# and should not be used as examples of how to write Ansible roles # +#################################################################### + # Copyright (c) Ansible Project # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later @@ -13,6 +18,30 @@ - "{{ openjdk_pkg }}" - unzip state: present + when: ansible_os_family != 'Darwin' + +- name: Install dependencies (OSX) + block: + - name: Find brew binary + command: which brew + register: brew_which + - name: Get owner of brew binary + stat: + path: "{{ brew_which.stdout }}" + register: brew_stat + - name: "Install package" + homebrew: + name: + - "{{ openjdk_pkg }}" + - unzip + state: present + update_homebrew: false + become: true + become_user: "{{ brew_stat.stat.pw_name }}" + environment: + HOMEBREW_NO_AUTO_UPDATE: "True" + when: + - ansible_os_family == 'Darwin' - name: Create Android SDK directory file: From eb10db3c9ea91f0deb1ef2ebc5f85d1edb30c69b Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Wed, 11 Dec 2024 20:32:42 +0200 Subject: [PATCH 31/48] fixes dependencies setup for OSX (2) --- tests/integration/targets/android_sdk/tasks/setup.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/integration/targets/android_sdk/tasks/setup.yml b/tests/integration/targets/android_sdk/tasks/setup.yml index a12ed90ce5c..6d981965dcb 100644 --- a/tests/integration/targets/android_sdk/tasks/setup.yml +++ b/tests/integration/targets/android_sdk/tasks/setup.yml @@ -40,6 +40,12 @@ become_user: "{{ brew_stat.stat.pw_name }}" environment: HOMEBREW_NO_AUTO_UPDATE: "True" + - name: Symlink java + become: true + file: + src: "$HOMEBREW_PREFIX/opt/openjdk@17/libexec/openjdk.jdk" + dest: "/Library/Java/JavaVirtualMachines/openjdk-17.jdk" + state: link when: - ansible_os_family == 'Darwin' From 7e1af46af648f66a4ffa4894879be470a2178901 Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Wed, 11 Dec 2024 20:44:07 +0200 Subject: [PATCH 32/48] fixes dependencies setup for OSX (3) --- tests/integration/targets/android_sdk/tasks/setup.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/targets/android_sdk/tasks/setup.yml b/tests/integration/targets/android_sdk/tasks/setup.yml index 6d981965dcb..ff2e3eb3cfe 100644 --- a/tests/integration/targets/android_sdk/tasks/setup.yml +++ b/tests/integration/targets/android_sdk/tasks/setup.yml @@ -43,7 +43,7 @@ - name: Symlink java become: true file: - src: "$HOMEBREW_PREFIX/opt/openjdk@17/libexec/openjdk.jdk" + src: "/usr/local/opt/openjdk@17/libexec/openjdk.jdk" dest: "/Library/Java/JavaVirtualMachines/openjdk-17.jdk" state: link when: From baa5f6532812f30727457609c460f1a9147f1e0c Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Thu, 12 Dec 2024 13:51:12 +0200 Subject: [PATCH 33/48] Apply minor suggestions from code review Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com> --- plugins/module_utils/sdkmanager.py | 2 +- plugins/modules/android_sdk.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/module_utils/sdkmanager.py b/plugins/module_utils/sdkmanager.py index 8db9b5c133b..4666156fd2d 100644 --- a/plugins/module_utils/sdkmanager.py +++ b/plugins/module_utils/sdkmanager.py @@ -43,7 +43,7 @@ def sdkmanager_runner(module, **kwargs): installed=cmd_runner_fmt.as_fixed("--list_installed"), list=cmd_runner_fmt.as_fixed('--list'), newer=cmd_runner_fmt.as_fixed("--newer"), - sdk_root=cmd_runner_fmt.as_opt_eq_val("--sdk_root", ignore_none=True), + sdk_root=cmd_runner_fmt.as_opt_eq_val("--sdk_root"), channel=cmd_runner_fmt.as_func(lambda x: ["{0}={1}".format("--channel", __map_channel(x))]) ), force_lang="C.UTF-8", # Without this, sdkmanager binary crashes diff --git a/plugins/modules/android_sdk.py b/plugins/modules/android_sdk.py index d704526355b..4d7bab0ea84 100644 --- a/plugins/modules/android_sdk.py +++ b/plugins/modules/android_sdk.py @@ -25,6 +25,7 @@ support: full diff_mode: support: none +version_added: 10.2.0 options: name: description: From d367bd2b196aef2b9b794d75ab91b482c008f16b Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Thu, 12 Dec 2024 15:34:05 +0200 Subject: [PATCH 34/48] applies code review suggestions --- plugins/module_utils/sdkmanager.py | 9 ++++----- plugins/modules/android_sdk.py | 11 +++-------- .../targets/android_sdk/handlers/main.yml | 9 --------- tests/integration/targets/android_sdk/meta/main.yml | 1 + .../targets/android_sdk/tasks/default-tests.yml | 13 +++---------- .../targets/android_sdk/tasks/freebsd-tests.yml | 13 +++---------- 6 files changed, 14 insertions(+), 42 deletions(-) delete mode 100644 tests/integration/targets/android_sdk/handlers/main.yml diff --git a/plugins/module_utils/sdkmanager.py b/plugins/module_utils/sdkmanager.py index 4666156fd2d..6cb63e4b299 100644 --- a/plugins/module_utils/sdkmanager.py +++ b/plugins/module_utils/sdkmanager.py @@ -10,8 +10,7 @@ import re -from ansible_collections.community.general.plugins.module_utils import cmd_runner_fmt -from ansible_collections.community.general.plugins.module_utils.cmd_runner import CmdRunner +from ansible_collections.community.general.plugins.module_utils.cmd_runner import CmdRunner, cmd_runner_fmt __state_map = { "present": "--install", @@ -113,7 +112,7 @@ def apply_packages_changes(self, packages): with self.runner('state name sdk_root channel', data=data) as ctx: rc, stdout, stderr = ctx.run(name=command_arg, data=data) - data = stdout.split('\n') + data = stdout.splitlines() for line in data: if self._RE_ACCEPT_LICENSE.match(line): @@ -124,7 +123,7 @@ def apply_packages_changes(self, packages): return rc, stdout, stderr def _try_parse_stderr(self, stderr): - data = stderr.split('\n') + data = stderr.splitlines() for line in data: unknown_package_regex = self._RE_UNKNOWN_PACKAGE.match(line) if unknown_package_regex: @@ -133,7 +132,7 @@ def _try_parse_stderr(self, stderr): @staticmethod def _parse_packages(stdout, header_regexp, row_regexp): - data = stdout.split('\n') + data = stdout.splitlines() updatable_section_found = False i = 0 diff --git a/plugins/modules/android_sdk.py b/plugins/modules/android_sdk.py index 4d7bab0ea84..6fa6878cdf2 100644 --- a/plugins/modules/android_sdk.py +++ b/plugins/modules/android_sdk.py @@ -145,15 +145,10 @@ def __init_module__(self): self.vars.set('removed', [], change=True) def _parse_packages(self): - arg_pkgs = self.vars.package - packages = set() - for arg_pkg in arg_pkgs: - package = Package(arg_pkg) - packages.add(package) - - if len(packages) < len(arg_pkgs): + arg_pkgs = set(self.vars.package) + if len(arg_pkgs) < len(self.vars.package): self.do_raise("Packages may not repeat") - return packages + return set([Package(p) for p in arg_pkgs]) def state_present(self): packages = self._parse_packages() diff --git a/tests/integration/targets/android_sdk/handlers/main.yml b/tests/integration/targets/android_sdk/handlers/main.yml deleted file mode 100644 index 316f6839c77..00000000000 --- a/tests/integration/targets/android_sdk/handlers/main.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -# Copyright (c) Ansible Project -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later - -- name: delete_newroot - file: - path: "{{ ansible_env.HOME }}/newroot" - state: absent \ No newline at end of file diff --git a/tests/integration/targets/android_sdk/meta/main.yml b/tests/integration/targets/android_sdk/meta/main.yml index 2fcd152f95c..d7c152feebd 100644 --- a/tests/integration/targets/android_sdk/meta/main.yml +++ b/tests/integration/targets/android_sdk/meta/main.yml @@ -5,3 +5,4 @@ dependencies: - setup_pkg_mgr + - setup_remote_tmp_dir \ No newline at end of file diff --git a/tests/integration/targets/android_sdk/tasks/default-tests.yml b/tests/integration/targets/android_sdk/tasks/default-tests.yml index 61e31177a54..1cb92b13d8e 100644 --- a/tests/integration/targets/android_sdk/tasks/default-tests.yml +++ b/tests/integration/targets/android_sdk/tasks/default-tests.yml @@ -59,26 +59,19 @@ state: latest register: platform_tools_updated -- name: Create new sdk root - file: - path: "{{ ansible_env.HOME }}/newroot" - state: directory - register: newroot - notify: delete_newroot - - name: Accept licenses in the new root - shell: "yes | sdkmanager --sdk_root={{ newroot.path }} --licenses" + shell: "yes | sdkmanager --sdk_root={{ remote_tmp_dir }} --licenses" - name: Install a package to a new root android_sdk: name: build-tools;34.0.0 state: present - sdk_root: "{{ newroot.path }}" + sdk_root: "{{ remote_tmp_dir }}" register: new_root_package - name: Check package is installed stat: - path: "{{ newroot.path }}/build-tools/34.0.0" + path: "{{ remote_tmp_dir }}/build-tools/34.0.0" register: new_root_package_stat - name: Install a package from canary channel diff --git a/tests/integration/targets/android_sdk/tasks/freebsd-tests.yml b/tests/integration/targets/android_sdk/tasks/freebsd-tests.yml index 9fb1d066927..d35a5e94a85 100644 --- a/tests/integration/targets/android_sdk/tasks/freebsd-tests.yml +++ b/tests/integration/targets/android_sdk/tasks/freebsd-tests.yml @@ -41,26 +41,19 @@ state: absent register: sources_android_26_deleted2 -- name: Create new sdk root (FreeBSD) - file: - path: "{{ ansible_env.HOME }}/newroot" - state: directory - register: newroot - notify: delete_newroot - - name: Accept licenses in the new root (FreeBSD) - shell: "yes | sdkmanager --sdk_root={{ newroot.path }} --licenses" + shell: "yes | sdkmanager --sdk_root={{ remote_tmp_dir }} --licenses" - name: Install a package to a new root (FreeBSD) android_sdk: name: sources;android-26 state: present - sdk_root: "{{ newroot.path }}" + sdk_root: "{{ remote_tmp_dir }}" register: new_root_package - name: Check package is installed (FreeBSD) stat: - path: "{{ newroot.path }}/sources/android-26" + path: "{{ remote_tmp_dir }}/sources/android-26" register: new_root_package_stat - name: Install a package from canary channel (FreeBSD) From 619f28dd58db005e466a19b98604221da82b7ecc Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Thu, 12 Dec 2024 22:22:08 +0200 Subject: [PATCH 35/48] changes force_lang from C.UTF-8 to auto in sdkmanager (as per discussion https://github.com/ansible-collections/community.general/pull/9236#discussion_r1881114326) --- plugins/module_utils/sdkmanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/module_utils/sdkmanager.py b/plugins/module_utils/sdkmanager.py index 6cb63e4b299..e2e3f81501f 100644 --- a/plugins/module_utils/sdkmanager.py +++ b/plugins/module_utils/sdkmanager.py @@ -45,7 +45,7 @@ def sdkmanager_runner(module, **kwargs): sdk_root=cmd_runner_fmt.as_opt_eq_val("--sdk_root"), channel=cmd_runner_fmt.as_func(lambda x: ["{0}={1}".format("--channel", __map_channel(x))]) ), - force_lang="C.UTF-8", # Without this, sdkmanager binary crashes + force_lang="auto", # Without this, sdkmanager binary crashes **kwargs ) From 751ec9ed936bb774c5c9572d0e2733962a66e9e9 Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Thu, 12 Dec 2024 23:11:19 +0200 Subject: [PATCH 36/48] Revert "changes force_lang from C.UTF-8 to auto in sdkmanager (as per discussion https://github.com/ansible-collections/community.general/pull/9236#discussion_r1881114326)" This reverts commit 619f28dd58db005e466a19b98604221da82b7ecc. --- plugins/module_utils/sdkmanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/module_utils/sdkmanager.py b/plugins/module_utils/sdkmanager.py index e2e3f81501f..6cb63e4b299 100644 --- a/plugins/module_utils/sdkmanager.py +++ b/plugins/module_utils/sdkmanager.py @@ -45,7 +45,7 @@ def sdkmanager_runner(module, **kwargs): sdk_root=cmd_runner_fmt.as_opt_eq_val("--sdk_root"), channel=cmd_runner_fmt.as_func(lambda x: ["{0}={1}".format("--channel", __map_channel(x))]) ), - force_lang="auto", # Without this, sdkmanager binary crashes + force_lang="C.UTF-8", # Without this, sdkmanager binary crashes **kwargs ) From 2089a5b3b8913d4c79d1e4fede326e117d9c52a2 Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Thu, 12 Dec 2024 23:25:46 +0200 Subject: [PATCH 37/48] fixes some more comments from review --- plugins/module_utils/sdkmanager.py | 4 +--- .../targets/android_sdk/tasks/main.yml | 20 +++++++++---------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/plugins/module_utils/sdkmanager.py b/plugins/module_utils/sdkmanager.py index 6cb63e4b299..ee5637a28a5 100644 --- a/plugins/module_utils/sdkmanager.py +++ b/plugins/module_utils/sdkmanager.py @@ -112,9 +112,7 @@ def apply_packages_changes(self, packages): with self.runner('state name sdk_root channel', data=data) as ctx: rc, stdout, stderr = ctx.run(name=command_arg, data=data) - data = stdout.splitlines() - - for line in data: + for line in stdout.splitlines(): if self._RE_ACCEPT_LICENSE.match(line): raise SdkManagerException("Licenses for some packages were not accepted") diff --git a/tests/integration/targets/android_sdk/tasks/main.yml b/tests/integration/targets/android_sdk/tasks/main.yml index 4cb42e0cab1..58937892d78 100644 --- a/tests/integration/targets/android_sdk/tasks/main.yml +++ b/tests/integration/targets/android_sdk/tasks/main.yml @@ -8,24 +8,24 @@ # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later +# java >= 17 is not available in RHEL and CentOS7 repos, which is required for sdkmanager to run +- name: Bail out if not supported + when: + - "ansible_os_family == 'RedHat' and ansible_distribution_version is version('8.0', '<')" + ansible.builtin.meta: end_play + - name: Run android_sdk tests + environment: + PATH: '{{ ansible_env.PATH }}:{{ android_sdk_location }}/cmdline-tools/latest/bin' block: - import_tasks: setup.yml - name: Run default tests - environment: - PATH: '{{ ansible_env.PATH }}:{{ android_sdk_location }}/cmdline-tools/latest/bin' - block: - - import_tasks: default-tests.yml + import_tasks: default-tests.yml when: ansible_os_family != 'FreeBSD' # Most of the important Android SDK packages are not available on FreeBSD (like, build-tools, platform-tools and so on), # but at least some of the functionality can be tested (like, downloading sources) - name: Run FreeBSD tests - environment: - PATH: '{{ ansible_env.PATH }}:{{ android_sdk_location }}/cmdline-tools/latest/bin' - block: - - import_tasks: freebsd-tests.yml + import_tasks: freebsd-tests.yml when: ansible_os_family == 'FreeBSD' - when: - - "not (ansible_os_family == 'RedHat' and ansible_distribution_version is version('8.0', '<'))" From 80080624523cadda0e536696cf2a2d6b8943e98b Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Thu, 12 Dec 2024 23:28:37 +0200 Subject: [PATCH 38/48] minor sanity issue fix --- tests/integration/targets/android_sdk/tasks/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/targets/android_sdk/tasks/main.yml b/tests/integration/targets/android_sdk/tasks/main.yml index 58937892d78..46cf3192e18 100644 --- a/tests/integration/targets/android_sdk/tasks/main.yml +++ b/tests/integration/targets/android_sdk/tasks/main.yml @@ -11,7 +11,7 @@ # java >= 17 is not available in RHEL and CentOS7 repos, which is required for sdkmanager to run - name: Bail out if not supported when: - - "ansible_os_family == 'RedHat' and ansible_distribution_version is version('8.0', '<')" + - "ansible_os_family == 'RedHat' and ansible_distribution_version is version('8.0', '<')" ansible.builtin.meta: end_play - name: Run android_sdk tests From 165f04e8c9653e6ff9cd32dc2d1977ab946a00db Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Mon, 16 Dec 2024 14:15:10 +0200 Subject: [PATCH 39/48] uses the 'changed' test instead of checking the 'changed' attribute --- .../targets/android_sdk/tasks/default-tests.yml | 16 ++++++++-------- .../targets/android_sdk/tasks/freebsd-tests.yml | 12 ++++++------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/integration/targets/android_sdk/tasks/default-tests.yml b/tests/integration/targets/android_sdk/tasks/default-tests.yml index 1cb92b13d8e..3123f6b8c23 100644 --- a/tests/integration/targets/android_sdk/tasks/default-tests.yml +++ b/tests/integration/targets/android_sdk/tasks/default-tests.yml @@ -85,12 +85,12 @@ assert: that: - build_tools_34_0_0.stat.exists - - build_tools_installed.changed - - not build_tools_installed2.changed - - build_tools_deleted.changed - - not build_tools_deleted2.changed - - not platform_tools_present.changed - - platform_tools_updated.changed - - new_root_package.changed + - build_tools_installed is changed + - build_tools_installed2 is not changed + - build_tools_deleted is changed + - build_tools_deleted2 is not changed + - platform_tools_present is not changed + - platform_tools_updated is changed + - new_root_package is changed - new_root_package_stat.stat.exists - - package_canary.changed \ No newline at end of file + - package_canary is changed \ No newline at end of file diff --git a/tests/integration/targets/android_sdk/tasks/freebsd-tests.yml b/tests/integration/targets/android_sdk/tasks/freebsd-tests.yml index d35a5e94a85..532e3a16369 100644 --- a/tests/integration/targets/android_sdk/tasks/freebsd-tests.yml +++ b/tests/integration/targets/android_sdk/tasks/freebsd-tests.yml @@ -67,10 +67,10 @@ assert: that: - sources_android_26.stat.exists - - sources_android_26_installed.changed - - not sources_android_26_installed2.changed - - sources_android_26_deleted.changed - - not sources_android_26_deleted2.changed - - new_root_package.changed + - sources_android_26_installed is changed + - sources_android_26_installed2 is not changed + - sources_android_26_deleted is changed + - sources_android_26_deleted2 is not changed + - new_root_package is changed - new_root_package_stat.stat.exists - - package_canary.changed + - package_canary is changed From c5474f20625600d6c11dcb17e9965f045e00b28f Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Mon, 16 Dec 2024 16:24:03 +0200 Subject: [PATCH 40/48] adds 'accept_licenses' parameter. Installation is now performed independently for each package specified. --- plugins/module_utils/sdkmanager.py | 30 +++++++++------- plugins/modules/android_sdk.py | 36 +++++++++++++++---- .../android_sdk/tasks/default-tests.yml | 10 ++---- .../android_sdk/tasks/freebsd-tests.yml | 12 +++---- 4 files changed, 53 insertions(+), 35 deletions(-) diff --git a/plugins/module_utils/sdkmanager.py b/plugins/module_utils/sdkmanager.py index ee5637a28a5..8ea82dfbb63 100644 --- a/plugins/module_utils/sdkmanager.py +++ b/plugins/module_utils/sdkmanager.py @@ -102,23 +102,27 @@ def get_updatable_packages(self): rc, stdout, stderr = ctx.run() return self._parse_packages(stdout, self._RE_UPDATABLE_PACKAGES_HEADER, self._RE_UPDATABLE_PACKAGE) - def apply_packages_changes(self, packages): + def apply_packages_changes(self, packages, accept_licenses=False): """ Install or delete packages, depending on the `module.vars.state` parameter """ if len(packages) == 0: return 0, '', '' - command_arg = [x.name for x in packages] - data = 'N' # Answer 'No' in case sdkmanager wants us to accept license - with self.runner('state name sdk_root channel', data=data) as ctx: - rc, stdout, stderr = ctx.run(name=command_arg, data=data) - - for line in stdout.splitlines(): - if self._RE_ACCEPT_LICENSE.match(line): - raise SdkManagerException("Licenses for some packages were not accepted") - - if rc != 0: - self._try_parse_stderr(stderr) - return rc, stdout, stderr + if accept_licenses: + license_prompt_answer = 'y' + else: + license_prompt_answer = 'N' + for package in packages: + with self.runner('state name sdk_root channel', data=license_prompt_answer) as ctx: + rc, stdout, stderr = ctx.run(name=package.name) + + for line in stdout.splitlines(): + if self._RE_ACCEPT_LICENSE.match(line): + raise SdkManagerException("Licenses for some packages were not accepted") + + if rc != 0: + self._try_parse_stderr(stderr) + return rc, stdout, stderr + return 0, '', '' def _try_parse_stderr(self, stderr): data = stderr.splitlines() diff --git a/plugins/modules/android_sdk.py b/plugins/modules/android_sdk.py index 6fa6878cdf2..bec4de13be9 100644 --- a/plugins/modules/android_sdk.py +++ b/plugins/modules/android_sdk.py @@ -27,6 +27,12 @@ support: none version_added: 10.2.0 options: + accept_licenses: + description: + - If this is set to B(true), the module will try to accept license prompts generated by C(sdkmanager) during + package installation. Otherwise, every license prompt will be rejected. + type: bool + default: false name: description: - A name of an Android SDK package (for instance, V(build-tools;34.0.0)). @@ -57,10 +63,20 @@ - C(java) >= 17 - C(sdkmanager) Command line tool for installing Android SDK packages. notes: - - It is assumed that all necessary licenses for the packages that are to be installed are accepted before using - this module. If there are any unaccepted licenses, the module will fail. In order to accept all licenses that - have not been already accepted, one may use the C(sdkmanager --licenses) command - (see U(https://developer.android.com/tools/sdkmanager#accept-licenses)). + - For some of the packages installed by C(sdkmanager) is it necessary to accept licenses. Usually it is done through + command line prompt in a form of a Y/N question when a licensed package is requested to be installed. If there are + several packages requested for installation and at least two of them belong to different licenses, the C(sdkmanager) + tool will prompt for these licenses in a loop. + In order to install packages, the module must be able to answer these license prompts. Currently, it is only + possible to answer one license prompt at a time, meaning that instead of installing multiple packages as a single + invocation of the C(sdkmanager --install) command, it will be done by executing the command independently for each + package. This makes sure that at most only one license prompt will need to be answered. + At the time of writing this module, a C(sdkmanager)'s package may belong to at most one license type that needs to + be accepted. However, if this is changes in the future, the module may hang as there might be more prompts generated + by the C(sdkmanager) tool which the module won't be able to answer. If this is the case, file an issue and in the + meantime, consider accepting all the licenses in advance, as it is described in the C(sdkmanager) + L(documentation,https://developer.android.com/tools/sdkmanager#accept-licenses), for instance, using the + L(ansible.builtin.command module,https://docs.ansible.com/ansible/latest/collections/ansible/builtin/command_module.html) seealso: - name: sdkmanager tool documentation description: Detailed information of how to install and use sdkmanager command line tool. @@ -74,6 +90,7 @@ - name: Install build-tools;34.0.0 community.general.android_sdk: name: build-tools;34.0.0 + accept_licenses: true state: present - name: Install build-tools;34.0.0 and platform-tools @@ -81,6 +98,7 @@ name: - build-tools;34.0.0 - platform-tools + accept_licenses: true state: present - name: Delete build-tools;34.0.0 @@ -91,17 +109,20 @@ - name: Install platform-tools or update if installed community.general.android_sdk: name: platform-tools + accept_licenses: true state: latest - name: Install build-tools;34.0.0 to a different SDK root community.general.android_sdk: name: build-tools;34.0.0 + accept_licenses: true state: present sdk_root: "/path/to/new/root" - name: Install a package from another channel community.general.android_sdk: name: some-package-present-in-canary-channel + accept_licenses: true state: present channel: canary ''' @@ -131,7 +152,8 @@ class AndroidSdk(StateModuleHelper): state=dict(type='str', default='present', choices=['present', 'absent', 'latest']), package=dict(type='list', elements='str', aliases=['pkg', 'name']), sdk_root=dict(type='path'), - channel=dict(type='str', default='stable', choices=['stable', 'beta', 'dev', 'canary']) + channel=dict(type='str', default='stable', choices=['stable', 'beta', 'dev', 'canary']), + accept_licenses=dict(type='bool', default=False) ), supports_check_mode=True ) @@ -157,7 +179,7 @@ def state_present(self): self.vars.installed = AndroidSdk._map_packages_to_names(pending_installation) if not self.check_mode: - rc, stdout, stderr = self.sdkmanager.apply_packages_changes(pending_installation) + rc, stdout, stderr = self.sdkmanager.apply_packages_changes(pending_installation, self.vars.accept_licenses) if rc != 0: self.do_raise("Could not install packages: %s" % stderr) @@ -180,7 +202,7 @@ def state_latest(self): self.vars.installed = AndroidSdk._map_packages_to_names(to_be_installed) if not self.check_mode: - rc, stdout, stderr = self.sdkmanager.apply_packages_changes(to_be_installed) + rc, stdout, stderr = self.sdkmanager.apply_packages_changes(to_be_installed, self.vars.accept_licenses) if rc != 0: self.do_raise("Could not install packages: %s" % stderr) diff --git a/tests/integration/targets/android_sdk/tasks/default-tests.yml b/tests/integration/targets/android_sdk/tasks/default-tests.yml index 3123f6b8c23..b8cb6df54df 100644 --- a/tests/integration/targets/android_sdk/tasks/default-tests.yml +++ b/tests/integration/targets/android_sdk/tasks/default-tests.yml @@ -8,12 +8,9 @@ # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later -- name: Accept all licenses - shell: 'yes | sdkmanager --licenses' - changed_when: false - - name: Install build-tools;34.0.0 android_sdk: + accept_licenses: true name: build-tools;34.0.0 state: present register: build_tools_installed @@ -50,6 +47,7 @@ - name: Try installing platform-tools from sdkmanager android_sdk: name: platform-tools + accept_licenses: true state: present register: platform_tools_present @@ -59,12 +57,10 @@ state: latest register: platform_tools_updated -- name: Accept licenses in the new root - shell: "yes | sdkmanager --sdk_root={{ remote_tmp_dir }} --licenses" - - name: Install a package to a new root android_sdk: name: build-tools;34.0.0 + accept_licenses: true state: present sdk_root: "{{ remote_tmp_dir }}" register: new_root_package diff --git a/tests/integration/targets/android_sdk/tasks/freebsd-tests.yml b/tests/integration/targets/android_sdk/tasks/freebsd-tests.yml index 532e3a16369..f1886f245d0 100644 --- a/tests/integration/targets/android_sdk/tasks/freebsd-tests.yml +++ b/tests/integration/targets/android_sdk/tasks/freebsd-tests.yml @@ -8,13 +8,10 @@ # GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) # SPDX-License-Identifier: GPL-3.0-or-later -- name: Accept all licenses (FreeBSD) - shell: 'yes | sdkmanager --licenses' - changed_when: false - - name: Install sources;android-26 (FreeBSD) android_sdk: name: sources;android-26 + accept_licenses: true state: present register: sources_android_26_installed @@ -35,18 +32,16 @@ state: absent register: sources_android_26_deleted -- name: sources;android-26 second time (FreeBSD) +- name: Delete sources;android-26 second time (FreeBSD) android_sdk: name: sources;android-26 state: absent register: sources_android_26_deleted2 -- name: Accept licenses in the new root (FreeBSD) - shell: "yes | sdkmanager --sdk_root={{ remote_tmp_dir }} --licenses" - - name: Install a package to a new root (FreeBSD) android_sdk: name: sources;android-26 + accept_licenses: true state: present sdk_root: "{{ remote_tmp_dir }}" register: new_root_package @@ -59,6 +54,7 @@ - name: Install a package from canary channel (FreeBSD) android_sdk: name: sources;android-26 + accept_licenses: true state: present channel: canary register: package_canary From 83dd52e32fae3b1aa701ba9eea8bc8e9dd17ae63 Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Mon, 16 Dec 2024 16:24:32 +0200 Subject: [PATCH 41/48] removes "Accept licenses" task from examples --- plugins/modules/android_sdk.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugins/modules/android_sdk.py b/plugins/modules/android_sdk.py index bec4de13be9..1915cb39b14 100644 --- a/plugins/modules/android_sdk.py +++ b/plugins/modules/android_sdk.py @@ -84,9 +84,6 @@ ''' EXAMPLES = r''' -- name: Before installing Android SDK packages, all licenses must be accepted - shell: "yes | sdkmanager --licenses" - - name: Install build-tools;34.0.0 community.general.android_sdk: name: build-tools;34.0.0 From ffd170d0bb7e4fca2f136bfb91824fe611725c6a Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Mon, 16 Dec 2024 16:27:29 +0200 Subject: [PATCH 42/48] fixes docs sanity issues --- plugins/modules/android_sdk.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/modules/android_sdk.py b/plugins/modules/android_sdk.py index 1915cb39b14..18c69fae492 100644 --- a/plugins/modules/android_sdk.py +++ b/plugins/modules/android_sdk.py @@ -66,16 +66,16 @@ - For some of the packages installed by C(sdkmanager) is it necessary to accept licenses. Usually it is done through command line prompt in a form of a Y/N question when a licensed package is requested to be installed. If there are several packages requested for installation and at least two of them belong to different licenses, the C(sdkmanager) - tool will prompt for these licenses in a loop. - In order to install packages, the module must be able to answer these license prompts. Currently, it is only - possible to answer one license prompt at a time, meaning that instead of installing multiple packages as a single - invocation of the C(sdkmanager --install) command, it will be done by executing the command independently for each + tool will prompt for these licenses in a loop. + In order to install packages, the module must be able to answer these license prompts. Currently, it is only + possible to answer one license prompt at a time, meaning that instead of installing multiple packages as a single + invocation of the C(sdkmanager --install) command, it will be done by executing the command independently for each package. This makes sure that at most only one license prompt will need to be answered. - At the time of writing this module, a C(sdkmanager)'s package may belong to at most one license type that needs to + At the time of writing this module, a C(sdkmanager)'s package may belong to at most one license type that needs to be accepted. However, if this is changes in the future, the module may hang as there might be more prompts generated by the C(sdkmanager) tool which the module won't be able to answer. If this is the case, file an issue and in the - meantime, consider accepting all the licenses in advance, as it is described in the C(sdkmanager) - L(documentation,https://developer.android.com/tools/sdkmanager#accept-licenses), for instance, using the + meantime, consider accepting all the licenses in advance, as it is described in the C(sdkmanager) + L(documentation,https://developer.android.com/tools/sdkmanager#accept-licenses), for instance, using the L(ansible.builtin.command module,https://docs.ansible.com/ansible/latest/collections/ansible/builtin/command_module.html) seealso: - name: sdkmanager tool documentation From bf10fe043edaa0c18bf05f2a6af5ded14c95a318 Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Tue, 17 Dec 2024 00:46:31 +0200 Subject: [PATCH 43/48] applies minor suggestions from code review --- plugins/module_utils/sdkmanager.py | 8 +++----- plugins/modules/android_sdk.py | 7 ++----- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/plugins/module_utils/sdkmanager.py b/plugins/module_utils/sdkmanager.py index 8ea82dfbb63..0862d535e1e 100644 --- a/plugins/module_utils/sdkmanager.py +++ b/plugins/module_utils/sdkmanager.py @@ -78,9 +78,7 @@ class AndroidSdkManager(object): _RE_UPDATABLE_PACKAGES_HEADER = re.compile(r'^Available Updates:$') # Example: ' platform-tools | 27.0.0 | Android SDK Platform-Tools 27 | platform-tools ' - _RE_INSTALLED_PACKAGE = ( - re.compile(r'^\s*(?P\S+)\s*\|\s*\S+\s*\|\s*.+\s*\|\s*(\S+)\s*$') - ) + _RE_INSTALLED_PACKAGE = re.compile(r'^\s*(?P\S+)\s*\|\s*\S+\s*\|\s*.+\s*\|\s*(\S+)\s*$') # Example: ' platform-tools | 27.0.0 | 35.0.2' _RE_UPDATABLE_PACKAGE = re.compile(r'^\s*(?P\S+)\s*\|\s*\S+\s*\|\s*\S+\s*$') @@ -89,8 +87,8 @@ class AndroidSdkManager(object): _RE_ACCEPT_LICENSE = re.compile(r'^The following packages can not be installed since their licenses or those of ' r'the packages they depend on were not accepted') - def __init__(self, runner): - self.runner = runner + def __init__(self, module): + self.runner = sdkmanager_runner(module) def get_installed_packages(self): with self.runner('installed sdk_root channel') as ctx: diff --git a/plugins/modules/android_sdk.py b/plugins/modules/android_sdk.py index 18c69fae492..898473da66e 100644 --- a/plugins/modules/android_sdk.py +++ b/plugins/modules/android_sdk.py @@ -139,8 +139,7 @@ ''' from ansible_collections.community.general.plugins.module_utils.mh.module_helper import StateModuleHelper -from ansible_collections.community.general.plugins.module_utils.sdkmanager import sdkmanager_runner, Package, \ - AndroidSdkManager +from ansible_collections.community.general.plugins.module_utils.sdkmanager import Package, AndroidSdkManager class AndroidSdk(StateModuleHelper): @@ -155,11 +154,9 @@ class AndroidSdk(StateModuleHelper): supports_check_mode=True ) use_old_vardict = False - output_params = ('installed', 'removed') - change_params = ('installed', 'removed') def __init_module__(self): - self.sdkmanager = AndroidSdkManager(sdkmanager_runner(self.module)) + self.sdkmanager = AndroidSdkManager(self.module) self.vars.set('installed', [], change=True) self.vars.set('removed', [], change=True) From 1878cd0f706f8a6ce860fb76f13d0bc761546512 Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Tue, 17 Dec 2024 14:34:50 +0200 Subject: [PATCH 44/48] fixes regexps. The previous version didn't match versions like "32.1.0 rc1". Also, this allows to simplify the parsing logic as there is no need to skip table headers anymore. --- plugins/module_utils/sdkmanager.py | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/plugins/module_utils/sdkmanager.py b/plugins/module_utils/sdkmanager.py index 0862d535e1e..9cbb2df6b09 100644 --- a/plugins/module_utils/sdkmanager.py +++ b/plugins/module_utils/sdkmanager.py @@ -78,10 +78,10 @@ class AndroidSdkManager(object): _RE_UPDATABLE_PACKAGES_HEADER = re.compile(r'^Available Updates:$') # Example: ' platform-tools | 27.0.0 | Android SDK Platform-Tools 27 | platform-tools ' - _RE_INSTALLED_PACKAGE = re.compile(r'^\s*(?P\S+)\s*\|\s*\S+\s*\|\s*.+\s*\|\s*(\S+)\s*$') + _RE_INSTALLED_PACKAGE = re.compile(r'^\s*(?P\S+)\s*\|\s*[0-9][^|]*\b\s*\|\s*.+\s*\|\s*(\S+)\s*$') # Example: ' platform-tools | 27.0.0 | 35.0.2' - _RE_UPDATABLE_PACKAGE = re.compile(r'^\s*(?P\S+)\s*\|\s*\S+\s*\|\s*\S+\s*$') + _RE_UPDATABLE_PACKAGE = re.compile(r'^\s*(?P\S+)\s*\|\s*[0-9][^|]*\b\s*\|\s*[0-9].*\b\s*$') _RE_UNKNOWN_PACKAGE = re.compile(r'^Warning: Failed to find package \'(?P\S+)\'\s*$') _RE_ACCEPT_LICENSE = re.compile(r'^The following packages can not be installed since their licenses or those of ' @@ -134,28 +134,15 @@ def _try_parse_stderr(self, stderr): def _parse_packages(stdout, header_regexp, row_regexp): data = stdout.splitlines() - updatable_section_found = False - i = 0 - lines_count = len(data) + section_found = False packages = set() - while i < lines_count: - if not updatable_section_found: - updatable_section_found = header_regexp.match(data[i]) - if updatable_section_found: - # Table has the following structure. Once header is found, 2 lines need to be skipped - # - # Available Updates: <--- we are here - # ID | Installed | Available - # ------- | ------- | ------- - # platform-tools | 27.0.0 | 35.0.2 <--- skip to here - i += 3 # skip table header - else: - i += 1 # just iterate next until we find the section's header + for line in data: + if not section_found: + section_found = header_regexp.match(line) continue else: - p = row_regexp.match(data[i]) + p = row_regexp.match(line) if p: packages.add(Package(p.group('name'))) - i += 1 return packages From 043fc163bc861d9cab60c7ad0cf1193c31f2c9ad Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Tue, 17 Dec 2024 21:48:42 +0200 Subject: [PATCH 45/48] renamed sdkmanager.py to android_sdkmanager.py --- plugins/module_utils/{sdkmanager.py => android_sdkmanager.py} | 0 plugins/modules/android_sdk.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename plugins/module_utils/{sdkmanager.py => android_sdkmanager.py} (100%) diff --git a/plugins/module_utils/sdkmanager.py b/plugins/module_utils/android_sdkmanager.py similarity index 100% rename from plugins/module_utils/sdkmanager.py rename to plugins/module_utils/android_sdkmanager.py diff --git a/plugins/modules/android_sdk.py b/plugins/modules/android_sdk.py index 898473da66e..a5e6a1ae5cb 100644 --- a/plugins/modules/android_sdk.py +++ b/plugins/modules/android_sdk.py @@ -139,7 +139,7 @@ ''' from ansible_collections.community.general.plugins.module_utils.mh.module_helper import StateModuleHelper -from ansible_collections.community.general.plugins.module_utils.sdkmanager import Package, AndroidSdkManager +from ansible_collections.community.general.plugins.module_utils.android_sdkmanager import Package, AndroidSdkManager class AndroidSdk(StateModuleHelper): From 7be3b3c2b7f8b3791220ca9923de79217659b6df Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Tue, 17 Dec 2024 21:49:51 +0200 Subject: [PATCH 46/48] applies minor suggestions from code review Co-authored-by: Felix Fontein --- plugins/modules/android_sdk.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/modules/android_sdk.py b/plugins/modules/android_sdk.py index a5e6a1ae5cb..9851a84fc29 100644 --- a/plugins/modules/android_sdk.py +++ b/plugins/modules/android_sdk.py @@ -73,10 +73,10 @@ package. This makes sure that at most only one license prompt will need to be answered. At the time of writing this module, a C(sdkmanager)'s package may belong to at most one license type that needs to be accepted. However, if this is changes in the future, the module may hang as there might be more prompts generated - by the C(sdkmanager) tool which the module won't be able to answer. If this is the case, file an issue and in the + by the C(sdkmanager) tool which the module will not be able to answer. If this is the case, file an issue and in the meantime, consider accepting all the licenses in advance, as it is described in the C(sdkmanager) L(documentation,https://developer.android.com/tools/sdkmanager#accept-licenses), for instance, using the - L(ansible.builtin.command module,https://docs.ansible.com/ansible/latest/collections/ansible/builtin/command_module.html) + M(ansible.builtin.command) module. seealso: - name: sdkmanager tool documentation description: Detailed information of how to install and use sdkmanager command line tool. From 90336954957c2f41212057c1d6d263ada65138bb Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Tue, 17 Dec 2024 21:58:25 +0200 Subject: [PATCH 47/48] updates BOTMETA --- .github/BOTMETA.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/BOTMETA.yml b/.github/BOTMETA.yml index d0cc407f7e2..159531011e7 100644 --- a/.github/BOTMETA.yml +++ b/.github/BOTMETA.yml @@ -375,7 +375,7 @@ files: maintainers: $team_redfish $module_utils/remote_management/lxca/common.py: maintainers: navalkp prabhosa - $module_utils/sdkmanager.py: + $module_utils/android_sdkmanager.py: maintainers: shamilovstas $module_utils/scaleway.py: labels: cloud scaleway From 2159c01ad9cc99a7cbfed6ab6152085cbee5d156 Mon Sep 17 00:00:00 2001 From: Stanislav Shamilov Date: Tue, 17 Dec 2024 22:06:36 +0200 Subject: [PATCH 48/48] reordered BOTMETA --- .github/BOTMETA.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/BOTMETA.yml b/.github/BOTMETA.yml index 159531011e7..b22d95db0da 100644 --- a/.github/BOTMETA.yml +++ b/.github/BOTMETA.yml @@ -309,6 +309,8 @@ files: maintainers: delineaKrehl tylerezimmerman $module_utils/: labels: module_utils + $module_utils/android_sdkmanager.py: + maintainers: shamilovstas $module_utils/btrfs.py: maintainers: gnfzdz $module_utils/cmd_runner_fmt.py: @@ -375,8 +377,6 @@ files: maintainers: $team_redfish $module_utils/remote_management/lxca/common.py: maintainers: navalkp prabhosa - $module_utils/android_sdkmanager.py: - maintainers: shamilovstas $module_utils/scaleway.py: labels: cloud scaleway maintainers: $team_scaleway