Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add android sdk module #9236

Merged
merged 48 commits into from
Dec 20, 2024

Conversation

shamilovstas
Copy link
Contributor

SUMMARY

This module allows to manage Android SDK packages

ISSUE TYPE
  • New Module/Plugin Pull Request
COMPONENT NAME

android_sdk

ADDITIONAL INFORMATION

This module uses sdkmanager binary, which is used to install Android SDK packages. Usually, Android SDK packages are installed by Android developers using Android Studio IDE, but in case when a server needs to be set up to be used as a CI/CD runner to build Android applications, sdkmanager command line tool is used instead.
In order to use this module, sdkmanager binary must be present on the target machine. The official way of installing sdkmanager is described here: https://developer.android.com/tools/sdkmanager
There is one caveat I'm not entirely sure how/whether to fix. For some of the packages installed with sdkmanager a license must be accepted beforehand. The accepted licenses are placed inside the directory containing Android SDK, in a subdirectory licenses as a file containing a single string:

[vagrant@alma licenses]$ pwd
/usr/local/android/sdk/licenses
[vagrant@alma licenses]$ ls
android-googletv-license     android-sdk-preview-license
android-sdk-arm-dbt-license  google-gdk-license
android-sdk-license          mips-android-sysimage-license
[vagrant@alma licenses]$ cat android-sdk-license 

24333f8a63b6825ea9c5514f83c2829b004d1fee

There are three ways how a license may be accepted

  1. if, after requesting of installation of a new package, sdkmanager sees that the license for the package has not been accepted, a command line prompt will be displayed that the user is supposed to answer interactively.
  2. sdkmanager --licenses command may be used. In this case sdkmanager prints every license that has not been accepted sequentially and prompts the user after each one.
  3. a file, containing the license as a string (I suppose, it is some sort of a hash), must be placed inside licenses subdirectory for any license that must be accepted (but IMO this is error-prone as there might be new licenses, which can't be known in advance).

The easiest way to accept licenses from an Ansible playbook is to use the following task:

- name: Accept all licenses
  shell: 'yes | sdkmanager --licenses'

I haven't figured a straightforward way of how to accept licenses from inside the module. What I tried was to use data parameter of module.run_command function to send y to stdin, but the problem is that in case if we try to use the 2nd approach from the list above (sdkmanager --license), the sdkmanager tool prints multiple prompts for each license that is not accepted. The data parameter sends y only to the first license prompt, but the remaining prompts hang the module as they are waiting for the user input. Another idea was to use data='y' every time a package is installed (1st approach), but this may also lead to the module hanging as there might be several packages requested for installation, and if they belong to different licenses, this ends up the same way.
So, I decided that the module assumes that all licenses are accepted in advance (this is reflected in the "Notes" section of the documentation).

@ansibullbot ansibullbot added integration tests/integration module module labels Dec 10, 2024
@shamilovstas
Copy link
Contributor Author

shamilovstas commented Dec 16, 2024

@felixfontein @russoz I also have a question regarding BOTMETA.yml. I see that some modules have labels there, but I didn't manage to find the docs describing what these labels are and whether I should add one.

@felixfontein
Copy link
Collaborator

It might be documented somewhere in the bot's repo / code (https://github.com/ansible-community/collection_bot/), but I'm not sure. Generally I would simply ignore that field. The bot might add corresponding labels to PRs/issues for the module, but that's not something we really use.

Comment on lines 81 to 83
_RE_INSTALLED_PACKAGE = (
re.compile(r'^\s*(?P<name>\S+)\s*\|\s*\S+\s*\|\s*.+\s*\|\s*(\S+)\s*$')
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Breaking into multiple lines not really needed here. Moreover, if you search the second column for version numbers instead of just "not spaces", then you could possibly simplify the loop in lines 144-162 because you would not need to skip the header, it would be skipped by the fact that it does not match the regexp (with digits):

Suggested change
_RE_INSTALLED_PACKAGE = (
re.compile(r'^\s*(?P<name>\S+)\s*\|\s*\S+\s*\|\s*.+\s*\|\s*(\S+)\s*$')
)
_RE_INSTALLED_PACKAGE = re.compile(r'^\s*(?P<name>\S+)\s*\|\s*[\d\.]+\s*\|\s*.+\s*\|\s*(\S+)\s*$')

Copy link
Contributor Author

@shamilovstas shamilovstas Dec 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might not work because sometimes the version column is not just numbers, for instance:
build-tools;32.1.0-rc1 | 32.1.0 rc1 | Android SDK Build-Tools 32.1-rc1 | build-tools/32.1.0-rc1

Here the version is 32.1.0 rc1

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, this is not something that is documented anywhere, but looking at the output of sdkmanager --list, I see the next possible options for version:

  • 24.0.3
  • 32.1.0 rc1
  • 3.6.4111459
  • 2
  • 27.0.11902837 rc2
    Does it make sense to make a regexp covering all these version forms, considering the fact that the module does not use version at all?

)

# Example: ' platform-tools | 27.0.0 | 35.0.2'
_RE_UPDATABLE_PACKAGE = re.compile(r'^\s*(?P<name>\S+)\s*\|\s*\S+\s*\|\s*\S+\s*$')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You would need to change this regexp to search for numbers as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment above

_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<name>\S+)\s*\|\s*\S+\s*\|\s*.+\s*\|\s*(\S+)\s*$')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, from these examples, the only two possibilities that are not covered by the suggested regexp (the ones with rc1 and rc2) are also not covered by the existing regexp: |\s*\S+\s*\| will not match | 32.1.0 rc1 | (because the \S+ will not match the whitespace before rc1).

The one thing that is the delimiter here is the | character, so maybe it would be better of as:

    _RE_INSTALLED_PACKAGE = re.compile(r'^\s*(?P<name>\S+)\s*\|\s*[0-9][^|]*\b\s*\|\s*.+\s*\|\s*(\S+)\s*$')

That forces the version to begin with a digit, then anything-but-the-vertical-bar until the space and vertical bar. The \b is used to make sure the "anything-but-the-vertical-bar" part does not include the whitespaces before the bar.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As to your question of whether it makes sense to cover all the cases with the regexp, I would answer that IMHO it makes more sense to cover all the search cases with a search tool (regexp) then to cover only part of the search with it and create another search mechanism later on. Just my $0.02 ;-)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, thanks

…0 rc1". Also, this allows to simplify the parsing logic as there is no need to skip table headers anymore.
Copy link
Collaborator

@russoz russoz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

plugins/module_utils/sdkmanager.py Outdated Show resolved Hide resolved
plugins/modules/android_sdk.py Outdated Show resolved Hide resolved
plugins/modules/android_sdk.py Outdated Show resolved Hide resolved
.github/BOTMETA.yml Outdated Show resolved Hide resolved
@felixfontein
Copy link
Collaborator

Looks good to me as well! If nobody objects, I'll merge by the end of the week.

@felixfontein felixfontein removed the check-before-release PR will be looked at again shortly before release and merged if possible. label Dec 20, 2024
@felixfontein felixfontein merged commit 2b2872f into ansible-collections:main Dec 20, 2024
129 checks passed
Copy link

patchback bot commented Dec 20, 2024

Backport to stable-10: 💚 backport PR created

✅ Backport PR branch: patchback/backports/stable-10/2b2872f0efe7f2fcf2c8c0e2b40764d940c79dff/pr-9236

Backported as #9293

🤖 @patchback
I'm built with octomachinery and
my source is open — https://github.com/sanitizers/patchback-github-app.

patchback bot pushed a commit that referenced this pull request Dec 20, 2024
* adds simple implementation of adding and removing android sdk packages

* adds package update

* adds simple installed packages parsing

* moves parsing logic to a separate class

* adds absent state for sdkmanager packages and setup for tests

* adds output for installing and removing packages

* removes version from Package object since it is not possible to specify version for a package while using sdkmanager

* adds 'latest' state

* adds tests

* fixes crash when sdkmanager is invoked from python with LC_ALL=C

* fixes latest state

* adds sdk_root parameter

* adds channel parameter

* simplifies regexps, removes unused named groups

* minor refactoring of sdkmanager parsing

* adds java dependency variable for different distributions

* adds RETURN documentation

* adds check for nonexisting package

* adds check for non-accepted licenses

* removes excessive methods from sdkmanager

* removes unused 'update' module parameter, packages may be updated using 'latest' state

* minor refactoring

* adds EXAMPLES doc section

* adds DOCUMENTATION section and license headers

* fixes formatting issues

* removes diff_params

* adds maintainer

* fixes sanity check issues in sdkmanager

* adds java dependency for macos and moves some tests to a separate FreeBSD configuration

* fixes dependencies setup for OSX

* fixes dependencies setup for OSX (2)

* fixes dependencies setup for OSX (3)

* Apply minor suggestions from code review

Co-authored-by: Alexei Znamensky <[email protected]>

* applies code review suggestions

