Repository: ansible-collections/community.general

Issue:
macports: include stdout and stderr for more actions
### Summary

At present, few `macports` actions include `stdout` and `stderr` in their status.
Add `stdout` and `stderr` to status for more actions so that it is possible to see detail on what has been done.


### Issue Type

Feature Idea

### Component Name

macports

### Additional Information

For example, running this task:

```yaml
    - name: Update and upgrade MacPorts
      community.general.macports:
        selfupdate: yes
        upgrade: yes
      become: yes
      register: macports_status
```

might produce in `macports_status`:

```json
{
  "msg": "Outdated ports upgraded successfully",
  "changed": true,
  "failed": false
}
```
Including `stdout` and `stderr` in status might produce:

```json
{
  "msg": "Outdated ports upgraded successfully",
  "changed": true,
  "stderr": "",
  "stdout": "--->  Updating MacPorts base sources using rsync\n\nWillkommen auf dem RSYNC-server auf ftp.fau.de.\nNicht all unsere Mirror sind per rsync verfuegbar.\n\nWelcome to the RSYNC daemon on ftp.fau.de.\nNot all of our mirrors are available through rsync.\n\n\nreceiving file list ... done\n\nsent 16 bytes  received 55 bytes  20.29 bytes/sec\ntotal size is 85857280  speedup is 1209257.46\n\nWillkommen auf dem RSYNC-server auf ftp.fau.de.\nNicht all unsere Mirror sind per rsync verfuegbar.\n\nWelcome to the RSYNC daemon on ftp.fau.de.\nNot all of our mirrors are available through rsync.\n\n\nreceiving file list ... done\n\nsent 16 bytes  received 62 bytes  31.20 bytes/sec\ntotal size is 512  speedup is 6.56\nMacPorts base version 2.7.1 installed,\nMacPorts base version 2.7.1 downloaded.\n--->  Updating the ports tree\nSynchronizing local ports tree from rsync://rsync.macports.org/release/tarballs/ports.tar\n\nWillkommen auf dem RSYNC-server auf ftp.fau.de.\nNicht all unsere Mirror sind per rsync verfuegbar.\n\nWelcome to the RSYNC daemon on ftp.fau.de.\nNot all of our mirrors are available through rsync.\n\n\nreceiving file list ... done\n./\n\nsent 68 bytes  received 99 bytes  66.80 bytes/sec\ntotal size is 89284608  speedup is 534638.37\nCreating port index in /opt/local/var/macports/sources/rsync.macports.org/release/tarballs/ports\n\nTotal number of ports parsed:\t0 \nPorts successfully parsed:\t0 \nPorts failed:\t\t\t0 \nUp-to-date ports skipped:\t26978\n\n--->  MacPorts base is already the latest version\n\nThe ports tree has been updated. To upgrade your installed ports, you should run\n  port upgrade outdated\nNothing to upgrade.\n--->  Scanning binaries for linking errors\n--->  No broken files found.\n--->  No broken ports found.\n",
  "stdout_lines": [
    "--->  Updating MacPorts base sources using rsync",
    "",
    "Willkommen auf dem RSYNC-server auf ftp.fau.de.",
    "Nicht all unsere Mirror sind per rsync verfuegbar.",
    "",
    "Welcome to the RSYNC daemon on ftp.fau.de.",
    "Not all of our mirrors are available through rsync.",
    "",
    "",
    "receiving file list ... done",
    "",
    "sent 16 bytes  received 55 bytes  20.29 bytes/sec",
    "total size is 85857280  speedup is 1209257.46",
    "",
    "Willkommen auf dem RSYNC-server auf ftp.fau.de.",
    "Nicht all unsere Mirror sind per rsync verfuegbar.",
    "",
    "Welcome to the RSYNC daemon on ftp.fau.de.",
    "Not all of our mirrors are available through rsync.",
    "",
    "",
    "receiving file list ... done",
    "",
    "sent 16 bytes  received 62 bytes  31.20 bytes/sec",
    "total size is 512  speedup is 6.56",
    "MacPorts base version 2.7.1 installed,",
    "MacPorts base version 2.7.1 downloaded.",
    "--->  Updating the ports tree",
    "Synchronizing local ports tree from rsync://rsync.macports.org/release/tarballs/ports.tar",
    "",
    "Willkommen auf dem RSYNC-server auf ftp.fau.de.",
    "Nicht all unsere Mirror sind per rsync verfuegbar.",
    "",
    "Welcome to the RSYNC daemon on ftp.fau.de.",
    "Not all of our mirrors are available through rsync.",
    "",
    "",
    "receiving file list ... done",
    "./",
    "",
    "sent 68 bytes  received 99 bytes  66.80 bytes/sec",
    "total size is 89284608  speedup is 534638.37",
    "Creating port index in /opt/local/var/macports/sources/rsync.macports.org/release/tarballs/ports",
    "",
    "Total number of ports parsed:\t0 ",
    "Ports successfully parsed:\t0 ",
    "Ports failed:\t\t\t0 ",
    "Up-to-date ports skipped:\t26978",
    "",
    "--->  MacPorts base is already the latest version",
    "",
    "The ports tree has been updated. To upgrade your installed ports, you should run",
    "  port upgrade outdated",
    "Nothing to upgrade.",
    "--->  Scanning binaries for linking errors",
    "--->  No broken files found.",
    "--->  No broken ports found."
  ],
  "stderr_lines": [],
  "failed": false
}
```


### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct


Discussion:
Files identified in the description:
* [`plugins/callback/say.py`](https://github.com/ansible-collections/community.general/blob/main/plugins/callback/say.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 @indrajitr @kyleabenson @martinm82
[click here for bot help](https://github.com/ansible/ansibullbot/blob/devel/ISSUE_HELP.md)
<!--- boilerplate: notify --->

Resolving patch:
diff --git a/plugins/modules/packaging/os/macports.py b/plugins/modules/packaging/os/macports.py
--- a/plugins/modules/packaging/os/macports.py
+++ b/plugins/modules/packaging/os/macports.py
@@ -120,7 +120,7 @@ def selfupdate(module, port_path):
             changed = False
             msg = "Macports already up-to-date"
 
-        return (changed, msg)
+        return (changed, msg, out, err)
     else:
         module.fail_json(msg="Failed to update Macports", stdout=out, stderr=err)
 
@@ -134,11 +134,11 @@ def upgrade(module, port_path):
     if out.strip() == "Nothing to upgrade.":
         changed = False
         msg = "Ports already upgraded"
-        return (changed, msg)
+        return (changed, msg, out, err)
     elif rc == 0:
         changed = True
         msg = "Outdated ports upgraded successfully"
-        return (changed, msg)
+        return (changed, msg, out, err)
     else:
         module.fail_json(msg="Failed to upgrade outdated ports", stdout=out, stderr=err)
 
@@ -165,7 +165,7 @@ def query_port(module, port_path, name, state="present"):
         return False
 
 
-def remove_ports(module, port_path, ports):
+def remove_ports(module, port_path, ports, stdout, stderr):
     """ Uninstalls one or more ports if installed. """
 
     remove_c = 0
@@ -176,20 +176,21 @@ def remove_ports(module, port_path, ports):
             continue
 
         rc, out, err = module.run_command("%s uninstall %s" % (port_path, port))
-
+        stdout += out
+        stderr += err
         if query_port(module, port_path, port):
-            module.fail_json(msg="Failed to remove %s: %s" % (port, err))
+            module.fail_json(msg="Failed to remove %s: %s" % (port, err), stdout=stdout, stderr=stderr)
 
         remove_c += 1
 
     if remove_c > 0:
 
-        module.exit_json(changed=True, msg="Removed %s port(s)" % remove_c)
+        module.exit_json(changed=True, msg="Removed %s port(s)" % remove_c, stdout=stdout, stderr=stderr)
 
-    module.exit_json(changed=False, msg="Port(s) already absent")
+    module.exit_json(changed=False, msg="Port(s) already absent", stdout=stdout, stderr=stderr)
 
 
-def install_ports(module, port_path, ports, variant):
+def install_ports(module, port_path, ports, variant, stdout, stderr):
     """ Installs one or more ports if not already installed. """
 
     install_c = 0
@@ -199,66 +200,70 @@ def install_ports(module, port_path, ports, variant):
             continue
 
         rc, out, err = module.run_command("%s install %s %s" % (port_path, port, variant))
-
+        stdout += out
+        stderr += err
         if not query_port(module, port_path, port):
-            module.fail_json(msg="Failed to install %s: %s" % (port, err))
+            module.fail_json(msg="Failed to install %s: %s" % (port, err), stdout=stdout, stderr=stderr)
 
         install_c += 1
 
     if install_c > 0:
-        module.exit_json(changed=True, msg="Installed %s port(s)" % (install_c))
+        module.exit_json(changed=True, msg="Installed %s port(s)" % (install_c), stdout=stdout, stderr=stderr)
 
-    module.exit_json(changed=False, msg="Port(s) already present")
+    module.exit_json(changed=False, msg="Port(s) already present", stdout=stdout, stderr=stderr)
 
 
-def activate_ports(module, port_path, ports):
+def activate_ports(module, port_path, ports, stdout, stderr):
     """ Activate a port if it's inactive. """
 
     activate_c = 0
 
     for port in ports:
         if not query_port(module, port_path, port):
-            module.fail_json(msg="Failed to activate %s, port(s) not present" % (port))
+            module.fail_json(msg="Failed to activate %s, port(s) not present" % (port), stdout=stdout, stderr=stderr)
 
         if query_port(module, port_path, port, state="active"):
             continue
 
         rc, out, err = module.run_command("%s activate %s" % (port_path, port))
+        stdout += out
+        stderr += err
 
         if not query_port(module, port_path, port, state="active"):
-            module.fail_json(msg="Failed to activate %s: %s" % (port, err))
+            module.fail_json(msg="Failed to activate %s: %s" % (port, err), stdout=stdout, stderr=stderr)
 
         activate_c += 1
 
     if activate_c > 0:
-        module.exit_json(changed=True, msg="Activated %s port(s)" % (activate_c))
+        module.exit_json(changed=True, msg="Activated %s port(s)" % (activate_c), stdout=stdout, stderr=stderr)
 
-    module.exit_json(changed=False, msg="Port(s) already active")
+    module.exit_json(changed=False, msg="Port(s) already active", stdout=stdout, stderr=stderr)
 
 
-def deactivate_ports(module, port_path, ports):
+def deactivate_ports(module, port_path, ports, stdout, stderr):
     """ Deactivate a port if it's active. """
 
     deactivated_c = 0
 
     for port in ports:
         if not query_port(module, port_path, port):
-            module.fail_json(msg="Failed to deactivate %s, port(s) not present" % (port))
+            module.fail_json(msg="Failed to deactivate %s, port(s) not present" % (port), stdout=stdout, stderr=stderr)
 
         if not query_port(module, port_path, port, state="active"):
             continue
 
         rc, out, err = module.run_command("%s deactivate %s" % (port_path, port))
-
+        stdout += out
+        stderr += err
         if query_port(module, port_path, port, state="active"):
-            module.fail_json(msg="Failed to deactivate %s: %s" % (port, err))
+            module.fail_json(msg="Failed to deactivate %s: %s" % (port, err), stdout=stdout, stderr=stderr)
 
         deactivated_c += 1
 
     if deactivated_c > 0:
-        module.exit_json(changed=True, msg="Deactivated %s port(s)" % (deactivated_c))
+        module.exit_json(changed=True, msg="Deactivated %s port(s)" % (deactivated_c), stdout=stdout, stderr=stderr)
 
-    module.exit_json(changed=False, msg="Port(s) already inactive")
+    module.exit_json(changed=False, msg="Port(s) already inactive", stdout=stdout, stderr=stderr)
 
 
 def main():
@@ -272,35 +277,42 @@ def main():
         )
     )
 
