diff options
Diffstat (limited to 'release/sign-tag.py')
-rwxr-xr-x | release/sign-tag.py | 171 |
1 files changed, 99 insertions, 72 deletions
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:])) |