8
8
import subprocess
9
9
import time
10
10
import typing
11
+ import urllib
11
12
import zipfile
12
13
13
14
import click
@@ -102,6 +103,108 @@ def fetch_github_actions_artifacts(
102
103
return download_artifacts_github_actions (session , token , run_url )
103
104
104
105
106
+ def wait_for_build_complete_circleci (
107
+ session : requests .Session , token : str , pipeline_id : str
108
+ ) -> None :
109
+ while True :
110
+ response = session .get (
111
+ f"https://circleci.com/api/v2/pipeline/{ pipeline_id } /workflow" ,
112
+ headers = {
113
+ "Circle-Token" : token ,
114
+ },
115
+ )
116
+ response .raise_for_status ()
117
+ status = response .json ()["items" ][0 ]["status" ]
118
+ if status == "success" :
119
+ break
120
+ elif status not in ("running" , "on_hold" , "not_run" ):
121
+ raise ValueError (f"CircleCI build failed with status { status } " )
122
+ time .sleep (3 )
123
+
124
+
125
+ def download_artifacts_circleci (
126
+ session : requests .Session , urls : typing .List [str ]
127
+ ) -> typing .List [str ]:
128
+ paths = []
129
+ for url in urls :
130
+ name = os .path .basename (urllib .parse .urlparse (url ).path )
131
+ response = session .get (url )
132
+ out_path = os .path .join (
133
+ os .path .dirname (__file__ ),
134
+ "dist" ,
135
+ os .path .basename (name ),
136
+ )
137
+ with open (out_path , "wb" ) as f :
138
+ f .write (response .content )
139
+ paths .append (out_path )
140
+ return paths
141
+
142
+
143
+ def fetch_circleci_artifacts (token : str , version : str ) -> typing .List [str ]:
144
+ session = requests .Session ()
145
+
146
+ response = session .get (
147
+ "https://circleci.com/api/v2/pipeline?org-slug=gh/pyca" ,
148
+ headers = {"Circle-Token" : token },
149
+ )
150
+ response .raise_for_status ()
151
+ pipeline_id = None
152
+ for item in response .json ()["items" ]:
153
+ if item ["project_slug" ] == "gh/pyca/cryptography" :
154
+ if item ["vcs" ].get ("tag" , None ) == version :
155
+ pipeline_id = item ["id" ]
156
+ break
157
+
158
+ if pipeline_id is None :
159
+ raise ValueError (f"Could not find a pipeline for version { version } " )
160
+
161
+ wait_for_build_complete_circleci (session , token , pipeline_id )
162
+ urls = fetch_circleci_artifact_urls (session , token , pipeline_id )
163
+ return download_artifacts_circleci (session , urls )
164
+
165
+
166
+ def fetch_circleci_artifact_urls (
167
+ session : requests .Session , token : str , pipeline_id : str
168
+ ) -> typing .List [str ]:
169
+ response = session .get (
170
+ f"https://circleci.com/api/v2/pipeline/{ pipeline_id } /workflow" ,
171
+ headers = {"Circle-Token" : token },
172
+ )
173
+ response .raise_for_status ()
174
+ workflow_id = response .json ()["items" ][0 ]["id" ]
175
+ job_response = session .get (
176
+ f"https://circleci.com/api/v2/workflow/{ workflow_id } /job" ,
177
+ headers = {"Circle-Token" : token },
178
+ )
179
+ job_response .raise_for_status ()
180
+ artifact_urls = []
181
+ for job in job_response .json ()["items" ]:
182
+ urls = fetch_circleci_artifact_url_from_job (
183
+ session , token , job ["job_number" ]
184
+ )
185
+ artifact_urls .extend (urls )
186
+
187
+ return artifact_urls
188
+
189
+
190
+ def fetch_circleci_artifact_url_from_job (
191
+ session : requests .Session , token : str , job : str
192
+ ) -> typing .List [str ]:
193
+ response = session .get (
194
+ f"https://circleci.com/api/v2/project/gh/pyca/cryptography/"
195
+ f"{ job } /artifacts" ,
196
+ headers = {"Circle-Token" : token },
197
+ )
198
+ response .raise_for_status ()
199
+ urls = []
200
+ for item in response .json ()["items" ]:
201
+ url = item .get ("url" , None )
202
+ if url is not None :
203
+ urls .append (url )
204
+
205
+ return urls
206
+
207
+
105
208
@click .command ()
106
209
@click .argument ("version" )
107
210
def release (version : str ) -> None :
@@ -113,7 +216,12 @@ def release(version: str) -> None:
113
216
f"https://github.com/settings/tokens/new?"
114
217
f"description={ version } &scopes=repo"
115
218
)
219
+ print (
220
+ "Get a CircleCI token at: "
221
+ "https://app.circleci.com/settings/user/tokens"
222
+ )
116
223
github_token = getpass .getpass ("Github person access token: " )
224
+ circle_token = getpass .getpass ("CircleCI token: " )
117
225
118
226
# Tag and push the tag (this will trigger the wheel builder in Actions)
119
227
run ("git" , "tag" , "-s" , version , "-m" , "{0} release" .format (version ))
@@ -123,9 +231,13 @@ def release(version: str) -> None:
123
231
github_actions_artifact_paths = fetch_github_actions_artifacts (
124
232
github_token , version
125
233
)
234
+ # Download wheels from CircleCI
235
+ circle_artifact_paths = fetch_circleci_artifacts (circle_token , version )
236
+
237
+ artifact_paths = github_actions_artifact_paths + circle_artifact_paths
126
238
127
239
# Upload wheels and sdist
128
- run ("twine" , "upload" , * github_actions_artifact_paths )
240
+ run ("twine" , "upload" , * artifact_paths )
129
241
130
242
131
243
if __name__ == "__main__" :
0 commit comments