+    stdout = ""
+    stderr = ""
+
     port_path = module.get_bin_path('port', True, ['/opt/local/bin'])
 
     p = module.params
 
     if p["selfupdate"]:
-        (changed, msg) = selfupdate(module, port_path)
+        (changed, msg, out, err) = selfupdate(module, port_path)
+        stdout += out
+        stderr += err
         if not (p["name"] or p["upgrade"]):
-            module.exit_json(changed=changed, msg=msg)
+            module.exit_json(changed=changed, msg=msg, stdout=stdout, stderr=stderr)
 
     if p["upgrade"]:
-        (changed, msg) = upgrade(module, port_path)
+        (changed, msg, out, err) = upgrade(module, port_path)
+        stdout += out
+        stderr += err
         if not p["name"]:
-            module.exit_json(changed=changed, msg=msg)
+            module.exit_json(changed=changed, msg=msg, stdout=stdout, stderr=stderr)
 
     pkgs = p["name"]
 
     variant = p["variant"]
 
     if p["state"] in ["present", "installed"]:
-        install_ports(module, port_path, pkgs, variant)
+        install_ports(module, port_path, pkgs, variant, stdout, stderr)
 
     elif p["state"] in ["absent", "removed"]:
-        remove_ports(module, port_path, pkgs)
+        remove_ports(module, port_path, pkgs, stdout, stderr)
 
     elif p["state"] == "active":
-        activate_ports(module, port_path, pkgs)
+        activate_ports(module, port_path, pkgs, stdout, stderr)
 
     elif p["state"] == "inactive":
-        deactivate_ports(module, port_path, pkgs)
+        deactivate_ports(module, port_path, pkgs, stdout, stderr)
 
 
 if __name__ == '__main__':


Test patch: