Skip to content

Commit 25ea9b9

Browse files
committed
ci: Introduce helper for publishing artifacts
This comes from meta-qcom. Its a temporary thing while we wait for permission to create a common Github Action. Signed-off-by: Andy Doan <[email protected]>
1 parent b6fb4fa commit 25ea9b9

File tree

1 file changed

+111
-0
lines changed

1 file changed

+111
-0
lines changed
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
3+
# SPDX-License-Identifier: BSD-3-Clause
4+
5+
from multiprocessing import Pool
6+
import os
7+
import sys
8+
from time import sleep
9+
from typing import List
10+
11+
import requests
12+
13+
gh_token = os.environ["GITHUB_TOKEN"]
14+
num_threads_str = os.environ.get("UPLOAD_THREADS", "5")
15+
16+
17+
def upload_file(args):
18+
"""
19+
Uploads a file to our file upload service. The service is a GCP CloudRun
20+
project that returns signed URLs to Google Storage objects we can upload to.
21+
"""
22+
try:
23+
url, base, name = args
24+
25+
headers = {
26+
"Authentication": f"Bearer {gh_token}",
27+
}
28+
29+
# Obtain the signed-url for GCS using Fibonacci backoff/retries
30+
for x in (1, 2, 3, 5, 0):
31+
r = requests.put(url, headers=headers, allow_redirects=False)
32+
if not r.ok:
33+
correlation_id = r.headers.get("X-Correlation-ID", "?")
34+
if not x:
35+
return (
36+
name,
37+
f"Unable to get signed url HTTP_{r.status_code}. Correlation ID: {correlation_id} - {r.text}",
38+
)
39+
else:
40+
print(
41+
f"Error getting signed URL for {name}: Correlation ID: {correlation_id} HTTP_{r.status_code} - {r.text}",
42+
flush=True,
43+
)
44+
print(f"Retrying in {x} seconds", flush=True)
45+
sleep(x)
46+
47+
# Upload the file to the signed URL with backoff/retry logic
48+
url = r.headers["location"]
49+
path = os.path.join(base, name)
50+
for x in (1, 2, 3, 0):
51+
r = requests.put(
52+
url,
53+
data=open(path, "rb"),
54+
headers={"Content-type": "application/octet-stream"},
55+
)
56+
if not r.ok:
57+
if not x:
58+
return (
59+
name,
60+
f"Unable to upload content HTTP_{r.status_code} - {r.text}",
61+
)
62+
else:
63+
print(
64+
f"Unable to upload content for {name}: HTTP_{r.status_code} - {r.text}"
65+
)
66+
print(f"Retrying in {x} seconds")
67+
sleep(x)
68+
69+
return name, None
70+
except Exception as e:
71+
return name, str(e)
72+
73+
74+
def get_files_to_publish(path: str) -> List[str]:
75+
paths = []
76+
for root, dirs, files in os.walk(path):
77+
for file in files:
78+
paths.append(os.path.join(root, file)[len(path) :])
79+
return paths
80+
81+
82+
def main(num_threads: int, artifacts_dir: str, base_url: str):
83+
paths = get_files_to_publish(artifacts_dir)
84+
print(f"= Found {len(paths)} files to publish", flush=True)
85+
86+
failed = False
87+
work = [(f"{base_url}{x}", artifacts_dir, x) for x in paths]
88+
with Pool(num_threads) as p:
89+
results = p.imap_unordered(upload_file, work)
90+
for i, res in enumerate(results):
91+
name, err = res
92+
print(f"= {i+1} of {len(work)} - {name}", flush=True)
93+
if err:
94+
print(f"|-> ERROR: {err}", flush=True)
95+
failed = True
96+
97+
if failed:
98+
sys.exit(1)
99+
100+
101+
if __name__ == "__main__":
102+
BUILD_DIR = os.environ["BUILD_DIR"]
103+
if BUILD_DIR[-1] != "/":
104+
BUILD_DIR = BUILD_DIR + "/"
105+
106+
URL = os.environ["URL"]
107+
if URL[-1] != "/":
108+
URL = URL + "/"
109+
110+
num_threads = int(num_threads_str)
111+
main(num_threads, BUILD_DIR, URL)

0 commit comments

Comments
 (0)