@@ -122,6 +122,26 @@ def verify_pypi_release(version: str, compare_dir: str) -> bool:
122122 return sorted (same ) == [wheel , tar ]
123123
124124
125+ def sign_release_artifacts (
126+ version : str , build_dir : str , key_id : str = None
127+ ) -> None :
128+ """Sign built release artifacts with gpg and write signature files to cwd"""
129+ sdist = f"{ PYPI_PROJECT } -{ version } .tar.gz"
130+ wheel = f"{ PYPI_PROJECT } -{ version } -py3-none-any.whl"
131+ cmd = ["gpg" , "--detach-sign" , "--armor" ]
132+
133+ if key_id is not None :
134+ cmd += ["--local-user" , key_id ]
135+
136+ for filename in [sdist , wheel ]:
137+ artifact_path = os .path .join (build_dir , filename )
138+ signature_path = f"{ filename } .asc"
139+ subprocess .run (
140+ cmd + ["--output" , signature_path , artifact_path ], check = True
141+ )
142+ assert os .path .exists (signature_path )
143+
144+
125145def finished (s : str ) -> None :
126146 # clear line
127147 sys .stdout .write ("\033 [K" )
@@ -143,6 +163,15 @@ def main() -> int:
143163 dest = "skip_pypi" ,
144164 help = "Skip PyPI release check." ,
145165 )
166+ parser .add_argument (
167+ "--sign" ,
168+ nargs = "?" ,
169+ const = True ,
170+ metavar = "<key id>" ,
171+ dest = "sign" ,
172+ help = "Sign release artifacts with 'gpg'. If no <key id> is passed, the default "
173+ "signing key is used. Resulting '*.asc' files are written to CWD." ,
174+ )
146175 args = parser .parse_args ()
147176
148177 success = True
@@ -172,15 +201,27 @@ def main() -> int:
172201 # This is expected while build is not reproducible
173202 finished ("ERROR: PyPI artifacts do not match built release" )
174203 success = False
204+ else :
205+ finished ("PyPI artifacts match the built release" )
175206
176207 progress ("Downloading release from GitHub" )
177208 if not verify_github_release (build_version , build_dir ):
178209 # This is expected while build is not reproducible
179210 finished ("ERROR: GitHub artifacts do not match built release" )
180211 success = False
181-
182- if success :
183- finished ("Github and PyPI artifacts match the built release" )
212+ else :
213+ finished ("GitHub artifacts match the built release" )
214+
215+ # NOTE: 'gpg' might prompt for password or ask if it should override files...
216+ if args .sign :
217+ progress ("Signing built release with gpg" )
218+ if success :
219+ key_id = args .sign if args .sign is not True else None
220+
221+ sign_release_artifacts (build_version , build_dir , key_id )
222+ finished ("Created signatures in cwd (see '*.asc' files)" )
223+ else :
224+ finished ("WARNING: Skipped signing of non-matching artifacts" )
184225
185226 return 0 if success else 1
186227
0 commit comments