Man page clean-up (#1508)

Ensure we don’t include dynamic content in the static man pages.
This commit is contained in:
Jakub Roztocil 2023-05-22 11:56:30 -07:00 committed by GitHub
parent c2677eeccf
commit 2da955fb06
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 67 additions and 48 deletions

View File

@ -1,27 +1,22 @@
name: Update Autogenerated Files name: Update Generated Content
on: on:
push: push:
branches: branches:
- master - master
jobs: jobs:
regen-autogenerated-files: update-content:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-python@v4 - uses: actions/setup-python@v4
with: with:
python-version: 3.9 python-version: 3.9
- run: make content
- run: make regen-all
- name: Create Pull Request - name: Create Pull Request
id: cpr id: cpr
uses: peter-evans/create-pull-request@v4 uses: peter-evans/create-pull-request@v4
with: with:
commit-message: "[automated] Update auto-generated files" commit-message: "[automated] Update generated content"
title: "[automated] Update auto-generated files" title: "[automated] Update generated content"
delete-branch: true delete-branch: true
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -231,15 +231,15 @@ brew-test:
brew audit --strict httpie brew audit --strict httpie
############################################################################### ###############################################################################
# Regeneration # Generated content
############################################################################### ###############################################################################
regen-all: regen-man-pages regen-install-methods content: man installation-docs
regen-man-pages: install man: install
@echo $(H1)Regenerate man pages$(H1END) @echo $(H1)Regenerate man pages$(H1END)
$(VENV_PYTHON) extras/scripts/generate_man_pages.py $(VENV_PYTHON) extras/scripts/generate_man_pages.py
regen-install-methods: installation-docs:
@echo $(H1)Updating installation instructions in the docs$(H1END) @echo $(H1)Updating installation instructions in the docs$(H1END)
$(VENV_PYTHON) docs/installation/generate.py $(VENV_PYTHON) docs/installation/generate.py

View File

@ -399,7 +399,7 @@ The authentication mechanism to be used. Defaults to \[dq]basic\[dq].
\[dq]bearer\[dq]: Bearer HTTP Auth \[dq]bearer\[dq]: Bearer HTTP Auth
For finding out all available authentication types in your system, try: To see all available auth types on your system, including ones installed via plugins, run:
$ http \fB\,--auth-type\/\fR $ http \fB\,--auth-type\/\fR
@ -510,11 +510,10 @@ are shown here).
A string in the OpenSSL cipher list format. By default, the following A string in the OpenSSL cipher list format.
ciphers are used on your system:
TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES256-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA
See `http \fB\,--help\/\fR` for the default ciphers list on you system.
.IP "\fB\,--cert\/\fR" .IP "\fB\,--cert\/\fR"

View File

@ -399,7 +399,7 @@ The authentication mechanism to be used. Defaults to \[dq]basic\[dq].
\[dq]bearer\[dq]: Bearer HTTP Auth \[dq]bearer\[dq]: Bearer HTTP Auth
For finding out all available authentication types in your system, try: To see all available auth types on your system, including ones installed via plugins, run:
$ http \fB\,--auth-type\/\fR $ http \fB\,--auth-type\/\fR
@ -509,12 +509,10 @@ are shown here).
.IP "\fB\,--ciphers\/\fR" .IP "\fB\,--ciphers\/\fR"
A string in the OpenSSL cipher list format.
A string in the OpenSSL cipher list format. By default, the following
ciphers are used on your system:
TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES256-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA
See `http \fB\,--help\/\fR` for the default ciphers list on you system.
.IP "\fB\,--cert\/\fR" .IP "\fB\,--cert\/\fR"

View File

