Repository: aws/aws-cli (Python)

Issue:
Add support for mandoc
### Describe the feature

Linux distros and BSD systems that use `mandoc` instead of `groff` should be supported to display 'help' documentation without the need for installing additional sofware. 

### Use Case

'Help' docs should be able to be displayed on Linux distros and BSD systems that don't use `groff`.

### Proposed Solution

PR #2635 adds support for this feature request.

### Other Information

_No response_

### Acknowledgements

- [ ] I may be able to implement this feature request
- [ ] This feature might incur a breaking change

### CLI version used

2.6.2

### Environment details (OS name and version, etc.)

aws-cli/2.6.2 Python/3.9.11 Darwin/20.6.0 exe/x86_64 prompt/off


Discussion:
i fixed the merge conflict of the original pr as #7406 
### ⚠️COMMENT VISIBILITY WARNING⚠️ 
Comments on closed issues are hard for our team to see. 
If you need more assistance, please open a new issue that references this one. If you wish to keep having a conversation with other community members under this issue feel free to do so.



Patch:
diff --git a/.changes/next-release/enhancement-docs-15168.json b/.changes/next-release/enhancement-docs-15168.json
new file mode 100644
index 000000000000..48545bf527c9
--- /dev/null
+++ b/.changes/next-release/enhancement-docs-15168.json
@@ -0,0 +1,5 @@
+{
+  "type": "enhancement",
+  "category": "docs",
+  "description": "Fixes `#6918 <https://github.com/aws/aws-cli/issues/6918>`__ and `#7400 <https://github.com/aws/aws-cli/issues/7400>`__. The CLI falls back on mandoc if groff isn't available."
+}
diff --git a/awscli/help.py b/awscli/help.py
index 0ab6328fb4bc..f2ef22b99b7d 100644
--- a/awscli/help.py
+++ b/awscli/help.py
@@ -106,13 +106,16 @@ class PosixHelpRenderer(PagingHelpRenderer):
 
     def _convert_doc_content(self, contents):
         man_contents = publish_string(contents, writer=manpage.Writer())
-        if not self._exists_on_path('groff'):
-            raise ExecutableNotFoundError('groff')
-        cmdline = ['groff', '-m', 'man', '-T', 'ascii']
+        if self._exists_on_path('groff'):
+            cmdline = ['groff', '-m', 'man', '-T', 'ascii']
+        elif self._exists_on_path('mandoc'):
+            cmdline = ['mandoc', '-T', 'ascii']
+        else:
+            raise ExecutableNotFoundError('groff or mandoc')
         LOG.debug("Running command: %s", cmdline)
         p3 = self._popen(cmdline, stdin=PIPE, stdout=PIPE, stderr=PIPE)
-        groff_output = p3.communicate(input=man_contents)[0]
-        return groff_output
+        output = p3.communicate(input=man_contents)[0]
+        return output
 
     def _send_output_to_pager(self, output):
         cmdline = self.get_pager_cmdline()


Test patch:
diff --git a/tests/unit/test_help.py b/tests/unit/test_help.py
index 3861b4601085..8727309cfccb 100644
--- a/tests/unit/test_help.py
+++ b/tests/unit/test_help.py
@@ -96,13 +96,25 @@ def test_pager_with_args(self):
                          pager_cmd.split())
 
     @skip_if_windows('Requires posix system.')
-    def test_no_groff_exists(self):
+    def test_no_groff_or_mandoc_exists(self):
         renderer = FakePosixHelpRenderer()
         renderer.exists_on_path['groff'] = False
-        expected_error = 'Could not find executable named "groff"'
+        renderer.exists_on_path['mandoc'] = False
+        expected_error = 'Could not find executable named "groff or mandoc"'
         with self.assertRaisesRegex(ExecutableNotFoundError, expected_error):
             renderer.render('foo')
 
+    @skip_if_windows('Requires POSIX system.')
+    def test_renderer_falls_back_to_mandoc(self):
+        stdout = six.StringIO()
+        renderer = FakePosixHelpRenderer(output_stream=stdout)
+
+        renderer.exists_on_path['groff'] = False
+        renderer.exists_on_path['mandoc'] = True
+        renderer.mock_popen.communicate.return_value = (b'foo', '')
+        renderer.render('foo')
+        self.assertEqual(stdout.getvalue(), 'foo\n')
+
     @skip_if_windows('Requires POSIX system.')
     def test_no_pager_exists(self):
         fake_pager = 'foobar'