mirror of https://github.com/containers/podman.git
Merge pull request #16170 from edsantiago/manpage_generic_include
[CI:DOCS] markdown-preprocess: add generic include mechanism
This commit is contained in:
commit
8fef5eb12c
|
@ -233,7 +233,7 @@ Pods removed:
|
||||||
`podman kube play --down` will not work with a URL if the YAML file the URL points to
|
`podman kube play --down` will not work with a URL if the YAML file the URL points to
|
||||||
has been changed or altered.
|
has been changed or altered.
|
||||||
|
|
||||||
@@option tls-verify
|
@@include ../../kubernetes_support.md
|
||||||
|
|
||||||
## SEE ALSO
|
## SEE ALSO
|
||||||
**[podman(1)](podman.1.md)**, **[podman-kube(1)](podman-kube.1.md)**, **[podman-kube-down(1)](podman-kube-down.1.md)**, **[podman-network-create(1)](podman-network-create.1.md)**, **[podman-kube-generate(1)](podman-kube-generate.1.md)**, **[containers-certs.d(5)](https://github.com/containers/image/blob/main/docs/containers-certs.d.5.md)**
|
**[podman(1)](podman.1.md)**, **[podman-kube(1)](podman-kube.1.md)**, **[podman-kube-down(1)](podman-kube-down.1.md)**, **[podman-network-create(1)](podman-network-create.1.md)**, **[podman-kube-generate(1)](podman-kube-generate.1.md)**, **[containers-certs.d(5)](https://github.com/containers/image/blob/main/docs/containers-certs.d.5.md)**
|
||||||
|
|
|
@ -2,83 +2,97 @@
|
||||||
#
|
#
|
||||||
# markdown-preprocess - filter *.md.in files, convert to .md
|
# markdown-preprocess - filter *.md.in files, convert to .md
|
||||||
#
|
#
|
||||||
|
"""
|
||||||
|
Simpleminded include mechanism for podman man pages.
|
||||||
|
"""
|
||||||
|
|
||||||
import glob
|
import glob
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
def main():
|
class Preprocessor():
|
||||||
script_dir = os.path.abspath(os.path.dirname(__file__))
|
"""
|
||||||
man_dir = os.path.join(script_dir,"../docs/source/markdown")
|
Doesn't really merit a whole OO approach, except we have a lot
|
||||||
|
of state variables to pass around, and self is a convenient
|
||||||
|
way to do that. Better than globals, anyway.
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
self.infile = ''
|
||||||
|
self.pod_or_container = ''
|
||||||
|
|
||||||
try:
|
def process(self, infile:str):
|
||||||
os.chdir(man_dir)
|
"""
|
||||||
except FileNotFoundError:
|
Main calling point: preprocesses one file
|
||||||
raise Exception("Please invoke me from the base repo dir")
|
"""
|
||||||
|
self.infile = infile
|
||||||
# If called with args, process only those files
|
|
||||||
infiles = [ os.path.basename(x) for x in sys.argv[1:] ]
|
|
||||||
if len(infiles) == 0:
|
|
||||||
# Called without args: process all *.md.in files
|
|
||||||
infiles = glob.glob('*.md.in')
|
|
||||||
for infile in infiles:
|
|
||||||
process(infile)
|
|
||||||
|
|
||||||
def process(infile):
|
|
||||||
# Some options are the same between containers and pods; determine
|
# Some options are the same between containers and pods; determine
|
||||||
# which description to use from the name of the source man page.
|
# which description to use from the name of the source man page.
|
||||||
pod_or_container = 'container'
|
self.pod_or_container = 'container'
|
||||||
if '-pod-' in infile or '-kube-' in infile:
|
if '-pod-' in infile or '-kube-' in infile:
|
||||||
pod_or_container = 'pod'
|
self.pod_or_container = 'pod'
|
||||||
|
|
||||||
# foo.md.in -> foo.md -- but always write to a tmpfile
|
# foo.md.in -> foo.md -- but always write to a tmpfile
|
||||||
outfile = os.path.splitext(infile)[0]
|
outfile = os.path.splitext(infile)[0]
|
||||||
outfile_tmp = outfile + '.tmp.' + str(os.getpid())
|
outfile_tmp = outfile + '.tmp.' + str(os.getpid())
|
||||||
|
|
||||||
# print("got here: ",infile, " -> ", outfile)
|
|
||||||
|
|
||||||
with open(infile, 'r') as fh_in, open(outfile_tmp, 'w') as fh_out:
|
with open(infile, 'r') as fh_in, open(outfile_tmp, 'w') as fh_out:
|
||||||
for line in fh_in:
|
for line in fh_in:
|
||||||
# '@@option foo' -> include file options/foo.md
|
# '@@option foo' -> include file options/foo.md
|
||||||
if line.startswith('@@option '):
|
if line.startswith('@@option '):
|
||||||
_, optionname = line.strip().split(" ")
|
_, optionname = line.strip().split(" ")
|
||||||
optionfile = os.path.join("options", optionname + '.md')
|
optionfile = os.path.join("options", optionname + '.md')
|
||||||
|
self.insert_file(fh_out, optionfile)
|
||||||
# Comment intended to help someone viewing the .md file.
|
# '@@include relative-path/must-exist.md'
|
||||||
# Leading newline is important because if two lines are
|
elif line.startswith('@@include '):
|
||||||
# consecutive without a break, sphinx (but not go-md2man)
|
_, path = line.strip().split(" ")
|
||||||
# treats them as one line and will unwantedly render the
|
self.insert_file(fh_out, path)
|
||||||
# comment in its output.
|
|
||||||
fh_out.write("\n[//]: # (BEGIN included file " + optionfile + ")\n")
|
|
||||||
with open(optionfile, 'r') as fh_optfile:
|
|
||||||
for opt_line in fh_optfile:
|
|
||||||
opt_line = replace_type(opt_line, pod_or_container)
|
|
||||||
opt_line = opt_line.replace('<<subcommand>>', podman_subcommand(infile))
|
|
||||||
opt_line = opt_line.replace('<<fullsubcommand>>', podman_subcommand(infile, 'full'))
|
|
||||||
fh_out.write(opt_line)
|
|
||||||
fh_out.write("\n[//]: # (END included file " + optionfile + ")\n")
|
|
||||||
else:
|
else:
|
||||||
fh_out.write(line)
|
fh_out.write(line)
|
||||||
|
|
||||||
os.chmod(outfile_tmp, 0o444)
|
os.chmod(outfile_tmp, 0o444)
|
||||||
os.rename(outfile_tmp, outfile)
|
os.rename(outfile_tmp, outfile)
|
||||||
|
|
||||||
# Given a file path of the form podman-foo-bar.1.md.in, return "foo bar"
|
def insert_file(self, fh_out, path: str):
|
||||||
def podman_subcommand(string: str, full=None) -> str:
|
"""
|
||||||
|
Reads one option file, writes it out to the given output filehandle
|
||||||
|
"""
|
||||||
|
# Comment intended to help someone viewing the .md file.
|
||||||
|
# Leading newline is important because if two lines are
|
||||||
|
# consecutive without a break, sphinx (but not go-md2man)
|
||||||
|
# treats them as one line and will unwantedly render the
|
||||||
|
# comment in its output.
|
||||||
|
fh_out.write("\n[//]: # (BEGIN included file " + path + ")\n")
|
||||||
|
with open(path, 'r') as fh_included:
|
||||||
|
for opt_line in fh_included:
|
||||||
|
opt_line = self.replace_type(opt_line)
|
||||||
|
opt_line = opt_line.replace('<<subcommand>>', self.podman_subcommand())
|
||||||
|
opt_line = opt_line.replace('<<fullsubcommand>>', self.podman_subcommand('full'))
|
||||||
|
fh_out.write(opt_line)
|
||||||
|
fh_out.write("\n[//]: # (END included file " + path + ")\n")
|
||||||
|
|
||||||
|
def podman_subcommand(self, full=None) -> str:
|
||||||
|
"""
|
||||||
|
Returns the string form of the podman command, based on man page name;
|
||||||
|
e.g., 'foo bar' for podman-foo-bar.1.md.in
|
||||||
|
"""
|
||||||
|
subcommand = self.infile
|
||||||
# Special case: 'podman-pod-start' becomes just 'start'
|
# Special case: 'podman-pod-start' becomes just 'start'
|
||||||
if not full:
|
if not full:
|
||||||
if string.startswith("podman-pod-"):
|
if subcommand.startswith("podman-pod-"):
|
||||||
string = string[len("podman-pod-"):]
|
subcommand = subcommand[len("podman-pod-"):]
|
||||||
if string.startswith("podman-"):
|
if subcommand.startswith("podman-"):
|
||||||
string = string[len("podman-"):]
|
subcommand = subcommand[len("podman-"):]
|
||||||
if string.endswith(".1.md.in"):
|
if subcommand.endswith(".1.md.in"):
|
||||||
string = string[:-len(".1.md.in")]
|
subcommand = subcommand[:-len(".1.md.in")]
|
||||||
return string.replace("-", " ")
|
return subcommand.replace("-", " ")
|
||||||
|
|
||||||
# Replace instances of '<<pod|container>>' with the desired one (based on
|
def replace_type(self, line: str) -> str:
|
||||||
# 'type' which is 'pod' or 'container').
|
"""
|
||||||
def replace_type(line: str, type: str) -> str:
|
Replace instances of '<<pod string|container string>>' with the
|
||||||
|
appropriate one based on whether this is a pod-related man page
|
||||||
|
or not.
|
||||||
|
"""
|
||||||
# Internal helper function: determines the desired half of the <a|b> string
|
# Internal helper function: determines the desired half of the <a|b> string
|
||||||
def replwith(matchobj):
|
def replwith(matchobj):
|
||||||
lhs, rhs = matchobj[0].split('|')
|
lhs, rhs = matchobj[0].split('|')
|
||||||
|
@ -93,21 +107,41 @@ def replace_type(line: str, type: str) -> str:
|
||||||
# <<container in pod|container>>.
|
# <<container in pod|container>>.
|
||||||
if re.match('.*pod([^m]|$)', lhs, re.IGNORECASE):
|
if re.match('.*pod([^m]|$)', lhs, re.IGNORECASE):
|
||||||
if re.match('.*pod([^m]|$)', rhs, re.IGNORECASE):
|
if re.match('.*pod([^m]|$)', rhs, re.IGNORECASE):
|
||||||
raise Exception("'%s' matches 'pod' in both left and right sides" % matchobj[0])
|
raise Exception(f"'{matchobj[0]}' matches 'pod' in both left and right sides")
|
||||||
# Only left-hand side has "pod"
|
# Only left-hand side has "pod"
|
||||||
if type == 'pod':
|
if self.pod_or_container == 'pod':
|
||||||
return lhs
|
return lhs
|
||||||
else:
|
|
||||||
return rhs
|
return rhs
|
||||||
else:
|
|
||||||
|
# 'pod' not in lhs, must be in rhs
|
||||||
if not re.match('.*pod([^m]|$)', rhs, re.IGNORECASE):
|
if not re.match('.*pod([^m]|$)', rhs, re.IGNORECASE):
|
||||||
raise Exception("'%s' does not match 'pod' in either side" % matchobj[0])
|
raise Exception(f"'{matchobj[0]}' does not match 'pod' in either side")
|
||||||
if type == 'pod':
|
if self.pod_or_container == 'pod':
|
||||||
return rhs
|
return rhs
|
||||||
else:
|
|
||||||
return lhs
|
return lhs
|
||||||
|
|
||||||
return re.sub('<<[^\|>]*\|[^\|>]*>>', replwith, line)
|
return re.sub(r'<<[^\|>]*\|[^\|>]*>>', replwith, line)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"script entry point"
|
||||||
|
script_dir = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
man_dir = os.path.join(script_dir,"../docs/source/markdown")
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.chdir(man_dir)
|
||||||
|
except FileNotFoundError as ex:
|
||||||
|
raise Exception("Please invoke me from the base repo dir") from ex
|
||||||
|
|
||||||
|
# If called with args, process only those files
|
||||||
|
infiles = [ os.path.basename(x) for x in sys.argv[1:] ]
|
||||||
|
if len(infiles) == 0:
|
||||||
|
# Called without args: process all *.md.in files
|
||||||
|
infiles = glob.glob('*.md.in')
|
||||||
|
|
||||||
|
preprocessor = Preprocessor()
|
||||||
|
for infile in infiles:
|
||||||
|
preprocessor.process(infile)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -14,65 +14,73 @@ spec = spec_from_loader("mp", SourceFileLoader("mp", "hack/markdown-preprocess")
|
||||||
mp = module_from_spec(spec)
|
mp = module_from_spec(spec)
|
||||||
spec.loader.exec_module(mp)
|
spec.loader.exec_module(mp)
|
||||||
|
|
||||||
|
pp = mp.Preprocessor()
|
||||||
|
|
||||||
class TestPodReplacer(unittest.TestCase):
|
class TestPodReplacer(unittest.TestCase):
|
||||||
|
def check_4_way(self, containerstring: str, podstring: str):
|
||||||
|
types = ['container', 'pod']
|
||||||
|
strings = [ containerstring, podstring ]
|
||||||
|
for i in 0, 1:
|
||||||
|
pp.pod_or_container = types[i]
|
||||||
|
for j in 0, 1:
|
||||||
|
s = '<<' + strings[j] + '|' + strings[(j+1)%2] + '>>'
|
||||||
|
self.assertEqual(pp.replace_type(s), strings[i])
|
||||||
|
|
||||||
def test_basic(self):
|
def test_basic(self):
|
||||||
"""basic pod|container and vice-versa"""
|
"""basic pod|container and vice-versa"""
|
||||||
s = '<<container|pod>>'
|
self.check_4_way('container', 'pod')
|
||||||
self.assertEqual(mp.replace_type(s, 'pod'), 'pod')
|
|
||||||
self.assertEqual(mp.replace_type(s, 'container'), 'container')
|
|
||||||
s = '<<container|pod>>'
|
|
||||||
self.assertEqual(mp.replace_type(s, 'pod'), 'pod')
|
|
||||||
self.assertEqual(mp.replace_type(s, 'container'), 'container')
|
|
||||||
|
|
||||||
def test_case_insensitive(self):
|
def test_case_insensitive(self):
|
||||||
"""test case-insensitive replacement of Pod, Container"""
|
"""test case-insensitive replacement of Pod, Container"""
|
||||||
s = '<<Pod|Container>>'
|
self.check_4_way('Container', 'Pod')
|
||||||
self.assertEqual(mp.replace_type(s, 'pod'), 'Pod')
|
|
||||||
self.assertEqual(mp.replace_type(s, 'container'), 'Container')
|
|
||||||
s = '<<Container|Pod>>'
|
|
||||||
self.assertEqual(mp.replace_type(s, 'pod'), 'Pod')
|
|
||||||
self.assertEqual(mp.replace_type(s, 'container'), 'Container')
|
|
||||||
|
|
||||||
def test_dont_care_about_podman(self):
|
def test_dont_care_about_podman(self):
|
||||||
"""we ignore 'podman'"""
|
"""we ignore 'podman'"""
|
||||||
self.assertEqual(mp.replace_type('<<podman container|pod in podman>>', 'container'), 'podman container')
|
self.check_4_way('podman container', 'pod in podman')
|
||||||
|
|
||||||
def test_not_at_beginning(self):
|
def test_not_at_beginning(self):
|
||||||
"""oops - test for 'pod' other than at beginning of string"""
|
"""oops - test for 'pod' other than at beginning of string"""
|
||||||
s = '<<container|container or pod>>'
|
self.check_4_way('container', 'container or pod')
|
||||||
self.assertEqual(mp.replace_type(s, 'container'), 'container')
|
|
||||||
self.assertEqual(mp.replace_type(s, 'pod'), 'container or pod')
|
|
||||||
s = '<<container or pod|container>>'
|
|
||||||
self.assertEqual(mp.replace_type(s, 'container'), 'container')
|
|
||||||
self.assertEqual(mp.replace_type(s, 'pod'), 'container or pod')
|
|
||||||
|
|
||||||
def test_blank(self):
|
def test_blank(self):
|
||||||
"""test that either side of '|' can be empty"""
|
"""test that either side of '|' can be empty"""
|
||||||
s = 'abc container<<| or pod>> def'
|
s_lblank = 'abc container<<| or pod>> def'
|
||||||
self.assertEqual(mp.replace_type(s, 'container'), 'abc container def')
|
s_rblank = 'abc container<< or pod|>> def'
|
||||||
self.assertEqual(mp.replace_type(s, 'pod'), 'abc container or pod def')
|
|
||||||
s = 'abc container<< or pod|>> def'
|
pp.pod_or_container = 'container'
|
||||||
self.assertEqual(mp.replace_type(s, 'container'), 'abc container def')
|
self.assertEqual(pp.replace_type(s_lblank), 'abc container def')
|
||||||
self.assertEqual(mp.replace_type(s, 'pod'), 'abc container or pod def')
|
self.assertEqual(pp.replace_type(s_rblank), 'abc container def')
|
||||||
|
|
||||||
|
pp.pod_or_container = 'pod'
|
||||||
|
self.assertEqual(pp.replace_type(s_lblank), 'abc container or pod def')
|
||||||
|
self.assertEqual(pp.replace_type(s_rblank), 'abc container or pod def')
|
||||||
|
|
||||||
def test_exception_both(self):
|
def test_exception_both(self):
|
||||||
"""test that 'pod' on both sides raises exception"""
|
"""test that 'pod' on both sides raises exception"""
|
||||||
|
for word in ['pod', 'container']:
|
||||||
|
pp.pod_or_container = word
|
||||||
with self.assertRaisesRegex(Exception, "in both left and right sides"):
|
with self.assertRaisesRegex(Exception, "in both left and right sides"):
|
||||||
mp.replace_type('<<pod 123|pod 321>>', 'pod')
|
pp.replace_type('<<pod 123|pod 321>>')
|
||||||
|
|
||||||
def test_exception_neither(self):
|
def test_exception_neither(self):
|
||||||
"""test that 'pod' on neither side raises exception"""
|
"""test that 'pod' on neither side raises exception"""
|
||||||
|
for word in ['pod', 'container']:
|
||||||
|
pp.pod_or_container = word
|
||||||
with self.assertRaisesRegex(Exception, "in either side"):
|
with self.assertRaisesRegex(Exception, "in either side"):
|
||||||
mp.replace_type('<<container 123|container 321>>', 'pod')
|
pp.replace_type('<<container 123|container 321>>')
|
||||||
|
|
||||||
class TestPodmanSubcommand(unittest.TestCase):
|
class TestPodmanSubcommand(unittest.TestCase):
|
||||||
def test_basic(self):
|
def test_basic(self):
|
||||||
"""podman subcommand basic test"""
|
"""podman subcommand basic test"""
|
||||||
self.assertEqual(mp.podman_subcommand("podman-foo.1.md.in"), "foo")
|
pp.infile = 'podman-foo.1.md.in'
|
||||||
self.assertEqual(mp.podman_subcommand("podman-foo-bar.1.md.in"), "foo bar")
|
self.assertEqual(pp.podman_subcommand(), "foo")
|
||||||
self.assertEqual(mp.podman_subcommand("podman-pod-rm.1.md.in"), "rm")
|
|
||||||
self.assertEqual(mp.podman_subcommand("podman-pod-rm.1.md.in", "full"), "pod rm")
|
|
||||||
|
|
||||||
|
pp.infile = 'podman-foo-bar.1.md.in'
|
||||||
|
self.assertEqual(pp.podman_subcommand(), "foo bar")
|
||||||
|
|
||||||
|
pp.infile = 'podman-pod-rm.1.md.in'
|
||||||
|
self.assertEqual(pp.podman_subcommand(), "rm")
|
||||||
|
self.assertEqual(pp.podman_subcommand("full"), "pod rm")
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
Loading…
Reference in New Issue