Repository: ansible-collections/community.general

Issue:
homebrew_cask: Install opt not working as expected
### Summary

If I use the play below it will install VSCodium and Firefox-Nightly on the first try as expected, however if the application gets removed, for example moved to trash from the applications folder instead of being uninstalled through brew, then rerunning this play will not reinstall the application. I was expecting `install_options: force` to force the re-installation of the app like `brew install --cask --force vscodium` would. Maybe I am going about this the wrong way. There is another mention of this I found, but it was not confirmed fixed I believe and the issue was somewhat old. 

### Issue Type

Bug Report

### Component Name

homebrew_cask

### Ansible Version

```console (paste below)
$ ansible --version


ansible [core 2.11.6] 
  config file = None
  configured module search path = ['/Users/brandon/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /opt/homebrew/lib/python3.9/site-packages/ansible
  ansible collection location = /Users/brandon/.ansible/collections:/usr/share/ansible/collections
  executable location = /opt/homebrew/bin/ansible
  python version = 3.9.7 (default, Sep  3 2021, 04:31:11) [Clang 12.0.5 (clang-1205.0.22.9)]
  jinja version = 2.11.2
  libyaml = False
```

### Community.general Version

```console (paste below)
$ ansible-galaxy collection list community.general

# /opt/homebrew/lib/python3.9/site-packages/ansible_collections
Collection        Version
----------------- -------
community.general 3.8.1  

# /Users/brandon/.ansible/collections/ansible_collections
Collection        Version
----------------- -------
community.general 4.0.0  
```

### Configuration

```console (paste below)
$ ansible-config dump --only-changed

```


### OS / Environment

macOS: 11.6 (20G165)
Chip: Apple M1

### Steps to Reproduce

<!--- Paste example playbooks or commands between quotes below -->

```YAML
---
- name: Setup macOS
  hosts: localhost
  connection: local
  vars:
    homebrew_casks:
      - vscodium
      - firefox-nightly
    
- name: Install Homebrew Casks
  when: ansible_distribution == 'MacOSX'
  homebrew_cask:
    name: "{{ homebrew_casks }}"
    state: present
    path: "/opt/homebrew/bin/brew"
    install_options: "force"
```

### Expected Results

I expected the cask to reinstall

### Actual Results

```console (paste below)
...
TASK [Install Homebrew Casks] *****************************************************************
task path: /Users/brandon/rush/local.yml:69
<127.0.0.1> ESTABLISH LOCAL CONNECTION FOR USER: brandon
<127.0.0.1> EXEC /bin/sh -c 'echo ~brandon && sleep 0'
<127.0.0.1> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /Users/brandon/.ansible/tmp `"&& mkdir "` echo /Users/brandon/.ansible/tmp/ansible-tmp-1636700262.7389839-41813-224928292110571 `" && echo ansible-tmp-1636700262.7389839-41813-224928292110571="` echo /Users/brandon/.ansible/tmp/ansible-tmp-1636700262.7389839-41813-224928292110571 `" ) && sleep 0'
redirecting (type: modules) ansible.builtin.homebrew_cask to community.general.homebrew_cask
Using module file /Users/brandon/.ansible/collections/ansible_collections/community/general/plugins/modules/homebrew_cask.py
<127.0.0.1> PUT /Users/brandon/.ansible/tmp/ansible-local-40239kok6_ek3/tmpcph82c4y TO /Users/brandon/.ansible/tmp/ansible-tmp-1636700262.7389839-41813-224928292110571/AnsiballZ_homebrew_cask.py
<127.0.0.1> EXEC /bin/sh -c 'chmod u+x /Users/brandon/.ansible/tmp/ansible-tmp-1636700262.7389839-41813-224928292110571/ /Users/brandon/.ansible/tmp/ansible-tmp-1636700262.7389839-41813-224928292110571/AnsiballZ_homebrew_cask.py && sleep 0'
<127.0.0.1> EXEC /bin/sh -c '/opt/homebrew/opt/python@3.9/bin/python3.9 /Users/brandon/.ansible/tmp/ansible-tmp-1636700262.7389839-41813-224928292110571/AnsiballZ_homebrew_cask.py && sleep 0'
<127.0.0.1> EXEC /bin/sh -c 'rm -f -r /Users/brandon/.ansible/tmp/ansible-tmp-1636700262.7389839-41813-224928292110571/ > /dev/null 2>&1 && sleep 0'
ok: [localhost] => {
    "changed": false,
    "invocation": {
        "module_args": {
            "accept_external_apps": false,
            "greedy": false,
            "install_options": [
                "force"
            ],
            "name": [
                "vscodium",
                "firefox-nightly"
            ],
            "path": "/opt/homebrew/bin/brew",
            "state": "present",
            "sudo_password": null,
            "update_homebrew": false,
            "upgrade_all": false
        }
    },
    "msg": "Changed: 0, Unchanged: 2"
}
...
```

### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct


Discussion:
Files identified in the description:
* [`plugins/modules/packaging/os/homebrew_cask.py`](https://github.com/ansible-collections/community.general/blob/main/plugins/modules/packaging/os/homebrew_cask.py)

If these files are incorrect, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/devel/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @Akasurde @danieljaouen @enriclluelles @indrajitr @kyleabenson @martinm82
[click here for bot help](https://github.com/ansible/ansibullbot/blob/devel/ISSUE_HELP.md)
<!--- boilerplate: notify --->
@typkrft Thanks for reporting this issue. I can reproduce this at my end. Here is the quick fix - 

```diff
diff --git a/plugins/modules/packaging/os/homebrew_cask.py b/plugins/modules/packaging/os/homebrew_cask.py
index 8fe54e3b04..4d9ecb2bcb 100644
--- a/plugins/modules/packaging/os/homebrew_cask.py
+++ b/plugins/modules/packaging/os/homebrew_cask.py
@@ -600,7 +600,7 @@ class HomebrewCask(object):
             self.message = 'Invalid cask: {0}.'.format(self.current_cask)
             raise HomebrewCaskException(self.message)