@ -1,20 +1,25 @@
import os
import re import re
from contextlib import contextmanager from contextlib import contextmanager
from pathlib import Path from pathlib import Path
from typing import Optional, Iterator, Iterable from typing import Optional, Iterator, Iterable
# So that httpie.cli.definition can provide man-page-specific output. Must be set before importing httpie.
os.environ['HTTPIE_BUILDING_MAN_PAGES'] = '1'
import httpie import httpie
from httpie.cli.definition import options as core_options from httpie.cli.definition import options as core_options, IS_MAN_PAGE
from httpie.cli.options import ParserSpec from httpie.cli.options import ParserSpec
from httpie.manager.cli import options as manager_options from httpie.manager.cli import options as manager_options
from httpie.output.ui.rich_help import OptionsHighlighter, to_usage from httpie.output.ui.rich_help import OptionsHighlighter, to_usage
from httpie.output.ui.rich_utils import render_as_string from httpie.output.ui.rich_utils import render_as_string
from httpie.utils import split_iterable
# Escape certain characters so they are rendered properly on assert IS_MAN_PAGE, 'CLI definition does not understand were building man pages'
# all terminals.
# https://man7.org/linux/man-pages/man7/groff_char.7.html # Escape certain characters, so they are rendered properly on all terminals.
# <https://man7.org/linux/man-pages/man7/groff_char.7.html>
ESCAPE_MAP = { ESCAPE_MAP = {
'"': '\[dq]', '"': '\[dq]',
"'": '\[aq]', "'": '\[aq]',
@ -32,6 +37,7 @@ OPTION_HIGHLIGHT_RE = re.compile(
OptionsHighlighter.highlights[0] OptionsHighlighter.highlights[0]
) )
class ManPageBuilder: class ManPageBuilder:
def __init__(self): def __init__(self):
self.source = [] self.source = []
@ -125,7 +131,7 @@ def to_man_page(program_name: str, spec: ParserSpec, *, is_top_level_cmd: bool =
with builder.section('SYNOPSIS'): with builder.section('SYNOPSIS'):
# `http` and `https` are commands that can be directly used, so they can have # `http` and `https` are commands that can be directly used, so they can have
# have a valid usage. But `httpie` is a top-level command with multiple sub commands, # a valid usage. But `httpie` is a top-level command with multiple sub commands,
# so for the synopsis we'll only reference the `httpie` name. # so for the synopsis we'll only reference the `httpie` name.
if is_top_level_cmd: if is_top_level_cmd:
synopsis = program_name synopsis = program_name
@ -153,7 +159,7 @@ def to_man_page(program_name: str, spec: ParserSpec, *, is_top_level_cmd: bool =
if raw_arg.get('is_positional'): if raw_arg.get('is_positional'):
# In case of positional arguments, metavar is always equal # In case of positional arguments, metavar is always equal
# to the list of options (e.g `METHOD`). # to the list of options (e.g `METHOD`).
metavar = None metavar = None
builder.add_options(raw_arg['options'], metavar=metavar) builder.add_options(raw_arg['options'], metavar=metavar)
desc = builder.format_desc(raw_arg.get('description', '')) desc = builder.format_desc(raw_arg.get('description', ''))
@ -178,6 +184,5 @@ def main() -> None:
stream.write(to_man_page(program_name, spec, **config)) stream.write(to_man_page(program_name, spec, **config))
if __name__ == '__main__': if __name__ == '__main__':
main() main()

View File

@ -1,5 +1,6 @@
from __future__ import annotations from __future__ import annotations
import os
import textwrap import textwrap
from argparse import FileType from argparse import FileType
@ -22,6 +23,12 @@ from httpie.plugins.builtin import BuiltinAuthPlugin
from httpie.plugins.registry import plugin_manager from httpie.plugins.registry import plugin_manager
from httpie.ssl_ import AVAILABLE_SSL_VERSION_ARG_MAPPING, DEFAULT_SSL_CIPHERS_STRING from httpie.ssl_ import AVAILABLE_SSL_VERSION_ARG_MAPPING, DEFAULT_SSL_CIPHERS_STRING
# Man pages are static (built when making a release).
# We use this check to not include generated, system-specific information there (e.g., default --ciphers).
IS_MAN_PAGE = bool(os.environ.get('HTTPIE_BUILDING_MAN_PAGES'))
options = ParserSpec( options = ParserSpec(
'http', 'http',
description=f'{__doc__.strip()} <https://httpie.io>', description=f'{__doc__.strip()} <https://httpie.io>',
@ -35,7 +42,6 @@ options = ParserSpec(
source_file=__file__ source_file=__file__
) )
####################################################################### #######################################################################
# Positional arguments. # Positional arguments.
####################################################################### #######################################################################
@ -234,6 +240,7 @@ processing_options.add_argument(
""", """,
) )
####################################################################### #######################################################################
# Output processing # Output processing
####################################################################### #######################################################################
@ -610,6 +617,7 @@ sessions.add_argument(
""", """,
) )
####################################################################### #######################################################################
# Authentication # Authentication
####################################################################### #######################################################################
@ -630,7 +638,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
if issubclass(auth_plugin, BuiltinAuthPlugin) if issubclass(auth_plugin, BuiltinAuthPlugin)
] ]
text += '\n' text += '\n'
text += 'For finding out all available authentication types in your system, try:\n\n' text += 'To see all available auth types on your system, including ones installed via plugins, run:\n\n'
text += ' $ http --auth-type' text += ' $ http --auth-type'
auth_types = '\n\n '.join( auth_types = '\n\n '.join(
@ -646,7 +654,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
'' ''
if not plugin.description if not plugin.description
else '\n ' else '\n '
+ ('\n '.join(textwrap.wrap(plugin.description))) + ('\n '.join(textwrap.wrap(plugin.description)))
), ),
) )
for plugin in auth_plugins for plugin in auth_plugins
@ -826,23 +834,36 @@ ssl.add_argument(
""", """,
) )
CIPHERS_CURRENT_DEFAULTS = (
"""
See `http --help` for the default ciphers list on you system.
"""
if IS_MAN_PAGE else
f"""
By default, the following ciphers are used on your system:
{DEFAULT_SSL_CIPHERS_STRING}
"""
)
ssl.add_argument( ssl.add_argument(
'--ciphers', '--ciphers',
short_help='A string in the OpenSSL cipher list format.', short_help='A string in the OpenSSL cipher list format.',
help=f""" help=f"""
A string in the OpenSSL cipher list format. By default, the following A string in the OpenSSL cipher list format.
ciphers are used on your system:
{DEFAULT_SSL_CIPHERS_STRING} {CIPHERS_CURRENT_DEFAULTS}
""", """
) )
ssl.add_argument( ssl.add_argument(
'--cert', '--cert',
default=None, default=None,
type=readable_file_arg, type=readable_file_arg,
short_help='Specifys a local cert to use as client side SSL certificate.', short_help='Specifies a local cert to use as the client-side SSL certificate.',
help=""" help="""
You can specify a local cert to use as client side SSL certificate. You can specify a local cert to use as client side SSL certificate.
This file may either contain both private key and certificate or you may This file may either contain both private key and certificate or you may

View File

@ -4,23 +4,22 @@ import subprocess
import os import os
from httpie.context import Environment from httpie.context import Environment
MAN_COMMAND = 'man' MAN_COMMAND = 'man'
NO_MAN_PAGES = os.getenv('HTTPIE_NO_MAN_PAGES', False) NO_MAN_PAGES = os.getenv('HTTPIE_NO_MAN_PAGES', False)
# On some systems, HTTP(n) might exist but we are only # On some systems, HTTP(n) might exist, but we are only interested in HTTP(1).
# interested in HTTP(1). # For more information on man page sections: <https://unix.stackexchange.com/a/138643>
#
# For more information on man page sections: https://unix.stackexchange.com/a/138643
MAN_PAGE_SECTION = '1' MAN_PAGE_SECTION = '1'
def is_available(program: str) -> bool: def is_available(program: str) -> bool:
"""Check whether HTTPie's man pages are available in this system.""" """
Check whether `program`'s man pages are available on this system.
"""
if NO_MAN_PAGES or os.system == 'nt': if NO_MAN_PAGES or os.system == 'nt':
return False return False
try: try:
process = subprocess.run( process = subprocess.run(
[MAN_COMMAND, MAN_PAGE_SECTION, program], [MAN_COMMAND, MAN_PAGE_SECTION, program],
@ -29,7 +28,7 @@ def is_available(program: str) -> bool:
stderr=subprocess.DEVNULL stderr=subprocess.DEVNULL
) )
except Exception: except Exception:
# There might be some errors outside of the process, e.g # There might be some errors outside the process, e.g
# a permission error to execute something that is not an # a permission error to execute something that is not an
# executable. # executable.
return False return False
@ -38,8 +37,10 @@ def is_available(program: str) -> bool:
def display_for(env: Environment, program: str) -> None: def display_for(env: Environment, program: str) -> None:
"""Display the man page for the given command (http/https).""" """
Open the system man page for the given command (http/https/httpie).
"""
subprocess.run( subprocess.run(
[MAN_COMMAND, MAN_PAGE_SECTION, program], [MAN_COMMAND, MAN_PAGE_SECTION, program],
stdout=env.stdout, stdout=env.stdout,