diff options
author | Gavin Mak <gavinmak@google.com> | 2023-03-11 06:46:20 +0000 |
---|---|---|
committer | LUCI <gerrit-scoped@luci-project-accounts.iam.gserviceaccount.com> | 2023-03-22 17:46:28 +0000 |
commit | ea2e330e43c182dc16b0111ebc69ee5a71ee4ce1 (patch) | |
tree | dc33ba0e56825b3e007d0589891756724725a465 /release | |
parent | 1604cf255f8c1786a23388db6d5277ac7949a24a (diff) | |
download | git-repo-ea2e330e43c182dc16b0111ebc69ee5a71ee4ce1.tar.gz |
Format codebase with black and check formatting in CQ
Apply rules set by https://gerrit-review.googlesource.com/c/git-repo/+/362954/ across the codebase and fix any lingering errors caught
by flake8. Also check black formatting in run_tests (and CQ).
Bug: b/267675342
Change-Id: I972d77649dac351150dcfeb1cd1ad0ea2efc1956
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/363474
Reviewed-by: Mike Frysinger <vapier@google.com>
Tested-by: Gavin Mak <gavinmak@google.com>
Commit-Queue: Gavin Mak <gavinmak@google.com>
Diffstat (limited to 'release')
-rwxr-xr-x | release/sign-launcher.py | 178 | ||||
-rwxr-xr-x | release/sign-tag.py | 171 | ||||
-rw-r--r-- | release/update_manpages.py | 186 | ||||
-rw-r--r-- | release/util.py | 78 |
4 files changed, 355 insertions, 258 deletions
diff --git a/release/sign-launcher.py b/release/sign-launcher.py index ffe23cc5..86566122 100755 --- a/release/sign-launcher.py +++ b/release/sign-launcher.py | |||
@@ -28,43 +28,56 @@ import util | |||
28 | 28 | ||
29 | 29 | ||
30 | def sign(opts): | 30 | def sign(opts): |
31 | """Sign the launcher!""" | 31 | """Sign the launcher!""" |
32 | output = '' | 32 | output = "" |
33 | for key in opts.keys: | 33 | for key in opts.keys: |
34 | # We use ! at the end of the key so that gpg uses this specific key. | 34 | # We use ! at the end of the key so that gpg uses this specific key. |
35 | # Otherwise it uses the key as a lookup into the overall key and uses the | 35 | # Otherwise it uses the key as a lookup into the overall key and uses |
36 | # default signing key. i.e. It will see that KEYID_RSA is a subkey of | 36 | # the default signing key. i.e. It will see that KEYID_RSA is a subkey |
37 | # another key, and use the primary key to sign instead of the subkey. | 37 | # of another key, and use the primary key to sign instead of the subkey. |
38 | cmd = ['gpg', '--homedir', opts.gpgdir, '-u', f'{key}!', '--batch', '--yes', | 38 | cmd = [ |
39 | '--armor', '--detach-sign', '--output', '-', opts.launcher] | 39 | "gpg", |
40 | ret = util.run(opts, cmd, encoding='utf-8', stdout=subprocess.PIPE) | 40 | "--homedir", |
41 | output += ret.stdout | 41 | opts.gpgdir, |
42 | 42 | "-u", | |
43 | # Save the combined signatures into one file. | 43 | f"{key}!", |
44 | with open(f'{opts.launcher}.asc', 'w', encoding='utf-8') as fp: | 44 | "--batch", |
45 | fp.write(output) | 45 | "--yes", |
46 | "--armor", | ||
47 | "--detach-sign", | ||
48 | "--output", | ||
49 | "-", | ||
50 | opts.launcher, | ||
51 | ] | ||
52 | ret = util.run(opts, cmd, encoding="utf-8", stdout=subprocess.PIPE) | ||
53 | output += ret.stdout | ||
54 | |||
55 | # Save the combined signatures into one file. | ||
56 | with open(f"{opts.launcher}.asc", "w", encoding="utf-8") as fp: | ||
57 | fp.write(output) | ||
46 | 58 | ||
47 | 59 | ||
48 | def check(opts): | 60 | def check(opts): |
49 | """Check the signature.""" | 61 | """Check the signature.""" |
50 | util.run(opts, ['gpg', '--verify', f'{opts.launcher}.asc']) | 62 | util.run(opts, ["gpg", "--verify", f"{opts.launcher}.asc"]) |
51 | 63 | ||
52 | 64 | ||
53 | def get_version(opts): | 65 | def get_version(opts): |
54 | """Get the version from |launcher|.""" | 66 | """Get the version from |launcher|.""" |
55 | # Make sure we don't search $PATH when signing the "repo" file in the cwd. | 67 | # Make sure we don't search $PATH when signing the "repo" file in the cwd. |
56 | launcher = os.path.join('.', opts.launcher) | 68 | launcher = os.path.join(".", opts.launcher) |
57 | cmd = [launcher, '--version'] | 69 | cmd = [launcher, "--version"] |
58 | ret = util.run(opts, cmd, encoding='utf-8', stdout=subprocess.PIPE) | 70 | ret = util.run(opts, cmd, encoding="utf-8", stdout=subprocess.PIPE) |
59 | m = re.search(r'repo launcher version ([0-9.]+)', ret.stdout) | 71 | m = re.search(r"repo launcher version ([0-9.]+)", ret.stdout) |
60 | if not m: | 72 | if not m: |
61 | sys.exit(f'{opts.launcher}: unable to detect repo version') | 73 | sys.exit(f"{opts.launcher}: unable to detect repo version") |
62 | return m.group(1) | 74 | return m.group(1) |
63 | 75 | ||
64 | 76 | ||
65 | def postmsg(opts, version): | 77 | def postmsg(opts, version): |
66 | """Helpful info to show at the end for release manager.""" | 78 | """Helpful info to show at the end for release manager.""" |
67 | print(f""" | 79 | print( |
80 | f""" | ||
68 | Repo launcher bucket: | 81 | Repo launcher bucket: |
69 | gs://git-repo-downloads/ | 82 | gs://git-repo-downloads/ |
70 | 83 | ||
@@ -81,55 +94,72 @@ NB: If a rollback is necessary, the GS bucket archives old versions, and may be | |||
81 | gsutil ls -la gs://git-repo-downloads/repo gs://git-repo-downloads/repo.asc | 94 | gsutil ls -la gs://git-repo-downloads/repo gs://git-repo-downloads/repo.asc |
82 | gsutil cp -a public-read gs://git-repo-downloads/repo#<unique id> gs://git-repo-downloads/repo | 95 | gsutil cp -a public-read gs://git-repo-downloads/repo#<unique id> gs://git-repo-downloads/repo |
83 | gsutil cp -a public-read gs://git-repo-downloads/repo.asc#<unique id> gs://git-repo-downloads/repo.asc | 96 | gsutil cp -a public-read gs://git-repo-downloads/repo.asc#<unique id> gs://git-repo-downloads/repo.asc |
84 | """) | 97 | """ # noqa: E501 |
98 | ) | ||
85 | 99 | ||
86 | 100 | ||
87 | def get_parser(): | 101 | def get_parser(): |
88 | """Get a CLI parser.""" | 102 | """Get a CLI parser.""" |
89 | parser = argparse.ArgumentParser(description=__doc__) | 103 | parser = argparse.ArgumentParser(description=__doc__) |
90 | parser.add_argument('-n', '--dry-run', | 104 | parser.add_argument( |
91 | dest='dryrun', action='store_true', | 105 | "-n", |
92 | help='show everything that would be done') | 106 | "--dry-run", |
93 | parser.add_argument('--gpgdir', | 107 | dest="dryrun", |
94 | default=os.path.join(util.HOMEDIR, '.gnupg', 'repo'), | 108 | action="store_true", |
95 | help='path to dedicated gpg dir with release keys ' | 109 | help="show everything that would be done", |
96 | '(default: ~/.gnupg/repo/)') | 110 | ) |
97 | parser.add_argument('--keyid', dest='keys', default=[], action='append', | 111 | parser.add_argument( |
98 | help='alternative signing keys to use') | 112 | "--gpgdir", |
99 | parser.add_argument('launcher', | 113 | default=os.path.join(util.HOMEDIR, ".gnupg", "repo"), |
100 | default=os.path.join(util.TOPDIR, 'repo'), nargs='?', | 114 | help="path to dedicated gpg dir with release keys " |
101 | help='the launcher script to sign') | 115 | "(default: ~/.gnupg/repo/)", |
102 | return parser | 116 | ) |
117 | parser.add_argument( | ||
118 | "--keyid", | ||
119 | dest="keys", | ||
120 | default=[], | ||
121 | action="append", | ||
122 | help="alternative signing keys to use", | ||
123 | ) | ||
124 | parser.add_argument( | ||
125 | "launcher", | ||
126 | default=os.path.join(util.TOPDIR, "repo"), | ||
127 | nargs="?", | ||
128 | help="the launcher script to sign", | ||
129 | ) | ||
130 | return parser | ||
103 | 131 | ||
104 | 132 | ||
105 | def main(argv): | 133 | def main(argv): |
106 | """The main func!""" | 134 | """The main func!""" |
107 | parser = get_parser() | 135 | parser = get_parser() |
108 | opts = parser.parse_args(argv) | 136 | opts = parser.parse_args(argv) |
109 | 137 | ||
110 | if not os.path.exists(opts.gpgdir): | 138 | if not os.path.exists(opts.gpgdir): |
111 | parser.error(f'--gpgdir does not exist: {opts.gpgdir}') | 139 | parser.error(f"--gpgdir does not exist: {opts.gpgdir}") |
112 | if not os.path.exists(opts.launcher): | 140 | if not os.path.exists(opts.launcher): |
113 | parser.error(f'launcher does not exist: {opts.launcher}') | 141 | parser.error(f"launcher does not exist: {opts.launcher}") |
114 | 142 | ||
115 | opts.launcher = os.path.relpath(opts.launcher) | 143 | opts.launcher = os.path.relpath(opts.launcher) |
116 | print(f'Signing "{opts.launcher}" launcher script and saving to ' | 144 | print( |
117 | f'"{opts.launcher}.asc"') | 145 | f'Signing "{opts.launcher}" launcher script and saving to ' |
118 | 146 | f'"{opts.launcher}.asc"' | |
119 | if opts.keys: | 147 | ) |
120 | print(f'Using custom keys to sign: {" ".join(opts.keys)}') | 148 | |
121 | else: | 149 | if opts.keys: |
122 | print('Using official Repo release keys to sign') | 150 | print(f'Using custom keys to sign: {" ".join(opts.keys)}') |
123 | opts.keys = [util.KEYID_DSA, util.KEYID_RSA, util.KEYID_ECC] | 151 | else: |
124 | util.import_release_key(opts) | 152 | print("Using official Repo release keys to sign") |
125 | 153 | opts.keys = [util.KEYID_DSA, util.KEYID_RSA, util.KEYID_ECC] | |
126 | version = get_version(opts) | 154 | util.import_release_key(opts) |
127 | sign(opts) | 155 | |
128 | check(opts) | 156 | version = get_version(opts) |
129 | postmsg(opts, version) | 157 | sign(opts) |
130 | 158 | check(opts) | |
131 | return 0 | 159 | postmsg(opts, version) |
132 | 160 | ||
133 | 161 | return 0 | |
134 | if __name__ == '__main__': | 162 | |
135 | sys.exit(main(sys.argv[1:])) | 163 | |
164 | if __name__ == "__main__": | ||
165 | sys.exit(main(sys.argv[1:])) | ||
diff --git a/release/sign-tag.py b/release/sign-tag.py index 605437c9..fbfe7b26 100755 --- a/release/sign-tag.py +++ b/release/sign-tag.py | |||
@@ -35,46 +35,61 @@ import util | |||
35 | KEYID = util.KEYID_DSA | 35 | KEYID = util.KEYID_DSA |
36 | 36 | ||
37 | # Regular expression to validate tag names. | 37 | # Regular expression to validate tag names. |
38 | RE_VALID_TAG = r'^v([0-9]+[.])+[0-9]+$' | 38 | RE_VALID_TAG = r"^v([0-9]+[.])+[0-9]+$" |
39 | 39 | ||
40 | 40 | ||
41 | def sign(opts): | 41 | def sign(opts): |
42 | """Tag the commit & sign it!""" | 42 | """Tag the commit & sign it!""" |
43 | # We use ! at the end of the key so that gpg uses this specific key. | 43 | # We use ! at the end of the key so that gpg uses this specific key. |
44 | # Otherwise it uses the key as a lookup into the overall key and uses the | 44 | # Otherwise it uses the key as a lookup into the overall key and uses the |
45 | # default signing key. i.e. It will see that KEYID_RSA is a subkey of | 45 | # default signing key. i.e. It will see that KEYID_RSA is a subkey of |
46 | # another key, and use the primary key to sign instead of the subkey. | 46 | # another key, and use the primary key to sign instead of the subkey. |
47 | cmd = ['git', 'tag', '-s', opts.tag, '-u', f'{opts.key}!', | 47 | cmd = [ |
48 | '-m', f'repo {opts.tag}', opts.commit] | 48 | "git", |
49 | 49 | "tag", | |
50 | key = 'GNUPGHOME' | 50 | "-s", |
51 | print('+', f'export {key}="{opts.gpgdir}"') | 51 | opts.tag, |
52 | oldvalue = os.getenv(key) | 52 | "-u", |
53 | os.putenv(key, opts.gpgdir) | 53 | f"{opts.key}!", |
54 | util.run(opts, cmd) | 54 | "-m", |
55 | if oldvalue is None: | 55 | f"repo {opts.tag}", |
56 | os.unsetenv(key) | 56 | opts.commit, |
57 | else: | 57 | ] |
58 | os.putenv(key, oldvalue) | 58 | |
59 | key = "GNUPGHOME" | ||
60 | print("+", f'export {key}="{opts.gpgdir}"') | ||
61 | oldvalue = os.getenv(key) | ||
62 | os.putenv(key, opts.gpgdir) | ||
63 | util.run(opts, cmd) | ||
64 | if oldvalue is None: | ||
65 | os.unsetenv(key) | ||
66 | else: | ||
67 | os.putenv(key, oldvalue) | ||
59 | 68 | ||
60 | 69 | ||
61 | def check(opts): | 70 | def check(opts): |
62 | """Check the signature.""" | 71 | """Check the signature.""" |
63 | util.run(opts, ['git', 'tag', '--verify', opts.tag]) | 72 | util.run(opts, ["git", "tag", "--verify", opts.tag]) |
64 | 73 | ||
65 | 74 | ||
66 | def postmsg(opts): | 75 | def postmsg(opts): |
67 | """Helpful info to show at the end for release manager.""" | 76 | """Helpful info to show at the end for release manager.""" |
68 | cmd = ['git', 'rev-parse', 'remotes/origin/stable'] | 77 | cmd = ["git", "rev-parse", "remotes/origin/stable"] |
69 | ret = util.run(opts, cmd, encoding='utf-8', stdout=subprocess.PIPE) | 78 | ret = util.run(opts, cmd, encoding="utf-8", stdout=subprocess.PIPE) |
70 | current_release = ret.stdout.strip() | 79 | current_release = ret.stdout.strip() |
71 | 80 | ||
72 | cmd = ['git', 'log', '--format=%h (%aN) %s', '--no-merges', | 81 | cmd = [ |
73 | f'remotes/origin/stable..{opts.tag}'] | 82 | "git", |
74 | ret = util.run(opts, cmd, encoding='utf-8', stdout=subprocess.PIPE) | 83 | "log", |
75 | shortlog = ret.stdout.strip() | 84 | "--format=%h (%aN) %s", |
76 | 85 | "--no-merges", | |
77 | print(f""" | 86 | f"remotes/origin/stable..{opts.tag}", |
87 | ] | ||
88 | ret = util.run(opts, cmd, encoding="utf-8", stdout=subprocess.PIPE) | ||
89 | shortlog = ret.stdout.strip() | ||
90 | |||
91 | print( | ||
92 | f""" | ||
78 | Here's the short log since the last release. | 93 | Here's the short log since the last release. |
79 | {shortlog} | 94 | {shortlog} |
80 | 95 | ||
@@ -84,57 +99,69 @@ NB: People will start upgrading to this version immediately. | |||
84 | 99 | ||
85 | To roll back a release: | 100 | To roll back a release: |
86 | git push origin --force {current_release}:stable -n | 101 | git push origin --force {current_release}:stable -n |
87 | """) | 102 | """ |
103 | ) | ||
88 | 104 | ||
89 | 105 | ||
90 | def get_parser(): | 106 | def get_parser(): |
91 | """Get a CLI parser.""" | 107 | """Get a CLI parser.""" |
92 | parser = argparse.ArgumentParser( | 108 | parser = argparse.ArgumentParser( |
93 | description=__doc__, | 109 | description=__doc__, |
94 | formatter_class=argparse.RawDescriptionHelpFormatter) | 110 | formatter_class=argparse.RawDescriptionHelpFormatter, |
95 | parser.add_argument('-n', '--dry-run', | 111 | ) |
96 | dest='dryrun', action='store_true', | 112 | parser.add_argument( |
97 | help='show everything that would be done') | 113 | "-n", |
98 | parser.add_argument('--gpgdir', | 114 | "--dry-run", |
99 | default=os.path.join(util.HOMEDIR, '.gnupg', 'repo'), | 115 | dest="dryrun", |
100 | help='path to dedicated gpg dir with release keys ' | 116 | action="store_true", |
101 | '(default: ~/.gnupg/repo/)') | 117 | help="show everything that would be done", |
102 | parser.add_argument('-f', '--force', action='store_true', | 118 | ) |
103 | help='force signing of any tag') | 119 | parser.add_argument( |
104 | parser.add_argument('--keyid', dest='key', | 120 | "--gpgdir", |
105 | help='alternative signing key to use') | 121 | default=os.path.join(util.HOMEDIR, ".gnupg", "repo"), |
106 | parser.add_argument('tag', | 122 | help="path to dedicated gpg dir with release keys " |
107 | help='the tag to create (e.g. "v2.0")') | 123 | "(default: ~/.gnupg/repo/)", |
108 | parser.add_argument('commit', default='HEAD', nargs='?', | 124 | ) |
109 | help='the commit to tag') | 125 | parser.add_argument( |
110 | return parser | 126 | "-f", "--force", action="store_true", help="force signing of any tag" |
127 | ) | ||
128 | parser.add_argument( | ||
129 | "--keyid", dest="key", help="alternative signing key to use" | ||
130 | ) | ||
131 | parser.add_argument("tag", help='the tag to create (e.g. "v2.0")') | ||
132 | parser.add_argument( | ||
133 | "commit", default="HEAD", nargs="?", help="the commit to tag" | ||
134 | ) | ||
135 | return parser | ||
111 | 136 | ||
112 | 137 | ||
113 | def main(argv): | 138 | def main(argv): |
114 | """The main func!""" | 139 | """The main func!""" |
115 | parser = get_parser() | 140 | parser = get_parser() |
116 | opts = parser.parse_args(argv) | 141 | opts = parser.parse_args(argv) |
117 | 142 | ||
118 | if not os.path.exists(opts.gpgdir): | 143 | if not os.path.exists(opts.gpgdir): |
119 | parser.error(f'--gpgdir does not exist: {opts.gpgdir}') | 144 | parser.error(f"--gpgdir does not exist: {opts.gpgdir}") |
120 | 145 | ||
121 | if not opts.force and not re.match(RE_VALID_TAG, opts.tag): | 146 | if not opts.force and not re.match(RE_VALID_TAG, opts.tag): |
122 | parser.error(f'tag "{opts.tag}" does not match regex "{RE_VALID_TAG}"; ' | 147 | parser.error( |
123 | 'use --force to sign anyways') | 148 | f'tag "{opts.tag}" does not match regex "{RE_VALID_TAG}"; ' |
149 | "use --force to sign anyways" | ||
150 | ) | ||
124 | 151 | ||
125 | if opts.key: | 152 | if opts.key: |
126 | print(f'Using custom key to sign: {opts.key}') | 153 | print(f"Using custom key to sign: {opts.key}") |
127 | else: | 154 | else: |
128 | print('Using official Repo release key to sign') | 155 | print("Using official Repo release key to sign") |
129 | opts.key = KEYID | 156 | opts.key = KEYID |
130 | util.import_release_key(opts) | 157 | util.import_release_key(opts) |
131 | 158 | ||
132 | sign(opts) | 159 | sign(opts) |
133 | check(opts) | 160 | check(opts) |
134 | postmsg(opts) | 161 | postmsg(opts) |
135 | 162 | ||
136 | return 0 | 163 | return 0 |
137 | 164 | ||
138 | 165 | ||
139 | if __name__ == '__main__': | 166 | if __name__ == "__main__": |
140 | sys.exit(main(sys.argv[1:])) | 167 | sys.exit(main(sys.argv[1:])) |
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 | |||
29 | import tempfile | 29 | import tempfile |
30 | 30 | ||
31 | TOPDIR = Path(__file__).resolve().parent.parent | 31 | TOPDIR = Path(__file__).resolve().parent.parent |
32 | MANDIR = TOPDIR.joinpath('man') | 32 | MANDIR = TOPDIR.joinpath("man") |
33 | 33 | ||
34 | # Load repo local modules. | 34 | # Load repo local modules. |
35 | sys.path.insert(0, str(TOPDIR)) | 35 | sys.path.insert(0, str(TOPDIR)) |
36 | from git_command import RepoSourceVersion | 36 | from git_command import RepoSourceVersion |
37 | import subcmds | 37 | import subcmds |
38 | 38 | ||
39 | |||
39 | def worker(cmd, **kwargs): | 40 | def worker(cmd, **kwargs): |
40 | subprocess.run(cmd, **kwargs) | 41 | subprocess.run(cmd, **kwargs) |
42 | |||
41 | 43 | ||
42 | def main(argv): | 44 | def 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 | ||
102 | def replace_regex(data): | 136 | def 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 |
diff --git a/release/util.py b/release/util.py index 9d0eb1dc..df7a5638 100644 --- a/release/util.py +++ b/release/util.py | |||
@@ -20,54 +20,60 @@ import subprocess | |||
20 | import sys | 20 | import sys |
21 | 21 | ||
22 | 22 | ||
23 | assert sys.version_info >= (3, 6), 'This module requires Python 3.6+' | 23 | assert sys.version_info >= (3, 6), "This module requires Python 3.6+" |
24 | 24 | ||
25 | 25 | ||
26 | TOPDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | 26 | TOPDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
27 | HOMEDIR = os.path.expanduser('~') | 27 | HOMEDIR = os.path.expanduser("~") |
28 | 28 | ||
29 | 29 | ||
30 | # These are the release keys we sign with. | 30 | # These are the release keys we sign with. |
31 | KEYID_DSA = '8BB9AD793E8E6153AF0F9A4416530D5E920F5C65' | 31 | KEYID_DSA = "8BB9AD793E8E6153AF0F9A4416530D5E920F5C65" |
32 | KEYID_RSA = 'A34A13BE8E76BFF46A0C022DA2E75A824AAB9624' | 32 | KEYID_RSA = "A34A13BE8E76BFF46A0C022DA2E75A824AAB9624" |
33 | KEYID_ECC = 'E1F9040D7A3F6DAFAC897CD3D3B95DA243E48A39' | 33 | KEYID_ECC = "E1F9040D7A3F6DAFAC897CD3D3B95DA243E48A39" |
34 | 34 | ||
35 | 35 | ||
36 | def cmdstr(cmd): | 36 | def cmdstr(cmd): |
37 | """Get a nicely quoted shell command.""" | 37 | """Get a nicely quoted shell command.""" |
38 | ret = [] | 38 | ret = [] |
39 | for arg in cmd: | 39 | for arg in cmd: |
40 | if not re.match(r'^[a-zA-Z0-9/_.=-]+$', arg): | 40 | if not re.match(r"^[a-zA-Z0-9/_.=-]+$", arg): |
41 | arg = f'"{arg}"' | 41 | arg = f'"{arg}"' |
42 | ret.append(arg) | 42 | ret.append(arg) |
43 | return ' '.join(ret) | 43 | return " ".join(ret) |
44 | 44 | ||
45 | 45 | ||
46 | def run(opts, cmd, check=True, **kwargs): | 46 | def run(opts, cmd, check=True, **kwargs): |
47 | """Helper around subprocess.run to include logging.""" | 47 | """Helper around subprocess.run to include logging.""" |
48 | print('+', cmdstr(cmd)) | 48 | print("+", cmdstr(cmd)) |
49 | if opts.dryrun: | 49 | if opts.dryrun: |
50 | cmd = ['true', '--'] + cmd | 50 | cmd = ["true", "--"] + cmd |
51 | try: | 51 | try: |
52 | return subprocess.run(cmd, check=check, **kwargs) | 52 | return subprocess.run(cmd, check=check, **kwargs) |
53 | except subprocess.CalledProcessError as e: | 53 | except subprocess.CalledProcessError as e: |
54 | print(f'aborting: {e}', file=sys.stderr) | 54 | print(f"aborting: {e}", file=sys.stderr) |
55 | sys.exit(1) | 55 | sys.exit(1) |
56 | 56 | ||
57 | 57 | ||
58 | def import_release_key(opts): | 58 | def import_release_key(opts): |
59 | """Import the public key of the official release repo signing key.""" | 59 | """Import the public key of the official release repo signing key.""" |
60 | # Extract the key from our repo launcher. | 60 | # Extract the key from our repo launcher. |
61 | launcher = getattr(opts, 'launcher', os.path.join(TOPDIR, 'repo')) | 61 | launcher = getattr(opts, "launcher", os.path.join(TOPDIR, "repo")) |
62 | print(f'Importing keys from "{launcher}" launcher script') | 62 | print(f'Importing keys from "{launcher}" launcher script') |
63 | with open(launcher, encoding='utf-8') as fp: | 63 | with open(launcher, encoding="utf-8") as fp: |
64 | data = fp.read() | 64 | data = fp.read() |
65 | 65 | ||
66 | keys = re.findall( | 66 | keys = re.findall( |
67 | r'\n-----BEGIN PGP PUBLIC KEY BLOCK-----\n[^-]*' | 67 | r"\n-----BEGIN PGP PUBLIC KEY BLOCK-----\n[^-]*" |
68 | r'\n-----END PGP PUBLIC KEY BLOCK-----\n', data, flags=re.M) | 68 | r"\n-----END PGP PUBLIC KEY BLOCK-----\n", |
69 | run(opts, ['gpg', '--import'], input='\n'.join(keys).encode('utf-8')) | 69 | data, |
70 | 70 | flags=re.M, | |
71 | print('Marking keys as fully trusted') | 71 | ) |
72 | run(opts, ['gpg', '--import-ownertrust'], | 72 | run(opts, ["gpg", "--import"], input="\n".join(keys).encode("utf-8")) |
73 | input=f'{KEYID_DSA}:6:\n'.encode('utf-8')) | 73 | |
74 | print("Marking keys as fully trusted") | ||
75 | run( | ||
76 | opts, | ||
77 | ["gpg", "--import-ownertrust"], | ||
78 | input=f"{KEYID_DSA}:6:\n".encode("utf-8"), | ||
79 | ) | ||