diff --git a/changelogs/fragments/9777-homebrew-fix-crash-when-packages-include-tap.yml b/changelogs/fragments/9777-homebrew-fix-crash-when-packages-include-tap.yml new file mode 100644 index 0000000000..a7649988bc --- /dev/null +++ b/changelogs/fragments/9777-homebrew-fix-crash-when-packages-include-tap.yml @@ -0,0 +1,2 @@ +bugfixes: + - homebrew - fix crash when package names include tap (https://github.com/ansible-collections/community.general/issues/9777, https://github.com/ansible-collections/community.general/pull/9803). diff --git a/plugins/modules/homebrew.py b/plugins/modules/homebrew.py index 168184fc68..bdc6f65724 100644 --- a/plugins/modules/homebrew.py +++ b/plugins/modules/homebrew.py @@ -385,14 +385,34 @@ class Homebrew(object): self.outdated_packages.add(package_name) def _extract_package_name(self, package_detail, is_cask): - canonical_name = package_detail["full_token"] if is_cask else package_detail["full_name"] # For ex: 'sqlite', might contain a tap prefix. - name = package_detail["token"] if is_cask else package_detail["name"] # For ex: 'sqlite' + # "brew info" can lookup by name, full_name, token, full_token, or aliases + # In addition, any name can be prefixed by the tap. + # Any of these can be supplied by the user as the package name. In case + # of ambiguity, where a given name might match multiple packages, + # formulae are preferred over casks. For all other ambiguities, the + # results are an error. Note that in the homebrew/core and + # homebrew/cask taps, there are no "other" ambiguities. + if is_cask: # according to brew info + name = package_detail["token"] + full_name = package_detail["full_token"] + else: + name = package_detail["name"] + full_name = package_detail["full_name"] - all_valid_names = set(package_detail.get("aliases", [])) # For ex: {'sqlite3'} - all_valid_names.update((canonical_name, name)) + tapped_name = package_detail["tap"] + "/" + name + aliases = package_detail.get("aliases", []) + + package_names = set([name, full_name, tapped_name] + aliases) + + # Finally, identify which of all those package names was the one supplied by the user. + package_names = package_names & set(self.packages) + if len(package_names) != 1: + self.failed = True + self.message = "Package names are missing or ambiguous: " + ", ".join(str(p) for p in package_names) + raise HomebrewException(self.message) # Then make sure the user provided name resurface. - return (all_valid_names & set(self.packages)).pop() + return package_names.pop() def _get_packages_info(self): cmd = [ diff --git a/tests/integration/targets/homebrew/tasks/casks.yml b/tests/integration/targets/homebrew/tasks/casks.yml index 50824a9e9f..b411886334 100644 --- a/tests/integration/targets/homebrew/tasks/casks.yml +++ b/tests/integration/targets/homebrew/tasks/casks.yml @@ -95,3 +95,12 @@ - assert: that: - package_result is not changed + + # This crashed on 4867eb4 - Ref: issue #9777 + - name: Install cask using homelab/cask syntax + homebrew: + package: "homebrew/cask/{{ package_name }}" + state: present + update_homebrew: false + become: true + become_user: "{{ brew_stat.stat.pw_name }}" \ No newline at end of file