* changes force_lang from C.UTF-8 to auto in sdkmanager (as per discussion #9236 (comment))

* Revert "changes force_lang from C.UTF-8 to auto in sdkmanager (as per discussion #9236 (comment))"

This reverts commit 619f28d.

* fixes some more comments from review

* minor sanity issue fix

* uses the 'changed' test instead of checking the 'changed' attribute

* adds 'accept_licenses' parameter. Installation is now performed independently for each package specified.

* removes "Accept licenses" task from examples

* fixes docs sanity issues

* applies minor suggestions from code review

* 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.

* renamed sdkmanager.py to android_sdkmanager.py

* applies minor suggestions from code review

Co-authored-by: Felix Fontein <[email protected]>

* updates BOTMETA

* reordered BOTMETA

---------

Co-authored-by: Alexei Znamensky <[email protected]>
Co-authored-by: Felix Fontein <[email protected]>
(cherry picked from commit 2b2872f)
@felixfontein
Copy link
Collaborator

@shamilovstas thanks for your contribution!
@russoz thanks for reviewing!

felixfontein pushed a commit that referenced this pull request Dec 20, 2024
Add android sdk module (#9236)

* adds simple implementation of adding and removing android sdk packages

* adds package update

* adds simple installed packages parsing

* moves parsing logic to a separate class

* adds absent state for sdkmanager packages and setup for tests

* adds output for installing and removing packages

* removes version from Package object since it is not possible to specify version for a package while using sdkmanager

* adds 'latest' state

* adds tests

* fixes crash when sdkmanager is invoked from python with LC_ALL=C

* fixes latest state

* adds sdk_root parameter

* adds channel parameter

* simplifies regexps, removes unused named groups

* minor refactoring of sdkmanager parsing

* adds java dependency variable for different distributions

* adds RETURN documentation

* adds check for nonexisting package

* adds check for non-accepted licenses

* removes excessive methods from sdkmanager

* removes unused 'update' module parameter, packages may be updated using 'latest' state

* minor refactoring

* adds EXAMPLES doc section

* adds DOCUMENTATION section and license headers

* fixes formatting issues

* removes diff_params

* adds maintainer

* fixes sanity check issues in sdkmanager

* adds java dependency for macos and moves some tests to a separate FreeBSD configuration

* fixes dependencies setup for OSX

* fixes dependencies setup for OSX (2)

* fixes dependencies setup for OSX (3)

* Apply minor suggestions from code review

Co-authored-by: Alexei Znamensky <[email protected]>

* applies code review suggestions

* changes force_lang from C.UTF-8 to auto in sdkmanager (as per discussion #9236 (comment))

* Revert "changes force_lang from C.UTF-8 to auto in sdkmanager (as per discussion #9236 (comment))"

This reverts commit 619f28d.

* fixes some more comments from review

* minor sanity issue fix

* uses the 'changed' test instead of checking the 'changed' attribute

* adds 'accept_licenses' parameter. Installation is now performed independently for each package specified.

* removes "Accept licenses" task from examples

* fixes docs sanity issues

* applies minor suggestions from code review

* 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.

* renamed sdkmanager.py to android_sdkmanager.py

* applies minor suggestions from code review

Co-authored-by: Felix Fontein <[email protected]>

* updates BOTMETA

* reordered BOTMETA

---------

Co-authored-by: Alexei Znamensky <[email protected]>
Co-authored-by: Felix Fontein <[email protected]>
(cherry picked from commit 2b2872f)

Co-authored-by: Stanislav Shamilov <[email protected]>
erichoog pushed a commit to erichoog/community.general that referenced this pull request Dec 23, 2024
* adds simple implementation of adding and removing android sdk packages

* adds package update

* adds simple installed packages parsing

* moves parsing logic to a separate class

* adds absent state for sdkmanager packages and setup for tests

* adds output for installing and removing packages

* removes version from Package object since it is not possible to specify version for a package while using sdkmanager

* adds 'latest' state

* adds tests

* fixes crash when sdkmanager is invoked from python with LC_ALL=C

* fixes latest state

* adds sdk_root parameter

* adds channel parameter

* simplifies regexps, removes unused named groups

* minor refactoring of sdkmanager parsing

* adds java dependency variable for different distributions

* adds RETURN documentation

* adds check for nonexisting package

* adds check for non-accepted licenses

* removes excessive methods from sdkmanager

* removes unused 'update' module parameter, packages may be updated using 'latest' state

* minor refactoring

* adds EXAMPLES doc section

* adds DOCUMENTATION section and license headers

* fixes formatting issues

* removes diff_params

* adds maintainer

* fixes sanity check issues in sdkmanager

* adds java dependency for macos and moves some tests to a separate FreeBSD configuration

* fixes dependencies setup for OSX

* fixes dependencies setup for OSX (2)

* fixes dependencies setup for OSX (3)

* Apply minor suggestions from code review

Co-authored-by: Alexei Znamensky <[email protected]>

* applies code review suggestions

* changes force_lang from C.UTF-8 to auto in sdkmanager (as per discussion ansible-collections#9236 (comment))

* Revert "changes force_lang from C.UTF-8 to auto in sdkmanager (as per discussion ansible-collections#9236 (comment))"

This reverts commit 619f28d.

* fixes some more comments from review

* minor sanity issue fix

* uses the 'changed' test instead of checking the 'changed' attribute

* adds 'accept_licenses' parameter. Installation is now performed independently for each package specified.

* removes "Accept licenses" task from examples

* fixes docs sanity issues

* applies minor suggestions from code review

* 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.

* renamed sdkmanager.py to android_sdkmanager.py

* applies minor suggestions from code review

Co-authored-by: Felix Fontein <[email protected]>

* updates BOTMETA

* reordered BOTMETA

---------

Co-authored-by: Alexei Znamensky <[email protected]>
Co-authored-by: Felix Fontein <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport-10 Automatically create a backport for the stable-10 branch integration tests/integration module_utils module_utils module module plugins plugin (any type) tests tests
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants