Skip to content

Commit 833e6e3

Browse files
homebrew: Support old_tokens and oldnames in homebrew package data (#10805)
* homebrew: Support old_tokens and oldnames in homebrew package data Fixes #10804 Since brew info will accept old_tokens (for casks) and oldnames (for formulae) when provided by the homebrew module "name" argument, the module also needs to consider thes old names as valid for the given package. This commit updates _extract_package_name to do that. All existing package name tests, including existing tests for name aliases and tap prefixing, have been consolidated with new name tests into package_names.yml. * Added changelog fragment. * homebrew: replace non-py2 compliant f-string usage * code formatting lint, and py2 compatibility fixes * homebrew: added licenses to new files, nox lint * Update plugins/modules/homebrew.py use str.format() instead of string addition Co-authored-by: Felix Fontein <[email protected]> * Update tests/integration/targets/homebrew/tasks/casks.yml Co-authored-by: Felix Fontein <[email protected]> * Update tests/integration/targets/homebrew/tasks/package_names_item.yml Co-authored-by: Felix Fontein <[email protected]> * Update tests/integration/targets/homebrew/tasks/formulae.yml Co-authored-by: Felix Fontein <[email protected]> * Fixes for performance concerns on new homebrew tests. 1) tests for alternate package names are commented out in main.yml. 2) the "install via alternate name, uninstall via base name" test case was deemed duplicative, and has been deleted . 3) minor fixes to use jinja2 "~" for string concat instead of "+" * Fix nox lint --------- Co-authored-by: Felix Fontein <[email protected]>
1 parent c1e877d commit 833e6e3

File tree

7 files changed

+146
-58
lines changed

7 files changed

+146
-58
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
bugfixes:
2+
- homebrew - do not fail when cask or formula name has changed in homebrew repo (https://github.com/ansible-collections/community.general/issues/10804, https://github.com/ansible-collections/community.general/pull/10805).

plugins/modules/homebrew.py

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -385,12 +385,13 @@ def _save_package_info(self, package_detail, package_name):
385385
self.outdated_packages.add(package_name)
386386

387387
def _extract_package_name(self, package_detail, is_cask):
388-
# "brew info" can lookup by name, full_name, token, full_token, or aliases
389-
# In addition, any name can be prefixed by the tap.
390-
# Any of these can be supplied by the user as the package name. In case
391-
# of ambiguity, where a given name might match multiple packages,
392-
# formulae are preferred over casks. For all other ambiguities, the
393-
# results are an error. Note that in the homebrew/core and
388+
# "brew info" can lookup by name, full_name, token, full_token,
389+
# oldnames, old_tokens, or aliases. In addition, any of the
390+
# above names can be prefixed by the tap. Any of these can be
391+
# supplied by the user as the package name. In case of
392+
# ambiguity, where a given name might match multiple packages,
393+
# formulae are preferred over casks. For all other ambiguities,
394+
# the results are an error. Note that in the homebrew/core and
394395
# homebrew/cask taps, there are no "other" ambiguities.
395396
if is_cask: # according to brew info
396397
name = package_detail["token"]
@@ -405,15 +406,26 @@ def _extract_package_name(self, package_detail, is_cask):
405406
#
406407
# Issue https://github.com/ansible-collections/community.general/issues/10012:
407408
# package_detail["tap"] is None if package is no longer available.
408-
tapped_name = [package_detail["tap"] + "/" + name] if package_detail["tap"] else []
409-
aliases = package_detail.get("aliases", [])
410-
package_names = set([name, full_name] + tapped_name + aliases)
409+
#
410+
# Issue https://github.com/ansible-collections/community.general/issues/10804
411+
# name can be an alias, oldnames or old_tokens optionally prefixed by tap
412+
package_names = {name, full_name}
413+
package_names.update(package_detail.get("aliases", []))
414+
package_names.update(package_detail.get("oldnames", []))
415+
package_names.update(package_detail.get("old_tokens", []))
416+
if package_detail['tap']:
417+
# names so far, with tap prefix added to each
418+
tapped_names = {package_detail["tap"] + "/" + x for x in package_names}
419+
package_names.update(tapped_names)
411420

412421
# Finally, identify which of all those package names was the one supplied by the user.
413422
package_names = package_names & set(self.packages)
414423
if len(package_names) != 1:
415424
self.failed = True
416-
self.message = "Package names are missing or ambiguous: " + ", ".join(str(p) for p in package_names)
425+
self.message = "Package names for {name} are missing or ambiguous: {packages}".format(
426+
name=name,
427+
packages=", ".join(str(p) for p in package_names),
428+
)
417429
raise HomebrewException(self.message)
418430

419431
# Then make sure the user provided name resurface.

tests/integration/targets/homebrew/tasks/casks.yml

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,3 @@
9595
- assert:
9696
that:
9797
- package_result is not changed
98-
99-
# This crashed on 4867eb4 - Ref: issue #9777
100-
- name: Install cask using homelab/cask syntax
101-
homebrew:
102-
package: "homebrew/cask/{{ package_name }}"
103-
state: present
104-
update_homebrew: false
105-
become: true
106-
become_user: "{{ brew_stat.stat.pw_name }}"

tests/integration/targets/homebrew/tasks/formulae.yml

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -303,45 +303,6 @@
303303
- "package_result.changed_pkgs == []"
304304
- "package_result.unchanged_pkgs | sort == ['gnu-tar', 'gnu-time']"
305305

306-
# Test alias handling with sqlite (that is aliased to sqlite3)
307-
- block:
308-
- name: Make sure sqlite package is not installed
309-
homebrew:
310-
name: "sqlite"
311-
state: absent
312-
become: true
313-
become_user: "{{ brew_stat.stat.pw_name }}"
314-
315-
- name: Install sqlite package using alias (sqlite3)
316-
homebrew:
317-
name: "sqlite3"
318-
state: present
319-
become: true
320-
become_user: "{{ brew_stat.stat.pw_name }}"
321-
register: install_result
322-
323-
- assert:
324-
that:
325-
- install_result is changed
326-
- "install_result.msg == 'Package installed: sqlite3'"
327-
- "install_result.changed_pkgs == ['sqlite3']"
328-
- "install_result.unchanged_pkgs == []"
329-
330-
- name: Again install sqlite package using alias (sqlite3)
331-
homebrew:
332-
name: "sqlite3"
333-
state: present
334-
become: true
335-
become_user: "{{ brew_stat.stat.pw_name }}"
336-
register: reinstall_result
337-
338-
- assert:
339-
that:
340-
- reinstall_result is not changed
341-
- "reinstall_result.msg == 'Package already installed: sqlite3'"
342-
- "reinstall_result.changed_pkgs == []"
343-
- "reinstall_result.unchanged_pkgs == ['sqlite3']"
344-
345306
# Test install from homebrew tap
346307
- block:
347308
- name: Tap hashicorp repository

tests/integration/targets/homebrew/tasks/main.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,8 @@
1414
- include_tasks: 'formulae.yml'
1515
- include_tasks: 'casks.yml'
1616
- include_tasks: 'docker.yml'
17+
18+
# Slow tests to exhaustively test install/uninstall on alternate package names,
19+
# Commented out for normal use:
20+
21+
# - include_tasks: 'package_names.yml'
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Copyright (c) Ansible Project
2+
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
3+
# SPDX-License-Identifier: GPL-3.0-or-later
4+
5+
# A series of package tests that check all the
6+
# different ways that "brew info" can refer to a package.
7+
#
8+
# The homebrew module must mimic what brew info supports,
9+
# or else errors will occur.
10+
11+
- name: Find brew binary
12+
command: which brew
13+
register: brew_which
14+
15+
- name: Get owner of brew binary
16+
stat:
17+
path: "{{ brew_which.stdout }}"
18+
register: brew_stat
19+
20+
# Note: We no longer use sqlite3 for "formula alias" tests, because mac
21+
# ships with old python3 and if we use python3 from homebrew, sqlite3
22+
# cannot be uninstalled, which is part of the alias test. We use mandoc
23+
# instead.
24+
25+
# An example of a case were old_tokens support is important: User has an
26+
# ansible task that installs homebrew/cask/docker (the tap prefix is
27+
# required, or else one gets the formula ansible/core/docker). Homebrew
28+
# recently changed the name of the cask to docker-desktop, moving
29+
# "docker" into its old_tokens array. At that point
30+
# community.general.homebrew stopped working, because it succeeds in
31+
# running "brew info" on the name "homebrew/cask/docker", which adds
32+
# docker-desktop to the list of affected packages. But, later, when it
33+
# asks which user name entry corresponds to the affected cask, it
34+
# failed, because the code didn't think to consider old_tokens as one of
35+
# the valid ways of referring to a cask. That is issue #10804, herein
36+
# fixed.
37+
38+
- name: Test brew uninstall/install using various names accepted by "brew info"
39+
vars:
40+
packages:
41+
# All packages are carefully selected to have no dependencies, to reduce install time
42+
# homebrew/cask/kitty crashed on 4867eb4 - Ref: issue #9777
43+
- [kitty, homebrew/cask/kitty, "cask by name with tap prefix"]
44+
# mdocml test replaces old sqlite3 test, see above.
45+
- [mandoc, mdocml, "formula alias"]
46+
- [mandoc, homebrew/core/mdocml, "formula alias with tap prefix"]
47+
# These all crash on f772bcd - Ref: issue #10804
48+
- [cmark, commonmark, "formula with oldnames"]
49+
- [cmark, homebrew/core/commonmark, "formula with oldnames and tap prefix"]
50+
- [sipgate, clinq, "cask with old_tokens"]
51+
- [sipgate, homebrew/cask/clinq, "cask with old_tokens and tap prefix"]
52+
include_tasks: package_names_item.yml
53+
loop: "{{ packages }}"
54+
loop_control:
55+
loop_var: package
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Copyright (c) Ansible Project
2+
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
3+
# SPDX-License-Identifier: GPL-3.0-or-later
4+
5+
# This task block is included by package_names.yml once for each test package,
6+
# having set package variable to the name to test
7+
- name: Test one package
8+
block:
9+
- debug:
10+
var: package
11+
12+
- name: Assure package is absent
13+
homebrew:
14+
package: "{{ package[0] }}"
15+
state: absent
16+
update_homebrew: false
17+
become: true
18+
become_user: "{{ brew_stat.stat.pw_name }}"
19+
20+
- name: Install package by an alternative name
21+
homebrew:
22+
package: "{{ package[1] }}"
23+
state: present
24+
update_homebrew: false
25+
become: true
26+
become_user: "{{ brew_stat.stat.pw_name }}"
27+
register: package_result
28+
29+
- assert:
30+
that:
31+
- package_result is changed
32+
- package_result.changed_pkgs[0] == package[1]
33+
- package_result.unchanged_pkgs | length == 0
34+
- 'package_result.msg == "Package installed: " ~ package[1]'
35+
36+
- name: Reinstall should do nothing
37+
homebrew:
38+
package: "{{ package[1] }}"
39+
state: present
40+
update_homebrew: false
41+
become: true
42+
become_user: "{{ brew_stat.stat.pw_name }}"
43+
register: package_result
44+
45+
- assert:
46+
that:
47+
- package_result is not changed
48+
- 'package_result.msg == "Package already installed: " ~ package[1]'
49+
50+
- name: Uninstall should work on alternate name
51+
homebrew:
52+
package: "{{ package[1] }}"
53+
state: absent
54+
update_homebrew: false
55+
become: true
56+
become_user: "{{ brew_stat.stat.pw_name }}"
57+
register: package_result
58+
59+
- assert:
60+
that:
61+
- package_result is changed
62+
- 'package_result.msg == "Package uninstalled: " ~ package[1]'

0 commit comments

Comments
 (0)