@@ -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+ tar = 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 [tar , 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
@@ -173,15 +202,28 @@ def main() -> int:
173202 finished ("ERROR: PyPI artifacts do not match built release" )
174203 success = False
175204 else :
176- finished (f "PyPI artifacts match the built release" )
205+ finished ("PyPI artifacts match the built release" )
177206
178207 progress ("Downloading release from GitHub" )
179208 if not verify_github_release (build_version , build_dir ):
180209 # This is expected while build is not reproducible
181210 finished ("ERROR: GitHub artifacts do not match built release" )
182211 success = False
183212 else :
184- finished (f"GitHub artifacts match the built release" )
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 = None
220+ if args .sign is not True :
221+ key_id = args .sign
222+
223+ sign_release_artifacts (build_version , build_dir , key_id )
224+ finished ("Created signatures in cwd (see '*.asc' files)" )
225+ else :
226+ finished ("WARNING: Skip signing of non-matching artifacts" )
185227
186228 return 0 if success else 1
187229
0 commit comments