1# This file is dual licensed under the terms of the Apache License, Version 2# 2.0, and the BSD License. See the LICENSE file in the root of this repository 3# for complete details. 4 5from __future__ import absolute_import, division, print_function 6 7import getpass 8import glob 9import io 10import json 11import os 12import subprocess 13import time 14import zipfile 15 16import click 17 18import requests 19 20 21def run(*args, **kwargs): 22 print("[running] {0}".format(list(args))) 23 subprocess.check_call(list(args), **kwargs) 24 25 26def wait_for_build_complete_github_actions(session, token, run_url): 27 while True: 28 response = session.get( 29 run_url, 30 headers={ 31 "Content-Type": "application/json", 32 "Authorization": "token {}".format(token), 33 }, 34 ) 35 response.raise_for_status() 36 if response.json()["conclusion"] is not None: 37 break 38 time.sleep(3) 39 40 41def download_artifacts_github_actions(session, token, run_url): 42 response = session.get( 43 run_url, 44 headers={ 45 "Content-Type": "application/json", 46 "Authorization": "token {}".format(token), 47 }, 48 ) 49 response.raise_for_status() 50 51 response = session.get( 52 response.json()["artifacts_url"], 53 headers={ 54 "Content-Type": "application/json", 55 "Authorization": "token {}".format(token), 56 }, 57 ) 58 response.raise_for_status() 59 paths = [] 60 for artifact in response.json()["artifacts"]: 61 response = session.get( 62 artifact["archive_download_url"], 63 headers={ 64 "Content-Type": "application/json", 65 "Authorization": "token {}".format(token), 66 }, 67 ) 68 with zipfile.ZipFile(io.BytesIO(response.content)) as z: 69 for name in z.namelist(): 70 if not name.endswith(".whl"): 71 continue 72 p = z.open(name) 73 out_path = os.path.join( 74 os.path.dirname(__file__), 75 "dist", 76 os.path.basename(name), 77 ) 78 with open(out_path, "wb") as f: 79 f.write(p.read()) 80 paths.append(out_path) 81 return paths 82 83 84def build_github_actions_wheels(token, version): 85 session = requests.Session() 86 87 response = session.post( 88 "https://api.github.com/repos/pyca/cryptography/actions/workflows/" 89 "wheel-builder.yml/dispatches", 90 headers={ 91 "Content-Type": "application/json", 92 "Accept": "application/vnd.github.v3+json", 93 "Authorization": "token {}".format(token), 94 }, 95 data=json.dumps({"ref": "master", "inputs": {"version": version}}), 96 ) 97 response.raise_for_status() 98 99 # Give it a few seconds for the run to kick off. 100 time.sleep(5) 101 response = session.get( 102 ( 103 "https://api.github.com/repos/pyca/cryptography/actions/workflows/" 104 "wheel-builder.yml/runs?event=workflow_dispatch" 105 ), 106 headers={ 107 "Content-Type": "application/json", 108 "Authorization": "token {}".format(token), 109 }, 110 ) 111 response.raise_for_status() 112 run_url = response.json()["workflow_runs"][0]["url"] 113 wait_for_build_complete_github_actions(session, token, run_url) 114 return download_artifacts_github_actions(session, token, run_url) 115 116 117@click.command() 118@click.argument("version") 119def release(version): 120 """ 121 ``version`` should be a string like '0.4' or '1.0'. 122 """ 123 github_token = getpass.getpass("Github person access token: ") 124 125 run("git", "tag", "-s", version, "-m", "{0} release".format(version)) 126 run("git", "push", "--tags") 127 128 run("python", "setup.py", "sdist") 129 run("python", "setup.py", "sdist", "bdist_wheel", cwd="vectors/") 130 131 packages = glob.glob("dist/cryptography-{0}*".format(version)) + glob.glob( 132 "vectors/dist/cryptography_vectors-{0}*".format(version) 133 ) 134 run("twine", "upload", "-s", *packages) 135 136 github_actions_wheel_paths = build_github_actions_wheels( 137 github_token, version 138 ) 139 run("twine", "upload", *github_actions_wheel_paths) 140 141 142if __name__ == "__main__": 143 release() 144