summaryrefslogtreecommitdiffstats
path: root/release/update_manpages.py
diff options
context:
space:
mode:
Diffstat (limited to 'release/update_manpages.py')
-rw-r--r--release/update_manpages.py186
1 files changed, 110 insertions, 76 deletions
diff --git a/release/update_manpages.py b/release/update_manpages.py
index d1bf8928..cd2acc01 100644
--- a/release/update_manpages.py
+++ b/release/update_manpages.py
@@ -29,91 +29,125 @@ import sys
29import tempfile 29import tempfile
30 30
31TOPDIR = Path(__file__).resolve().parent.parent 31TOPDIR = Path(__file__).resolve().parent.parent
32MANDIR = TOPDIR.joinpath('man') 32MANDIR = TOPDIR.joinpath("man")
33 33
34# Load repo local modules. 34# Load repo local modules.
35sys.path.insert(0, str(TOPDIR)) 35sys.path.insert(0, str(TOPDIR))
36from git_command import RepoSourceVersion 36from git_command import RepoSourceVersion
37import subcmds 37import subcmds
38 38
39
39def worker(cmd, **kwargs): 40def worker(cmd, **kwargs):
40 subprocess.run(cmd, **kwargs) 41 subprocess.run(cmd, **kwargs)
42
41 43
42def main(argv): 44def main(argv):
43 parser = argparse.ArgumentParser(description=__doc__) 45 parser = argparse.ArgumentParser(description=__doc__)
44 opts = parser.parse_args(argv) 46 parser.parse_args(argv)
45 47
46 if not shutil.which('help2man'): 48 if not shutil.which("help2man"):
47 sys.exit('Please install help2man to continue.') 49 sys.exit("Please install help2man to continue.")
48 50
49 # Let repo know we're generating man pages so it can avoid some dynamic 51 # Let repo know we're generating man pages so it can avoid some dynamic
50 # behavior (like probing active number of CPUs). We use a weird name & 52 # behavior (like probing active number of CPUs). We use a weird name &
51 # value to make it less likely for users to set this var themselves. 53 # value to make it less likely for users to set this var themselves.
52 os.environ['_REPO_GENERATE_MANPAGES_'] = ' indeed! ' 54 os.environ["_REPO_GENERATE_MANPAGES_"] = " indeed! "
53 55
54 # "repo branch" is an alias for "repo branches". 56 # "repo branch" is an alias for "repo branches".
55 del subcmds.all_commands['branch'] 57 del subcmds.all_commands["branch"]
56 (MANDIR / 'repo-branch.1').write_text('.so man1/repo-branches.1') 58 (MANDIR / "repo-branch.1").write_text(".so man1/repo-branches.1")
57 59
58 version = RepoSourceVersion() 60 version = RepoSourceVersion()
59 cmdlist = [['help2man', '-N', '-n', f'repo {cmd} - manual page for repo {cmd}', 61 cmdlist = [
60 '-S', f'repo {cmd}', '-m', 'Repo Manual', f'--version-string={version}', 62 [
61 '-o', MANDIR.joinpath(f'repo-{cmd}.1.tmp'), './repo', 63 "help2man",
62 '-h', f'help {cmd}'] for cmd in subcmds.all_commands] 64 "-N",
63 cmdlist.append(['help2man', '-N', '-n', 'repository management tool built on top of git', 65 "-n",
64 '-S', 'repo', '-m', 'Repo Manual', f'--version-string={version}', 66 f"repo {cmd} - manual page for repo {cmd}",
65 '-o', MANDIR.joinpath('repo.1.tmp'), './repo', 67 "-S",
66 '-h', '--help-all']) 68 f"repo {cmd}",
67 69 "-m",
68 with tempfile.TemporaryDirectory() as tempdir: 70 "Repo Manual",
69 tempdir = Path(tempdir) 71 f"--version-string={version}",
70 repo_dir = tempdir / '.repo' 72 "-o",
71 repo_dir.mkdir() 73 MANDIR.joinpath(f"repo-{cmd}.1.tmp"),
72 (repo_dir / 'repo').symlink_to(TOPDIR) 74 "./repo",
73 75 "-h",
74 # Create a repo wrapper using the active Python executable. We can't pass 76 f"help {cmd}",
75 # this directly to help2man as it's too simple, so insert it via shebang. 77 ]
76 data = (TOPDIR / 'repo').read_text(encoding='utf-8') 78 for cmd in subcmds.all_commands
77 tempbin = tempdir / 'repo' 79 ]
78 tempbin.write_text(f'#!{sys.executable}\n' + data, encoding='utf-8') 80 cmdlist.append(
79 tempbin.chmod(0o755) 81 [
80 82 "help2man",
81 # Run all cmd in parallel, and wait for them to finish. 83 "-N",
82 with multiprocessing.Pool() as pool: 84 "-n",
83 pool.map(partial(worker, cwd=tempdir, check=True), cmdlist) 85 "repository management tool built on top of git",
84 86 "-S",
85 for tmp_path in MANDIR.glob('*.1.tmp'): 87 "repo",
86 path = tmp_path.parent / tmp_path.stem 88 "-m",
87 old_data = path.read_text() if path.exists() else '' 89 "Repo Manual",
88 90 f"--version-string={version}",
89 data = tmp_path.read_text() 91 "-o",
90 tmp_path.unlink() 92 MANDIR.joinpath("repo.1.tmp"),
91 93 "./repo",
92 data = replace_regex(data) 94 "-h",
93 95 "--help-all",
94 # If the only thing that changed was the date, don't refresh. This avoids 96 ]
95 # a lot of noise when only one file actually updates. 97 )
96 old_data = re.sub(r'^(\.TH REPO "1" ")([^"]+)', r'\1', old_data, flags=re.M) 98
97 new_data = re.sub(r'^(\.TH REPO "1" ")([^"]+)', r'\1', data, flags=re.M) 99 with tempfile.TemporaryDirectory() as tempdir:
98 if old_data != new_data: 100 tempdir = Path(tempdir)
99 path.write_text(data) 101 repo_dir = tempdir / ".repo"
102 repo_dir.mkdir()
103 (repo_dir / "repo").symlink_to(TOPDIR)
104
105 # Create a repo wrapper using the active Python executable. We can't
106 # pass this directly to help2man as it's too simple, so insert it via
107 # shebang.
108 data = (TOPDIR / "repo").read_text(encoding="utf-8")
109 tempbin = tempdir / "repo"
110 tempbin.write_text(f"#!{sys.executable}\n" + data, encoding="utf-8")
111 tempbin.chmod(0o755)
112
113 # Run all cmd in parallel, and wait for them to finish.
114 with multiprocessing.Pool() as pool:
115 pool.map(partial(worker, cwd=tempdir, check=True), cmdlist)
116
117 for tmp_path in MANDIR.glob("*.1.tmp"):
118 path = tmp_path.parent / tmp_path.stem
119 old_data = path.read_text() if path.exists() else ""
120
121 data = tmp_path.read_text()
122 tmp_path.unlink()
123
124 data = replace_regex(data)
125
126 # If the only thing that changed was the date, don't refresh. This
127 # avoids a lot of noise when only one file actually updates.
128 old_data = re.sub(
129 r'^(\.TH REPO "1" ")([^"]+)', r"\1", old_data, flags=re.M
130 )
131 new_data = re.sub(r'^(\.TH REPO "1" ")([^"]+)', r"\1", data, flags=re.M)
132 if old_data != new_data:
133 path.write_text(data)
100 134
101 135
102def replace_regex(data): 136def replace_regex(data):
103 """Replace semantically null regexes in the data. 137 """Replace semantically null regexes in the data.
104 138
105 Args: 139 Args:
106 data: manpage text. 140 data: manpage text.
107 141
108 Returns: 142 Returns:
109 Updated manpage text. 143 Updated manpage text.
110 """ 144 """
111 regex = ( 145 regex = (
112 (r'(It was generated by help2man) [0-9.]+', r'\g<1>.'), 146 (r"(It was generated by help2man) [0-9.]+", r"\g<1>."),
113 (r'^\033\[[0-9;]*m([^\033]*)\033\[m', r'\g<1>'), 147 (r"^\033\[[0-9;]*m([^\033]*)\033\[m", r"\g<1>"),
114 (r'^\.IP\n(.*:)\n', r'.SS \g<1>\n'), 148 (r"^\.IP\n(.*:)\n", r".SS \g<1>\n"),
115 (r'^\.PP\nDescription', r'.SH DETAILS'), 149 (r"^\.PP\nDescription", r".SH DETAILS"),
116 ) 150 )
117 for pattern, replacement in regex: 151 for pattern, replacement in regex:
118 data = re.sub(pattern, replacement, data, flags=re.M) 152 data = re.sub(pattern, replacement, data, flags=re.M)
119 return data 153 return data