summaryrefslogtreecommitdiff
path: root/hack
diff options
context:
space:
mode:
Diffstat (limited to 'hack')
-rwxr-xr-xhack/markdown-preprocess62
-rwxr-xr-xhack/markdown-preprocess-review132
-rwxr-xr-xhack/markdown-preprocess.t58
3 files changed, 234 insertions, 18 deletions
diff --git a/hack/markdown-preprocess b/hack/markdown-preprocess
index 4bc67a819..86667a32d 100755
--- a/hack/markdown-preprocess
+++ b/hack/markdown-preprocess
@@ -5,6 +5,7 @@
import glob
import os
+import re
import sys
def main():
@@ -28,11 +29,11 @@ def process(infile):
# Some options are the same between containers and pods; determine
# which description to use from the name of the source man page.
pod_or_container = 'container'
- if '-pod-' in infile:
+ if '-pod-' in infile or '-kube-' in infile:
pod_or_container = 'pod'
# Sometimes a man page includes the subcommand.
- subcommand = removesuffix(removeprefix(infile,"podman-"),".1.md.in").replace("-", " ")
+ subcommand = podman_subcommand(infile)
# foo.md.in -> foo.md -- but always write to a tmpfile
outfile = os.path.splitext(infile)[0]
@@ -55,8 +56,8 @@ def process(infile):
fh_out.write("\n[//]: # (BEGIN included file " + optionfile + ")\n")
with open(optionfile, 'r') as fh_optfile:
for opt_line in fh_optfile:
- opt_line = opt_line.replace('<POD-OR-CONTAINER>', pod_or_container)
- opt_line = opt_line.replace('<SUBCOMMAND>', subcommand)
+ opt_line = replace_type(opt_line, pod_or_container)
+ opt_line = opt_line.replace('<<subcommand>>', subcommand)
fh_out.write(opt_line)
fh_out.write("\n[//]: # (END included file " + optionfile + ")\n")
else:
@@ -65,21 +66,46 @@ def process(infile):
os.chmod(outfile_tmp, 0o444)
os.rename(outfile_tmp, outfile)
-# str.removeprefix() is python 3.9+, we need to support older versions on mac
-def removeprefix(string: str, prefix: str) -> str:
- if string.startswith(prefix):
- return string[len(prefix):]
- else:
- return string[:]
-
-# str.removesuffix() is python 3.9+, we need to support older versions on mac
-def removesuffix(string: str, suffix: str) -> str:
- # suffix='' should not call self[:-0].
- if suffix and string.endswith(suffix):
- return string[:-len(suffix)]
- else:
- return string[:]
+# Given a file path of the form podman-foo-bar.1.md.in, return "foo bar"
+def podman_subcommand(string: str) -> str:
+ if string.startswith("podman-"):
+ string = string[len("podman-"):]
+ if string.endswith(".1.md.in"):
+ string = string[:-len(".1.md.in")]
+ return string.replace("-", " ")
+# Replace instances of '<<pod|container>>' with the desired one (based on
+# 'type' which is 'pod' or 'container').
+def replace_type(line: str, type: str) -> str:
+ # Internal helper function: determines the desired half of the <a|b> string
+ def replwith(matchobj):
+ lhs, rhs = matchobj[0].split('|')
+ # Strip off '<<' and '>>'
+ lhs = lhs[2:]
+ rhs = rhs[:len(rhs)-2]
+
+ # Check both sides for 'pod' followed by (non-"m" or end-of-string).
+ # The non-m prevents us from triggering on 'podman', which could
+ # conceivably be present in both sides. And we check for 'pod',
+ # not 'container', because it's possible to have something like
+ # <<container in pod|container>>.
+ if re.match('pod([^m]|$)', lhs, re.IGNORECASE):
+ if re.match('pod([^m]|$)', rhs, re.IGNORECASE):
+ raise Exception("'%s' matches 'pod' in both left and right sides" % matchobj[0])
+ # Only left-hand side has "pod"
+ if type == 'pod':
+ return lhs
+ else:
+ return rhs
+ else:
+ if not re.match('pod([^m]|$)', rhs, re.IGNORECASE):
+ raise Exception("'%s' does not match 'pod' in either side" % matchobj[0])
+ if type == 'pod':
+ return rhs
+ else:
+ return lhs
+
+ return re.sub('<<[^\|>]+\|[^\|>]+>>', replwith, line)
if __name__ == "__main__":
main()
diff --git a/hack/markdown-preprocess-review b/hack/markdown-preprocess-review
new file mode 100755
index 000000000..a487265ad
--- /dev/null
+++ b/hack/markdown-preprocess-review
@@ -0,0 +1,132 @@
+#!/usr/bin/perl
+
+(our $ME = $0) =~ s|^.*/||;
+
+use v5.20;
+
+our $DSM = 'docs/source/markdown';
+
+my ($oldname, $newname);
+my %oldname;
+my %changed;
+open my $git_diff, '-|', 'git', 'log', '-1', '-p'
+ or die "$ME: Cannot fork: $!\n";
+while (my $line = <$git_diff>) {
+ chomp $line;
+
+ if ($line =~ m!^\-\-\-\s+a/$DSM/(podman-\S+\.md(\.in)?)!) {
+ $oldname = $1;
+ $newname = undef;
+ }
+ elsif ($line =~ m!^\+\+\+\s+b/$DSM/(podman-\S+\.md(\.in)?)!) {
+ $newname = $1;
+ $oldname{$newname} = $oldname;
+ }
+ elsif ($newname) {
+ if ($line =~ s/^-####\s+//) {
+ $line =~ /^\*\*--(\S+?)\*\*/
+ or die "$ME: in $newname: weird '$line'";
+ $changed{$newname}{$1}{name} //= $1;
+ }
+ # Usually the same, but not for host.container and host.pod.md
+ elsif ($line =~ /^\+\@\@option\s+(\S+)/) {
+ my $optfile = $1;
+ if ($optfile =~ /^(.*)\.\S+$/) {
+ $changed{$newname}{$1}{name} = $optfile;
+ }
+ }
+ }
+}
+close $git_diff;
+
+# Pass 2: read each oldfile, parse changed options
+for my $f (sort keys %changed) {
+ my $oldfile = $oldname{$f};
+ open my $git_fh, '-|', 'git', 'show', "HEAD^:$DSM/$oldfile"
+ or die "$ME: Cannot fork: $!\n";
+ my $opt;
+ while (my $line = <$git_fh>) {
+ if ($line =~ /^####\s+\*\*--(\S+?)\*\*/) {
+ $opt = $1;
+ if ($changed{$f}{$opt}) {
+ $changed{$f}{$opt}{text} = $line;
+ }
+ else {
+ undef $opt;
+ }
+ }
+ elsif ($line =~ /^#/ || $line =~ /^\@\@option\s/) {
+ undef $opt;
+ }
+ elsif ($opt) {
+ $changed{$f}{$opt}{text} .= $line;
+ }
+ }
+ close $git_fh
+ or die "$ME: Error running git on $oldfile\n";
+}
+
+# Pass 3: write out files
+my $tempdir = "/tmp/$ME.diffs";
+system('rm', '-rf', $tempdir);
+mkdir $tempdir, 0755;
+
+for my $md_file (sort keys %changed) {
+ for my $opt (sort keys %{$changed{$md_file}}) {
+ my $d = "$tempdir/$changed{$md_file}{$opt}{name}";
+ mkdir $d, 0755;
+
+ my $outfile = "$d/$md_file";
+ open my $fh, '>', $outfile
+ or die "$ME: Cannot create $outfile: $!\n";
+ # strip all trailing newlines
+ (my $text = $changed{$md_file}{$opt}{text}) =~ s/\n+$/\n/s;
+ print { $fh } $text;
+ close $fh
+ or die "$ME: Error writing $outfile: $!\n";
+
+ my $new_text = "$DSM/options/$changed{$md_file}{$opt}{name}.md";
+ die "$ME: $md_file: File does not exist: $new_text\n" if ! -e $new_text;
+ system('cp', $new_text, "$d/zzz-chosen.md");
+ }
+}
+
+# Now run diffuse
+chdir $tempdir or die;
+my @all_opts = glob("*");
+for my $i (0..$#all_opts) {
+ my $opt = $all_opts[$i];
+ chdir "$tempdir/$opt"
+ or die "??? Internal error, cannot cd $tempdir/$opt: $!";
+
+ $| = 1; printf "--%s (%d/%d) ", $opt, $i+1, scalar(@all_opts);
+
+ my @all_files = glob("*");
+ if (all_files_identical(@all_files)) {
+ pop @all_files;
+ print "[identical between @all_files]\n";
+ next;
+ }
+
+ # Prompt
+ print "[Y/n/q] ";
+ my $ans = <STDIN>;
+ next if $ans =~ /^n/i;
+ exit 0 if $ans =~ /^q/i;
+
+ system("diffuse", "-w", glob("*")) == 0
+ or die "Diffuse failed\n";
+}
+
+
+sub all_files_identical {
+ my %sha;
+ for my $f (@_) {
+ my $result = qx{sha256sum $f};
+ $result =~ /^([0-9a-f]+)\s/
+ or die "Internal error: unexpected result from sha256sum $f: $result";
+ $sha{$1}++;
+ }
+
+ return (keys(%sha) == 1);
+}
diff --git a/hack/markdown-preprocess.t b/hack/markdown-preprocess.t
new file mode 100755
index 000000000..a6fe793b1
--- /dev/null
+++ b/hack/markdown-preprocess.t
@@ -0,0 +1,58 @@
+#!/usr/bin/env python3
+
+"""
+Tests for markdown-preprocess
+"""
+
+import unittest
+
+# https://stackoverflow.com/questions/66665217/how-to-import-a-python-script-without-a-py-extension
+from importlib.util import spec_from_loader, module_from_spec
+from importlib.machinery import SourceFileLoader
+
+spec = spec_from_loader("mp", SourceFileLoader("mp", "hack/markdown-preprocess"))
+mp = module_from_spec(spec)
+spec.loader.exec_module(mp)
+
+class TestPodReplacer(unittest.TestCase):
+ def test_basic(self):
+ """basic pod|container and vice-versa"""
+ s = '<<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):
+ """test case-insensitive replacement of Pod, Container"""
+ s = '<<Pod|Container>>'
+ 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):
+ """we ignore 'podman'"""
+ self.assertEqual(mp.replace_type('<<podman container|pod in podman>>', 'container'), 'podman container')
+
+ def test_exception_both(self):
+ """test that 'pod' on both sides raises exception"""
+ with self.assertRaisesRegex(Exception, "in both left and right sides"):
+ mp.replace_type('<<pod 123|pod 321>>', 'pod')
+
+ def test_exception_neither(self):
+ """test that 'pod' on neither side raises exception"""
+ with self.assertRaisesRegex(Exception, "in either side"):
+ mp.replace_type('<<container 123|container 321>>', 'pod')
+
+class TestPodmanSubcommand(unittest.TestCase):
+ def test_basic(self):
+ """podman subcommand basic test"""
+ self.assertEqual(mp.podman_subcommand("podman-foo.1.md.in"), "foo")
+ self.assertEqual(mp.podman_subcommand("podman-foo-bar.1.md.in"), "foo bar")
+
+
+if __name__ == '__main__':
+ unittest.main()