-        if self._current_cask_is_installed():
+        if '--force' not in self.install_options and self._current_cask_is_installed():
             self.unchanged_count += 1
             self.message = 'Cask already installed: {0}'.format(
                 self.current_cask,
```

Let me know if it works for you.
cc @jatorcasso

Resolving patch:
diff --git a/plugins/modules/packaging/os/homebrew_cask.py b/plugins/modules/packaging/os/homebrew_cask.py
--- a/plugins/modules/packaging/os/homebrew_cask.py
+++ b/plugins/modules/packaging/os/homebrew_cask.py
@@ -102,6 +102,12 @@
     state: present
     install_options: 'debug,appdir=/Applications'
 
+- name: Install cask with force option
+  community.general.homebrew_cask:
+    name: alfred
+    state: present
+    install_options: force
+
 - name: Allow external app
   community.general.homebrew_cask:
     name: alfred
@@ -600,7 +606,7 @@ def _install_current_cask(self):
             self.message = 'Invalid cask: {0}.'.format(self.current_cask)
             raise HomebrewCaskException(self.message)
 
-        if self._current_cask_is_installed():
+        if '--force' not in self.install_options and self._current_cask_is_installed():
             self.unchanged_count += 1
             self.message = 'Cask already installed: {0}'.format(
                 self.current_cask,


Test patch:
diff --git a/tests/integration/targets/homebrew_cask/aliases b/tests/integration/targets/homebrew_cask/aliases
new file mode 100644
--- /dev/null
+++ b/tests/integration/targets/homebrew_cask/aliases
@@ -0,0 +1,6 @@
+shippable/posix/group1
+skip/aix
+skip/freebsd
+skip/rhel
+skip/docker
+skip/python2.6
diff --git a/tests/integration/targets/homebrew_cask/defaults/main.yml b/tests/integration/targets/homebrew_cask/defaults/main.yml
new file mode 100644
--- /dev/null
+++ b/tests/integration/targets/homebrew_cask/defaults/main.yml
@@ -0,0 +1 @@
+cask: brooklyn
\ No newline at end of file
diff --git a/tests/integration/targets/homebrew_cask/tasks/main.yml b/tests/integration/targets/homebrew_cask/tasks/main.yml
new file mode 100644
--- /dev/null
+++ b/tests/integration/targets/homebrew_cask/tasks/main.yml
@@ -0,0 +1,71 @@
+####################################################################
+# WARNING: These are designed specifically for Ansible tests       #
+# and should not be used as examples of how to write Ansible roles #
+####################################################################
+
+# Test code for the homebrew_cask module.
+# Copyright: (c) 2022, Joseph Torcasso <jtorcass@redhat.com>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+---
+  - name: Find brew binary
+    command: which brew
+    register: brew_which
+    when: ansible_distribution in ['MacOSX']
+
+  - name: Get owner of brew binary
+    stat:
+      path: "{{ brew_which.stdout }}"
+    register: brew_stat
+    when: ansible_distribution in ['MacOSX']
+
+  - block:
+      - name: Install cask
+        homebrew_cask:
+          name: "{{ cask }}"
+          state: present
+        become: yes
+        become_user: "{{ brew_stat.stat.pw_name }}"
+        register: cask_install_result
+
+      - assert:
+          that:
+            - cask_install_result is changed
+            - "'Cask installed' in cask_install_result.msg"
+
+      - name: Install cask (idempotence)
+        homebrew_cask:
+          name: "{{ cask }}"
+          state: present
+        become: yes
+        become_user: "{{ brew_stat.stat.pw_name }}"
+        register: cask_install_result
+
+      - assert:
+          that:
+            - cask_install_result is not changed
+            - "'Cask installed' not in cask_install_result.msg"
+            - "'Cask already installed' in cask_install_result.msg"
+
+      - name: Install cask with force install option
+        homebrew_cask:
+          name: "{{ cask }}"
+          state: present
+          install_options: force
+        become: yes
+        become_user: "{{ brew_stat.stat.pw_name }}"
+        register: cask_install_result
+
+      - assert:
+          that:
+            - cask_install_result is changed
+            - "'Cask installed' in cask_install_result.msg"
+
+    always:
+      - name: Delete cask
+        homebrew_cask:
+          name: "{{ cask }}"
+          state: absent
+          install_options: force
+        become: yes
+        become_user: "{{ brew_stat.stat.pw_name }}"
+        ignore_errors: yes