diff --git a/release/update-manpages b/release/update-manpages index 739cedb1..0402ad66 100755 --- a/release/update-manpages +++ b/release/update-manpages @@ -18,107 +18,8 @@ This is intended to be run before every official Repo release. """ -from pathlib import Path -from functools import partial -import argparse -import multiprocessing -import os -import re -import shutil -import subprocess import sys -import tempfile -TOPDIR = Path(__file__).resolve().parent.parent -MANDIR = TOPDIR.joinpath('man') +import update_manpages -# Load repo local modules. -sys.path.insert(0, str(TOPDIR)) -from git_command import RepoSourceVersion -import subcmds - -def worker(cmd, **kwargs): - subprocess.run(cmd, **kwargs) - -def main(argv): - parser = argparse.ArgumentParser(description=__doc__) - opts = parser.parse_args(argv) - - if not shutil.which('help2man'): - sys.exit('Please install help2man to continue.') - - # Let repo know we're generating man pages so it can avoid some dynamic - # behavior (like probing active number of CPUs). We use a weird name & - # value to make it less likely for users to set this var themselves. - os.environ['_REPO_GENERATE_MANPAGES_'] = ' indeed! ' - - # "repo branch" is an alias for "repo branches". - del subcmds.all_commands['branch'] - (MANDIR / 'repo-branch.1').write_text('.so man1/repo-branches.1') - - version = RepoSourceVersion() - cmdlist = [['help2man', '-N', '-n', f'repo {cmd} - manual page for repo {cmd}', - '-S', f'repo {cmd}', '-m', 'Repo Manual', f'--version-string={version}', - '-o', MANDIR.joinpath(f'repo-{cmd}.1.tmp'), './repo', - '-h', f'help {cmd}'] for cmd in subcmds.all_commands] - cmdlist.append(['help2man', '-N', '-n', 'repository management tool built on top of git', - '-S', 'repo', '-m', 'Repo Manual', f'--version-string={version}', - '-o', MANDIR.joinpath('repo.1.tmp'), './repo', - '-h', '--help-all']) - - with tempfile.TemporaryDirectory() as tempdir: - tempdir = Path(tempdir) - repo_dir = tempdir / '.repo' - repo_dir.mkdir() - (repo_dir / 'repo').symlink_to(TOPDIR) - - # Create a repo wrapper using the active Python executable. We can't pass - # this directly to help2man as it's too simple, so insert it via shebang. - data = (TOPDIR / 'repo').read_text(encoding='utf-8') - tempbin = tempdir / 'repo' - tempbin.write_text(f'#!{sys.executable}\n' + data, encoding='utf-8') - tempbin.chmod(0o755) - - # Run all cmd in parallel, and wait for them to finish. - with multiprocessing.Pool() as pool: - pool.map(partial(worker, cwd=tempdir, check=True), cmdlist) - - for tmp_path in MANDIR.glob('*.1.tmp'): - path = tmp_path.parent / tmp_path.stem - old_data = path.read_text() if path.exists() else '' - - data = tmp_path.read_text() - tmp_path.unlink() - - data = replace_regex(data) - - # If the only thing that changed was the date, don't refresh. This avoids - # a lot of noise when only one file actually updates. - old_data = re.sub(r'^(\.TH REPO "1" ")([^"]+)', r'\1', old_data, flags=re.M) - new_data = re.sub(r'^(\.TH REPO "1" ")([^"]+)', r'\1', data, flags=re.M) - if old_data != new_data: - path.write_text(data) - - -def replace_regex(data): - """Replace semantically null regexes in the data. - - Args: - data: manpage text. - - Returns: - Updated manpage text. - """ - regex = ( - (r'(It was generated by help2man) [0-9.]+', '\g<1>.'), - (r'^\033\[[0-9;]*m([^\033]*)\033\[m', '\g<1>'), - (r'^\.IP\n(.*:)\n', '.SS \g<1>\n'), - (r'^\.PP\nDescription', '.SH DETAILS'), - ) - for pattern, replacement in regex: - data = re.sub(pattern, replacement, data, flags=re.M) - return data - - -if __name__ == '__main__': - sys.exit(main(sys.argv[1:])) +sys.exit(update_manpages.main(sys.argv[1:])) diff --git a/release/update_manpages.py b/release/update_manpages.py new file mode 100644 index 00000000..9cbb43b6 --- /dev/null +++ b/release/update_manpages.py @@ -0,0 +1,119 @@ +# Copyright (C) 2021 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Helper tool for generating manual page for all repo commands. + +Most code lives in this module so it can be unittested. +""" + +from pathlib import Path +from functools import partial +import argparse +import multiprocessing +import os +import re +import shutil +import subprocess +import sys +import tempfile + +TOPDIR = Path(__file__).resolve().parent.parent +MANDIR = TOPDIR.joinpath('man') + +# Load repo local modules. +sys.path.insert(0, str(TOPDIR)) +from git_command import RepoSourceVersion +import subcmds + +def worker(cmd, **kwargs): + subprocess.run(cmd, **kwargs) + +def main(argv): + parser = argparse.ArgumentParser(description=__doc__) + opts = parser.parse_args(argv) + + if not shutil.which('help2man'): + sys.exit('Please install help2man to continue.') + + # Let repo know we're generating man pages so it can avoid some dynamic + # behavior (like probing active number of CPUs). We use a weird name & + # value to make it less likely for users to set this var themselves. + os.environ['_REPO_GENERATE_MANPAGES_'] = ' indeed! ' + + # "repo branch" is an alias for "repo branches". + del subcmds.all_commands['branch'] + (MANDIR / 'repo-branch.1').write_text('.so man1/repo-branches.1') + + version = RepoSourceVersion() + cmdlist = [['help2man', '-N', '-n', f'repo {cmd} - manual page for repo {cmd}', + '-S', f'repo {cmd}', '-m', 'Repo Manual', f'--version-string={version}', + '-o', MANDIR.joinpath(f'repo-{cmd}.1.tmp'), './repo', + '-h', f'help {cmd}'] for cmd in subcmds.all_commands] + cmdlist.append(['help2man', '-N', '-n', 'repository management tool built on top of git', + '-S', 'repo', '-m', 'Repo Manual', f'--version-string={version}', + '-o', MANDIR.joinpath('repo.1.tmp'), './repo', + '-h', '--help-all']) + + with tempfile.TemporaryDirectory() as tempdir: + tempdir = Path(tempdir) + repo_dir = tempdir / '.repo' + repo_dir.mkdir() + (repo_dir / 'repo').symlink_to(TOPDIR) + + # Create a repo wrapper using the active Python executable. We can't pass + # this directly to help2man as it's too simple, so insert it via shebang. + data = (TOPDIR / 'repo').read_text(encoding='utf-8') + tempbin = tempdir / 'repo' + tempbin.write_text(f'#!{sys.executable}\n' + data, encoding='utf-8') + tempbin.chmod(0o755) + + # Run all cmd in parallel, and wait for them to finish. + with multiprocessing.Pool() as pool: + pool.map(partial(worker, cwd=tempdir, check=True), cmdlist) + + for tmp_path in MANDIR.glob('*.1.tmp'): + path = tmp_path.parent / tmp_path.stem + old_data = path.read_text() if path.exists() else '' + + data = tmp_path.read_text() + tmp_path.unlink() + + data = replace_regex(data) + + # If the only thing that changed was the date, don't refresh. This avoids + # a lot of noise when only one file actually updates. + old_data = re.sub(r'^(\.TH REPO "1" ")([^"]+)', r'\1', old_data, flags=re.M) + new_data = re.sub(r'^(\.TH REPO "1" ")([^"]+)', r'\1', data, flags=re.M) + if old_data != new_data: + path.write_text(data) + + +def replace_regex(data): + """Replace semantically null regexes in the data. + + Args: + data: manpage text. + + Returns: + Updated manpage text. + """ + regex = ( + (r'(It was generated by help2man) [0-9.]+', '\g<1>.'), + (r'^\033\[[0-9;]*m([^\033]*)\033\[m', '\g<1>'), + (r'^\.IP\n(.*:)\n', '.SS \g<1>\n'), + (r'^\.PP\nDescription', '.SH DETAILS'), + ) + for pattern, replacement in regex: + data = re.sub(pattern, replacement, data, flags=re.M) + return data diff --git a/tests/test_update_manpages.py b/tests/test_update_manpages.py index f0ef72af..0de85be9 100644 --- a/tests/test_update_manpages.py +++ b/tests/test_update_manpages.py @@ -15,7 +15,8 @@ """Unittests for the update_manpages module.""" import unittest -import tests.update_manpages as um + +from release import update_manpages class UpdateManpagesTest(unittest.TestCase): @@ -24,4 +25,4 @@ class UpdateManpagesTest(unittest.TestCase): def test_replace_regex(self): """Check that replace_regex works.""" data = '\n\033[1mSummary\033[m\n' - self.assertEqual(um.replace_regex(data),'\nSummary\n') + self.assertEqual(update_manpages.replace_regex(data),'\nSummary\n') diff --git a/tests/update_manpages.py b/tests/update_manpages.py deleted file mode 120000 index e89c5d8f..00000000 --- a/tests/update_manpages.py +++ /dev/null @@ -1 +0,0 @@ -../release/update-manpages \ No newline at end of file