Skip to content

Commit 84a3cd7

Browse files
authored
automatically download and upload circleci wheels (#7949)
1 parent 525c0b3 commit 84a3cd7

File tree

1 file changed

+113
-1
lines changed

1 file changed

+113
-1
lines changed

release.py

+113-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import subprocess
99
import time
1010
import typing
11+
import urllib
1112
import zipfile
1213

1314
import click
@@ -102,6 +103,108 @@ def fetch_github_actions_artifacts(
102103
return download_artifacts_github_actions(session, token, run_url)
103104

104105

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+
105208
@click.command()
106209
@click.argument("version")
107210
def release(version: str) -> None:
@@ -113,7 +216,12 @@ def release(version: str) -> None:
113216
f"https://github.com/settings/tokens/new?"
114217
f"description={version}&scopes=repo"
115218
)
219+
print(
220+
"Get a CircleCI token at: "
221+
"https://app.circleci.com/settings/user/tokens"
222+
)
116223
github_token = getpass.getpass("Github person access token: ")
224+
circle_token = getpass.getpass("CircleCI token: ")
117225

118226
# Tag and push the tag (this will trigger the wheel builder in Actions)
119227
run("git", "tag", "-s", version, "-m", "{0} release".format(version))
@@ -123,9 +231,13 @@ def release(version: str) -> None:
123231
github_actions_artifact_paths = fetch_github_actions_artifacts(
124232
github_token, version
125233
)
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
126238

127239
# Upload wheels and sdist
128-
run("twine", "upload", *github_actions_artifact_paths)
240+
run("twine", "upload", *artifact_paths)
129241

130242

131243
if __name__ == "__main__":

0 commit comments

Comments
 (0)