Repository: ansible-collections/community.general

Issue:
pacman: cannot use yay with become option
### Summary

When run component community.general.pacman with yay and become.

Error is when execute https://github.com/ansible-collections/community.general/blob/main/plugins/modules/pacman.py#L707 with become configuration

When execute `yay` with sudo i got outputs:
```
$ sudo yay --query | head -n 10
 -> Avoid running yay as root/sudo.
a52dec 0.7.4-12
aalib 1.4rc5-15
accounts-qml-module 0.7-4
accountsservice 22.08.8-2
acl 2.3.1-3
```

Problem is with ` -> Avoid running yay as root/sudo.` line

Split doesn't just return 2 items in lines:
- https://github.com/ansible-collections/community.general/blob/3862de3f153c4401b7d77dba9e71a6535973ecb4/plugins/modules/pacman.py#L713
- https://github.com/ansible-collections/community.general/blob/3862de3f153c4401b7d77dba9e71a6535973ecb4/plugins/modules/pacman.py#L728
- https://github.com/ansible-collections/community.general/blob/3862de3f153c4401b7d77dba9e71a6535973ecb4/plugins/modules/pacman.py#L754

Here got error with sudo msg: https://github.com/ansible-collections/community.general/blob/main/plugins/modules/pacman.py#L776

### Issue Type

Bug Report

### Component Name

community.general.pacman

### Ansible Version

```console (paste below)
$ ansible --version
ansible [core 2.13.1]
  config file = None
  configured module search path = ['/home/username/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/username/.local/lib/python3.10/site-packages/ansible
  ansible collection location = /home/username/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/local/bin/ansible
  python version = 3.10.6 (main, Aug 10 2022, 11:40:04) [GCC 11.3.0]
  jinja version = 3.0.3
  libyaml = True
```


### Community.general Version

```console (paste below)
$ ansible-galaxy collection list community.general

# /home/username/.local/lib/python3.10/site-packages/ansible_collections
Collection        Version
----------------- -------
community.general 5.0.2

# /usr/local/lib/python3.10/dist-packages/ansible_collections
Collection        Version
----------------- -------
community.general 6.4.0
```


### Configuration

```console (paste below)
$ ansible-config dump --only-changed

```


### OS / Environment

NAME="Manjaro Linux"
PRETTY_NAME="Manjaro Linux"
ID=manjaro
ID_LIKE=arch
BUILD_ID=rolling

### Steps to Reproduce

<!--- Paste example playbooks or commands between quotes below -->
```yaml (paste below)
- name:  Check connection with hosts
  hosts: all
  become: true
  tasks:
  - name: Install Base packages
    ansible.builtin.package:
      name: "{{ item }}"
      state: present
    with_items:
      - base-devel
      - git
      - yay
  - name: Install XRDP
    community.general.pacman:
      name: xrdp
      state: present
      executable: yay
      extra_args: --builddir /var/cache/yay
```


### Expected Results

All `OK`

### Actual Results

```console (paste below)
TASK [Install Base packages] ***********************************************************************************************************************
ok: [homelab] => (item=base-devel)
ok: [homelab] => (item=git)
ok: [homelab] => (item=yay)

TASK [Install XRDP] ********************************************************************************************************************************
fatal: [homelab]: FAILED! => {"changed": false, "module_stderr": "Shared connection to 192.168.1.92 closed.\r\n", "module_stdout": "\r\nTraceback (most recent call last):\r\n  File \"/home/username/.ansible/tmp/ansible-tmp-1678793803.2755966-3430-73725877690430/AnsiballZ_pacman.py\", line 107, in <module>\r\n    _ansiballz_main()\r\n  File \"/home/username/.ansible/tmp/ansible-tmp-1678793803.2755966-3430-73725877690430/AnsiballZ_pacman.py\", line 99, in _ansiballz_main\r\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\r\n  File \"/home/username/.ansible/tmp/ansible-tmp-1678793803.2755966-3430-73725877690430/AnsiballZ_pacman.py\", line 47, in invoke_module\r\n    runpy.run_module(mod_name='ansible_collections.community.general.plugins.modules.packaging.os.pacman', init_globals=dict(_module_fqn='ansible_collections.community.general.plugins.modules.packaging.os.pacman', _modlib_path=modlib_path),\r\n  File \"/usr/lib/python3.10/runpy.py\", line 224, in run_module\r\n    return _run_module_code(code, init_globals, run_name, mod_spec)\r\n  File \"/usr/lib/python3.10/runpy.py\", line 96, in _run_module_code\r\n    _run_code(code, mod_globals, init_globals,\r\n  File \"/usr/lib/python3.10/runpy.py\", line 86, in _run_code\r\n    exec(code, run_globals)\r\n  File \"/tmp/ansible_community.general.pacman_payload_deduvc4u/ansible_community.general.pacman_payload.zip/ansible_collections/community/general/plugins/modules/packaging/os/pacman.py\", line 770, in <module>\r\n  File \"/tmp/ansible_community.general.pacman_payload_deduvc4u/ansible_community.general.pacman_payload.zip/ansible_collections/community/general/plugins/modules/packaging/os/pacman.py\", line 766, in main\r\n  File \"/tmp/ansible_community.general.pacman_payload_deduvc4u/ansible_community.general.pacman_payload.zip/ansible_collections/community/general/plugins/modules/packaging/os/pacman.py\", line 313, in run\r\n  File \"/tmp/ansible_community.general.pacman_payload_deduvc4u/ansible_community.general.pacman_payload.zip/ansible_collections/community/general/plugins/modules/packaging/os/pacman.py\", line 645, in _build_inventory\r\nValueError: too many values to unpack (expected 2)\r\n", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}
```


### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct


Discussion:
Files identified in the description:
* [`plugins/modules/pacman.py`](https://github.com/ansible-collections/community.general/blob/main/plugins/modules/pacman.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 @indrajitr @tchernomax
[click here for bot help](https://github.com/ansible/ansibullbot/blob/devel/ISSUE_HELP.md)
<!--- boilerplate: notify --->
cc @jraby
[click here for bot help](https://github.com/ansible/ansibullbot/blob/devel/ISSUE_HELP.md)
<!--- boilerplate: notify --->
I've also had issues running yay with the pacman module. In fact, using yay with sudo is not recommended, so the installation should somehow be separated from the download. I'm not sure if yay has an option to download, then install separately. I know that is a separate issue (https://github.com/Jguer/yay/issues/1026) but it's important to keep this in mind when trying to run yay using ansible. 

Resolving patch:
diff --git a/plugins/modules/pacman.py b/plugins/modules/pacman.py
--- a/plugins/modules/pacman.py
+++ b/plugins/modules/pacman.py
@@ -133,6 +133,9 @@
     it is much more efficient to pass the list directly to the I(name) option.
   - To use an AUR helper (I(executable) option), a few extra setup steps might be required beforehand.
     For example, a dedicated build user with permissions to install packages could be necessary.
+  - >
+    In the tests, while using C(yay) as the I(executable) option, the module failed to install AUR packages
+    with the error: C(error: target not found: <pkg>).
 """
 
 RETURN = """
@@ -263,6 +266,7 @@
     reason_for: all
 """
 
+import re
 import shlex
 from ansible.module_utils.basic import AnsibleModule
 from collections import defaultdict, namedtuple
@@ -418,7 +422,7 @@ def _build_install_diff(pacman_verb, pkglist):
             for p in name_ver:
                 # With Pacman v6.0.1 - libalpm v13.0.1, --upgrade outputs "loading packages..." on stdout. strip that.
                 # When installing from URLs, pacman can also output a 'nothing to do' message. strip that too.
-                if "loading packages" in p or "there is nothing to do" in p:
+                if "loading packages" in p or "there is nothing to do" in p or 'Avoid running' in p:
                     continue
                 name, version = p.split()
                 if name in self.inventory["installed_pkgs"]:
@@ -706,11 +710,12 @@ def _build_inventory(self):
         installed_pkgs = {}
         dummy, stdout, dummy = self.m.run_command([self.pacman_path, "--query"], check_rc=True)
         # Format of a line: "pacman 6.0.1-2"
+        query_re = re.compile(r'^\s*(?P<pkg>\S+)\s+(?P<ver>\S+)\s*$')
         for l in stdout.splitlines():
-            l = l.strip()
-            if not l:
+            query_match = query_re.match(l)
+            if not query_match:
                 continue
-            pkg, ver = l.split()
+            pkg, ver = query_match.groups()
             installed_pkgs[pkg] = ver
 
         installed_groups = defaultdict(set)
@@ -721,11 +726,12 @@ def _build_inventory(self):
         #     base-devel file
         #     base-devel findutils
         #     ...
+        query_groups_re = re.compile(r'^\s*(?P<group>\S+)\s+(?P<pkg>\S+)\s*$')
         for l in stdout.splitlines():
-            l = l.strip()
-            if not l:
+            query_groups_match = query_groups_re.match(l)
+            if not query_groups_match:
                 continue
-            group, pkgname = l.split()
+            group, pkgname = query_groups_match.groups()
             installed_groups[group].add(pkgname)
 
         available_pkgs = {}
@@ -747,11 +753,12 @@ def _build_inventory(self):
         #     vim-plugins vim-airline-themes
         #     vim-plugins vim-ale
         #     ...
+        sync_groups_re = re.compile(r'^\s*(?P<group>\S+)\s+(?P<pkg>\S+)\s*$')
         for l in stdout.splitlines():
-            l = l.strip()
-            if not l:
+            sync_groups_match = sync_groups_re.match(l)
+            if not sync_groups_match:
                 continue
-            group, pkg = l.split()
+            group, pkg = sync_groups_match.groups()
             available_groups[group].add(pkg)
 
         upgradable_pkgs = {}
@@ -759,9 +766,14 @@ def _build_inventory(self):
             [self.pacman_path, "--query", "--upgrades"], check_rc=False
         )
 
+        stdout = stdout.splitlines()
+        if stdout and "Avoid running" in stdout[0]:
+            stdout = stdout[1:]
+        stdout = "\n".join(stdout)
+
         # non-zero exit with nothing in stdout -> nothing to upgrade, all good
         # stderr can have warnings, so not checked here
-        if rc == 1 and stdout == "":
+        if rc == 1 and not stdout:
             pass  # nothing to upgrade
         elif rc == 0:
             # Format of lines:
@@ -771,7 +783,7 @@ def _build_inventory(self):
                 l = l.strip()
                 if not l:
                     continue
-                if "[ignored]" in l:
+                if "[ignored]" in l or "Avoid running" in l:
                     continue
                 s = l.split()
                 if len(s) != 4:


Test patch:
diff --git a/tests/integration/targets/pacman/handlers/main.yml b/tests/integration/targets/pacman/handlers/main.yml
new file mode 100644
--- /dev/null
+++ b/tests/integration/targets/pacman/handlers/main.yml
@@ -0,0 +1,23 @@
+---
+# 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: Remove user yaybuilder
+  ansible.builtin.user:
+    name: yaybuilder
+    state: absent
+
+- name: Remove yay
+  ansible.builtin.package:
+    name: yay
+    state: absent
+
+- name: Remove packages for yay-become
+  ansible.builtin.package:
+    name:
+      - base-devel
+      - yay
+      - git
+      - nmap
+    state: absent
diff --git a/tests/integration/targets/pacman/tasks/main.yml b/tests/integration/targets/pacman/tasks/main.yml
--- a/tests/integration/targets/pacman/tasks/main.yml
+++ b/tests/integration/targets/pacman/tasks/main.yml
@@ -17,3 +17,4 @@
     - include_tasks: 'update_cache.yml'
     - include_tasks: 'locally_installed_package.yml'
     - include_tasks: 'reason.yml'
+    - include_tasks: 'yay-become.yml'
diff --git a/tests/integration/targets/pacman/tasks/yay-become.yml b/tests/integration/targets/pacman/tasks/yay-become.yml
new file mode 100644
--- /dev/null
+++ b/tests/integration/targets/pacman/tasks/yay-become.yml
@@ -0,0 +1,66 @@
+---
+# 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
+
+# This is more convoluted that one might expect, because:
+#   - yay is not available or installation in ArchLinux (as it is in Manjaro - issue 6184 reports using it)
+#   - to install yay in ArchLinux requires building the package
+#   - makepkg cannot be run as root, but the user running it must have sudo to install the resulting package
+
+- name: create user
+  ansible.builtin.user:
+    name: yaybuilder
+    state: present
+  notify: Remove user yaybuilder
+
+- name: grant sudo powers to builder
+  community.general.sudoers:
+    name: yaybuilder
+    user: yaybuilder
+    commands: ALL
+    nopassword: true
+
+- name: Install base packages
+  ansible.builtin.package:
+    name:
+      - base-devel
+      - git
+      - go
+    state: present
+  notify: Remove packages for yay-become
+
+- name: Hack permssions for the remote_tmp_dir
+  ansible.builtin.file:
+    path: "{{ remote_tmp_dir }}"
+    mode: '0777'
+
+- name: Create temp directory for builder
+  ansible.builtin.file:
+    path: "{{ remote_tmp_dir }}/builder"
+    owner: yaybuilder
+    state: directory
+    mode: '0755'
+
+- name: clone yay git repo
+  become: true
+  become_user: yaybuilder
+  ansible.builtin.git:
+    repo: https://aur.archlinux.org/yay.git
+    dest: "{{ remote_tmp_dir  }}/builder/yay"
+    depth: 1
+
+- name: make package
+  become: true
+  become_user: yaybuilder
+  ansible.builtin.command:
+    chdir: "{{ remote_tmp_dir  }}/builder/yay"
+    cmd: makepkg -si --noconfirm
+  notify: Remove yay
+
+- name: Install nmap
+  community.general.pacman:
+    name: nmap
+    state: present
+    executable: yay
+    extra_args: --builddir /var/cache/yay